149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes/* 249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * Copyright (C) 2013 The Android Open Source Project 349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * 449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * you may not use this file except in compliance with the License. 649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * You may obtain a copy of the License at 749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * 849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * http://www.apache.org/licenses/LICENSE-2.0 949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * 1049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * Unless required by applicable law or agreed to in writing, software 1149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * distributed under the License is distributed on an "AS IS" BASIS, 1249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * See the License for the specific language governing permissions and 1449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * limitations under the License. 1549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes */ 1649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 1749c78900da0d43140fb602431fb93212bd7f6c70Chris Banespackage android.support.v4.widget; 1849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 196142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.v4.view.GravityCompat; 206142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.v4.view.ViewCompat; 216142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.view.Gravity; 2249c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.view.View; 23322f8748f9d8de6f79345f660c859f163ad363b7Chris Banesimport android.view.WindowManager; 2449c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.widget.PopupWindow; 2549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 2649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes/** 2749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * Helper for accessing features in PopupWindow introduced after API level 4 2849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * in a backwards compatible fashion. 2949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes */ 30c5847d13e40f5d52459f5c0dab32dc08f1a9a683Chris Banespublic final class PopupWindowCompat { 3149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes /** 3249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * Interface for the full API. 3349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes */ 3449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes interface PopupWindowImpl { 35322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, int gravity); 36322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor); 37322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes boolean getOverlapAnchor(PopupWindow popupWindow); 38322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes void setWindowLayoutType(PopupWindow popupWindow, int layoutType); 39322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes int getWindowLayoutType(PopupWindow popupWindow); 4049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 4149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 4249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes /** 4349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * Interface implementation that doesn't use anything above v4 APIs. 4449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes */ 4549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes static class BasePopupWindowImpl implements PopupWindowImpl { 4649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes @Override 4749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, 4849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes int gravity) { 496142a54baae3289f734947c6b5375b12eb0fb722Chris Banes final int hgrav = GravityCompat.getAbsoluteGravity(gravity, 506142a54baae3289f734947c6b5375b12eb0fb722Chris Banes ViewCompat.getLayoutDirection(anchor)) & Gravity.HORIZONTAL_GRAVITY_MASK; 516142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (hgrav == Gravity.RIGHT) { 526142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // Flip the location to align the right sides of the popup and 536142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // anchor instead of left. 546142a54baae3289f734947c6b5375b12eb0fb722Chris Banes xoff -= (popup.getWidth() - anchor.getWidth()); 556142a54baae3289f734947c6b5375b12eb0fb722Chris Banes } 5649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes popup.showAsDropDown(anchor, xoff, yoff); 5749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 5844918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes 5944918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes @Override 6044918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) { 6144918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes // noop 6244918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } 6344918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes 6444918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes @Override 6544918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes public boolean getOverlapAnchor(PopupWindow popupWindow) { 6644918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes return false; 6744918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } 68322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes 69322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes @Override 70322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) { 71322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes // no-op 72322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes } 73322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes 74322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes @Override 75322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes public int getWindowLayoutType(PopupWindow popupWindow) { 76322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes return 0; 77322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes } 78322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes } 79322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes 80322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes /** 81322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes * Interface implementation that doesn't use anything above v4 APIs. 82322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes */ 83322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes static class GingerbreadPopupWindowImpl extends BasePopupWindowImpl { 84322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes @Override 85322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) { 86322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes PopupWindowCompatGingerbread.setWindowLayoutType(popupWindow, layoutType); 87322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes } 88322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes 89322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes @Override 90322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes public int getWindowLayoutType(PopupWindow popupWindow) { 91322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes return PopupWindowCompatGingerbread.getWindowLayoutType(popupWindow); 92322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes } 9349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 9449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 9549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes /** 9649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * Interface implementation for devices with at least KitKat APIs. 9749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes */ 98322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes static class KitKatPopupWindowImpl extends GingerbreadPopupWindowImpl { 9949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes @Override 10049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, 10149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes int gravity) { 10249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes PopupWindowCompatKitKat.showAsDropDown(popup, anchor, xoff, yoff, gravity); 10349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 10449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 10549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 10644918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes static class Api21PopupWindowImpl extends KitKatPopupWindowImpl { 10744918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes @Override 10844918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) { 10944918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes PopupWindowCompatApi21.setOverlapAnchor(popupWindow, overlapAnchor); 11044918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } 11144918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes 11244918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes @Override 11344918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes public boolean getOverlapAnchor(PopupWindow popupWindow) { 11444918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes return PopupWindowCompatApi21.getOverlapAnchor(popupWindow); 11544918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } 11644918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } 11744918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes 11844918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes static class Api23PopupWindowImpl extends Api21PopupWindowImpl { 11944918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes @Override 12044918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) { 12144918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes PopupWindowCompatApi23.setOverlapAnchor(popupWindow, overlapAnchor); 12244918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } 12344918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes 12444918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes @Override 12544918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes public boolean getOverlapAnchor(PopupWindow popupWindow) { 12644918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes return PopupWindowCompatApi23.getOverlapAnchor(popupWindow); 12744918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } 128322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes 129322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes @Override 130322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) { 131322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes PopupWindowCompatApi23.setWindowLayoutType(popupWindow, layoutType); 132322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes } 133322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes 134322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes @Override 135322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes public int getWindowLayoutType(PopupWindow popupWindow) { 136322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes return PopupWindowCompatApi23.getWindowLayoutType(popupWindow); 137322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes } 13844918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } 13944918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes 14049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes /** 14149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * Select the correct implementation to use for the current platform. 14249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes */ 14349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes static final PopupWindowImpl IMPL; 14449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes static { 14549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes final int version = android.os.Build.VERSION.SDK_INT; 14644918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes if (version >= 23) { 14744918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes IMPL = new Api23PopupWindowImpl(); 14844918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } else if (version >= 21) { 14944918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes IMPL = new Api21PopupWindowImpl(); 15044918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } else if (version >= 19) { 15149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes IMPL = new KitKatPopupWindowImpl(); 152322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes } else if (version >= 9) { 153322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes IMPL = new GingerbreadPopupWindowImpl(); 15449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } else { 15549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes IMPL = new BasePopupWindowImpl(); 15649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 15749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 15849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 15949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes private PopupWindowCompat() { 16049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes // This class is not publicly instantiable. 16149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 16249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 16349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes /** 16449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * <p>Display the content view in a popup window anchored to the bottom-left 16549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * corner of the anchor view offset by the specified x and y coordinates. 16649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * If there is not enough room on screen to show 16749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * the popup in its entirety, this method tries to find a parent scroll 16849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * view to scroll. If no parent scroll view can be scrolled, the bottom-left 16949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * corner of the popup is pinned at the top left corner of the anchor view.</p> 17049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * <p>If the view later scrolls to move <code>anchor</code> to a different 17149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * location, the popup will be moved correspondingly.</p> 17249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * 17349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * @param popup the PopupWindow to show 17449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * @param anchor the view on which to pin the popup window 17549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * @param xoff A horizontal offset from the anchor in pixels 17649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * @param yoff A vertical offset from the anchor in pixels 17749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes * @param gravity Alignment of the popup relative to the anchor 17849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes */ 17949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes public static void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, 18049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes int gravity) { 18149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes IMPL.showAsDropDown(popup, anchor, xoff, yoff, gravity); 18249c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 18344918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes 18444918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes /** 18544918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes * Sets whether the popup window should overlap its anchor view when 18644918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes * displayed as a drop-down. 18744918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes * 18844918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes * @param overlapAnchor Whether the popup should overlap its anchor. 18944918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes */ 19044918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes public static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) { 19144918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes IMPL.setOverlapAnchor(popupWindow, overlapAnchor); 19244918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } 19344918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes 19444918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes /** 19544918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes * Returns whether the popup window should overlap its anchor view when 19644918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes * displayed as a drop-down. 19744918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes * 19844918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes * @return Whether the popup should overlap its anchor. 19944918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes */ 20044918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes public static boolean getOverlapAnchor(PopupWindow popupWindow) { 20144918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes return IMPL.getOverlapAnchor(popupWindow); 20244918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } 203322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes 204322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes /** 205322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes * Set the layout type for this window. This value will be passed through to 206322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes * {@link WindowManager.LayoutParams#type} therefore the value should match any value 207322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes * {@link WindowManager.LayoutParams#type} accepts. 208322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes * 209322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes * @param layoutType Layout type for this window. 210322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes * 211322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes * @see WindowManager.LayoutParams#type 212322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes */ 213322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes public static void setWindowLayoutType(PopupWindow popupWindow, int layoutType) { 214322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes IMPL.setWindowLayoutType(popupWindow, layoutType); 215322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes } 216322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes 217322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes /** 218322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes * Returns the layout type for this window. 219322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes * 220322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes * @see #setWindowLayoutType(PopupWindow popupWindow, int) 221322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes */ 222322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes public static int getWindowLayoutType(PopupWindow popupWindow) { 223322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes return IMPL.getWindowLayoutType(popupWindow); 224322f8748f9d8de6f79345f660c859f163ad363b7Chris Banes } 22549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes} 226