MenuPopupHelper.java revision d6e47228c44aaadb0d4518da6db5c3f5dffda1ab
1bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell/* 2bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Copyright (C) 2010 The Android Open Source Project 3bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * 4bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Licensed under the Apache License, Version 2.0 (the "License"); 5bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * you may not use this file except in compliance with the License. 6bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * You may obtain a copy of the License at 7bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * 8bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * http://www.apache.org/licenses/LICENSE-2.0 9bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * 10bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Unless required by applicable law or agreed to in writing, software 11bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * distributed under the License is distributed on an "AS IS" BASIS, 12bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * See the License for the specific language governing permissions and 14bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * limitations under the License. 15bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell */ 16bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 1766698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banespackage android.support.v7.view.menu; 18bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 19bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.content.Context; 20d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.graphics.Point; 216142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.graphics.Rect; 22d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.os.Build; 236142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.annotation.AttrRes; 246142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.annotation.NonNull; 256142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.annotation.Nullable; 266142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.annotation.StyleRes; 276142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.v4.view.GravityCompat; 286142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.v4.view.ViewCompat; 29da10fdd1400ecfd8d7f2e55651dd528d0614dfc5Jeff Brownimport android.support.v7.appcompat.R; 30d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.view.Display; 3149c78900da0d43140fb602431fb93212bd7f6c70Chris Banesimport android.view.Gravity; 32bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powellimport android.view.View; 33d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.view.WindowManager; 346142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.widget.PopupWindow.OnDismissListener; 35bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 36bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell/** 37bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * Presents a menu as a small, simple popup anchored to another view. 38ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani * 39bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell * @hide 40bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell */ 416142a54baae3289f734947c6b5375b12eb0fb722Chris Banespublic class MenuPopupHelper implements MenuHelper { 426142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private static final int TOUCH_EPICENTER_SIZE_DP = 48; 43bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 4449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes private final Context mContext; 456142a54baae3289f734947c6b5375b12eb0fb722Chris Banes 466142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // Immutable cached popup menu properties. 4749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes private final MenuBuilder mMenu; 4849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes private final boolean mOverflowOnly; 4949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes private final int mPopupStyleAttr; 50a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette private final int mPopupStyleRes; 5149c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 526142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // Mutable cached popup menu properties. 53ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani private View mAnchorView; 546142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private int mDropDownGravity = Gravity.START; 556142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private boolean mForceShowIcon; 566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private MenuPresenter.Callback mPresenterCallback; 5749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 586142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private MenuPopup mPopup; 596142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private OnDismissListener mOnDismissListener; 6049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 616142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu) { 626142a54baae3289f734947c6b5375b12eb0fb722Chris Banes this(context, menu, null, false, R.attr.popupMenuStyle, 0); 63ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 64bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 656142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu, 666142a54baae3289f734947c6b5375b12eb0fb722Chris Banes @NonNull View anchorView) { 676142a54baae3289f734947c6b5375b12eb0fb722Chris Banes this(context, menu, anchorView, false, R.attr.popupMenuStyle, 0); 68ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 69bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 706142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu, 716142a54baae3289f734947c6b5375b12eb0fb722Chris Banes @NonNull View anchorView, 726142a54baae3289f734947c6b5375b12eb0fb722Chris Banes boolean overflowOnly, @AttrRes int popupStyleAttr) { 73a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette this(context, menu, anchorView, overflowOnly, popupStyleAttr, 0); 74a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette } 75a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette 766142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public MenuPopupHelper(@NonNull Context context, @NonNull MenuBuilder menu, 776142a54baae3289f734947c6b5375b12eb0fb722Chris Banes @NonNull View anchorView, boolean overflowOnly, @AttrRes int popupStyleAttr, 786142a54baae3289f734947c6b5375b12eb0fb722Chris Banes @StyleRes int popupStyleRes) { 79ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mContext = context; 80ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mMenu = menu; 816142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mAnchorView = anchorView; 82ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mOverflowOnly = overflowOnly; 8349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes mPopupStyleAttr = popupStyleAttr; 84a9cf27da7f78d81db5ad482003a10d1a6562107cAlan Viverette mPopupStyleRes = popupStyleRes; 856142a54baae3289f734947c6b5375b12eb0fb722Chris Banes } 86bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 876142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public void setOnDismissListener(@Nullable OnDismissListener listener) { 886142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mOnDismissListener = listener; 89ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 90bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 916142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 926142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Sets the view to which the popup window is anchored. 936142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 946142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Changes take effect on the next call to show(). 956142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * 966142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @param anchor the view to which the popup window should be anchored 976142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 986142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public void setAnchorView(@NonNull View anchor) { 99ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani mAnchorView = anchor; 100ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 101bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 1026142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 1036142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Sets whether the popup menu's adapter is forced to show icons in the 1046142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * menu item views. 1056142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 1066142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Changes take effect on the next call to show(). 1076142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * 1086142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @param forceShowIcon {@code true} to force icons to be shown, or 1096142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * {@code false} for icons to be optionally shown 1106142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 1116142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public void setForceShowIcon(boolean forceShowIcon) { 1126142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mForceShowIcon = forceShowIcon; 113d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes if (mPopup != null) { 114d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes mPopup.setForceShowIcon(forceShowIcon); 115d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes } 116bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell } 117ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 1186142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 1196142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Sets the alignment of the popup window relative to the anchor view. 1206142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 1216142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Changes take effect on the next call to show(). 1226142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * 1236142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @param gravity alignment of the popup relative to the anchor 1246142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 12549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes public void setGravity(int gravity) { 12649c78900da0d43140fb602431fb93212bd7f6c70Chris Banes mDropDownGravity = gravity; 12749c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 12849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 1296142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 1306142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @return alignment of the popup relative to the anchor 1316142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 13244918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes public int getGravity() { 13344918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes return mDropDownGravity; 13444918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes } 13544918a92e1d66a01a03063e2c5e68b2570f64b03Chris Banes 136ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public void show() { 137ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani if (!tryShow()) { 138ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); 139ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 140bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell } 141bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 1426142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public void show(int x, int y) { 1436142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (!tryShow(x, y)) { 1446142a54baae3289f734947c6b5375b12eb0fb722Chris Banes throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); 1456142a54baae3289f734947c6b5375b12eb0fb722Chris Banes } 1466142a54baae3289f734947c6b5375b12eb0fb722Chris Banes } 1476142a54baae3289f734947c6b5375b12eb0fb722Chris Banes 1486142a54baae3289f734947c6b5375b12eb0fb722Chris Banes @NonNull 1496142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public MenuPopup getPopup() { 1506142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (mPopup == null) { 1516142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mPopup = createPopup(); 1526142a54baae3289f734947c6b5375b12eb0fb722Chris Banes } 15349c78900da0d43140fb602431fb93212bd7f6c70Chris Banes return mPopup; 15449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 15549c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 1566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 1576142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Attempts to show the popup anchored to the view specified by {@link #setAnchorView(View)}. 1586142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * 1596142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @return {@code true} if the popup was shown or was already showing prior to calling this 1606142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * method, {@code false} otherwise 1616142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 162ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani public boolean tryShow() { 1636142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (isShowing()) { 1646142a54baae3289f734947c6b5375b12eb0fb722Chris Banes return true; 165ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 166bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 1676142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (mAnchorView == null) { 1686142a54baae3289f734947c6b5375b12eb0fb722Chris Banes return false; 16949c78900da0d43140fb602431fb93212bd7f6c70Chris Banes } 17049c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 1716142a54baae3289f734947c6b5375b12eb0fb722Chris Banes showPopup(0, 0, false, false); 172ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani return true; 173bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell } 174ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 1756142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 1766142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Shows the popup menu and makes a best-effort to anchor it to the 1776142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * specified (x,y) coordinate relative to the anchor view. 1786142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 1796142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Additionally, the popup's transition epicenter (see 1806142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * {@link android.widget.PopupWindow#setEpicenterBounds(Rect)} will be 1816142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * centered on the specified coordinate, rather than using the bounds of 1826142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * the anchor view. 1836142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 1846142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * If the popup's resolved gravity is {@link Gravity#LEFT}, this will 1856142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * display the popup with its top-left corner at (x,y) relative to the 1866142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * anchor view. If the resolved gravity is {@link Gravity#RIGHT}, the 1876142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * popup's top-right corner will be at (x,y). 1886142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 1896142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * If the popup cannot be displayed fully on-screen, this method will 1906142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * attempt to scroll the anchor view's ancestors and/or offset the popup 1916142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * such that it may be displayed fully on-screen. 1926142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * 1936142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @param x x coordinate relative to the anchor view 1946142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @param y y coordinate relative to the anchor view 1956142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @return {@code true} if the popup was shown or was already showing prior 1966142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * to calling this method, {@code false} otherwise 1976142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 1986142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public boolean tryShow(int x, int y) { 199ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani if (isShowing()) { 2006142a54baae3289f734947c6b5375b12eb0fb722Chris Banes return true; 201ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 202ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 2036142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (mAnchorView == null) { 2046142a54baae3289f734947c6b5375b12eb0fb722Chris Banes return false; 205ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 206ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 2076142a54baae3289f734947c6b5375b12eb0fb722Chris Banes showPopup(x, y, true, true); 2086142a54baae3289f734947c6b5375b12eb0fb722Chris Banes return true; 209ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 210bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 2116142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 2126142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Creates the popup and assigns cached properties. 2136142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * 2146142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * @return an initialized popup 2156142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 2166142a54baae3289f734947c6b5375b12eb0fb722Chris Banes @NonNull 2176142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private MenuPopup createPopup() { 218d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes final WindowManager windowManager = (WindowManager) mContext.getSystemService( 219d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes Context.WINDOW_SERVICE); 220d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes final Display display = windowManager.getDefaultDisplay(); 221d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes final Point displaySize = new Point(); 222d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes 223d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes if (Build.VERSION.SDK_INT >= 17) { 224d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes display.getRealSize(displaySize); 225d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes } else if (Build.VERSION.SDK_INT >= 13) { 226d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes display.getSize(displaySize); 227d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes } else { 228d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes displaySize.set(display.getWidth(), display.getHeight()); 229d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes } 230d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes 231d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes final int smallestWidth = Math.min(displaySize.x, displaySize.y); 232d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes final int minSmallestWidthCascading = mContext.getResources().getDimensionPixelSize( 233d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes R.dimen.abc_cascading_menus_min_smallest_width); 234d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes final boolean enableCascadingSubmenus = smallestWidth >= minSmallestWidthCascading; 2356142a54baae3289f734947c6b5375b12eb0fb722Chris Banes 2366142a54baae3289f734947c6b5375b12eb0fb722Chris Banes final MenuPopup popup; 2376142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (enableCascadingSubmenus) { 2386142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup = new CascadingMenuPopup(mContext, mAnchorView, mPopupStyleAttr, 2396142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mPopupStyleRes, mOverflowOnly); 2406142a54baae3289f734947c6b5375b12eb0fb722Chris Banes } else { 2416142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup = new StandardMenuPopup(mContext, mMenu, mAnchorView, mPopupStyleAttr, 2426142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mPopupStyleRes, mOverflowOnly); 243ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 244ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 2456142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // Assign immutable properties. 2466142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup.addMenu(mMenu); 2476142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup.setOnDismissListener(mInternalOnDismissListener); 24849c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 2496142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // Assign mutable properties. These may be reassigned later. 2506142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup.setAnchorView(mAnchorView); 2516142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup.setCallback(mPresenterCallback); 2526142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup.setForceShowIcon(mForceShowIcon); 2536142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup.setGravity(mDropDownGravity); 25449c78900da0d43140fb602431fb93212bd7f6c70Chris Banes 2556142a54baae3289f734947c6b5375b12eb0fb722Chris Banes return popup; 256ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 257bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 2586142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private void showPopup(int xOffset, int yOffset, boolean useOffsets, boolean showTitle) { 2596142a54baae3289f734947c6b5375b12eb0fb722Chris Banes final MenuPopup popup = getPopup(); 2606142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup.setShowTitle(showTitle); 2616142a54baae3289f734947c6b5375b12eb0fb722Chris Banes 2626142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (useOffsets) { 2636142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // If the resolved drop-down gravity is RIGHT, the popup's right 2646142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // edge will be aligned with the anchor view. Adjust by the anchor 2656142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // width such that the top-right corner is at the X offset. 2666142a54baae3289f734947c6b5375b12eb0fb722Chris Banes final int hgrav = GravityCompat.getAbsoluteGravity(mDropDownGravity, 2676142a54baae3289f734947c6b5375b12eb0fb722Chris Banes ViewCompat.getLayoutDirection(mAnchorView)) & Gravity.HORIZONTAL_GRAVITY_MASK; 2686142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (hgrav == Gravity.RIGHT) { 2696142a54baae3289f734947c6b5375b12eb0fb722Chris Banes xOffset -= mAnchorView.getWidth(); 270ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 271bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 2726142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup.setHorizontalOffset(xOffset); 2736142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup.setVerticalOffset(yOffset); 2746142a54baae3289f734947c6b5375b12eb0fb722Chris Banes 2756142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // Set the transition epicenter to be roughly finger (or mouse 2766142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // cursor) sized and centered around the offset position. This 2776142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // will give the appearance that the window is emerging from 2786142a54baae3289f734947c6b5375b12eb0fb722Chris Banes // the touch point. 2796142a54baae3289f734947c6b5375b12eb0fb722Chris Banes final float density = mContext.getResources().getDisplayMetrics().density; 2806142a54baae3289f734947c6b5375b12eb0fb722Chris Banes final int halfSize = (int) (TOUCH_EPICENTER_SIZE_DP * density / 2); 2816142a54baae3289f734947c6b5375b12eb0fb722Chris Banes final Rect epicenter = new Rect(xOffset - halfSize, yOffset - halfSize, 2826142a54baae3289f734947c6b5375b12eb0fb722Chris Banes xOffset + halfSize, yOffset + halfSize); 2836142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup.setEpicenterBounds(epicenter); 28420ac724a3a836bfd362c911f7dc55a61c02b4d44Trevor Johns } 285bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 2866142a54baae3289f734947c6b5375b12eb0fb722Chris Banes popup.show(); 287bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell } 288bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 2896142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 2906142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Dismisses the popup, if showing. 2916142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 292ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani @Override 2936142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public void dismiss() { 2946142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (isShowing()) { 2956142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mPopup.dismiss(); 296ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 297bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell } 298bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 2996142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 3006142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Called after the popup has been dismissed. 3016142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <p> 3026142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * <strong>Note:</strong> Subclasses should call the super implementation 3036142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * last to ensure that any necessary tear down has occurred before the 3046142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * listener specified by {@link #setOnDismissListener(OnDismissListener)} 3056142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * is called. 3066142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 3076142a54baae3289f734947c6b5375b12eb0fb722Chris Banes protected void onDismiss() { 3086142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mPopup = null; 309ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 3106142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (mOnDismissListener != null) { 3116142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mOnDismissListener.onDismiss(); 312bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell } 313bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell } 314bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell 3156142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public boolean isShowing() { 3166142a54baae3289f734947c6b5375b12eb0fb722Chris Banes return mPopup != null && mPopup.isShowing(); 317ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 318ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 319ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani @Override 3206142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public void setPresenterCallback(@Nullable MenuPresenter.Callback cb) { 3216142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mPresenterCallback = cb; 3226142a54baae3289f734947c6b5375b12eb0fb722Chris Banes if (mPopup != null) { 3236142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mPopup.setCallback(cb); 324ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 3256142a54baae3289f734947c6b5375b12eb0fb722Chris Banes } 326ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani 3276142a54baae3289f734947c6b5375b12eb0fb722Chris Banes /** 3286142a54baae3289f734947c6b5375b12eb0fb722Chris Banes * Listener used to proxy dismiss callbacks to the helper's owner. 3296142a54baae3289f734947c6b5375b12eb0fb722Chris Banes */ 3306142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private final OnDismissListener mInternalOnDismissListener = new OnDismissListener() { 331ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani @Override 3326142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public void onDismiss() { 3336142a54baae3289f734947c6b5375b12eb0fb722Chris Banes MenuPopupHelper.this.onDismiss(); 334ced50ab7536cd3d3573f03310fc899f10c414d37Anirudh Dewani } 3356142a54baae3289f734947c6b5375b12eb0fb722Chris Banes }; 336bbbb8f39d1b1d1b317c5f9237f20fe6b1d9eb17fAdam Powell} 337