MenuPopupHelper.java revision ca51e8788a58f2af3525b7214a675f2d0233e5da
14267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell/* 24267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * Copyright (C) 2010 The Android Open Source Project 34267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * 44267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * Licensed under the Apache License, Version 2.0 (the "License"); 54267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * you may not use this file except in compliance with the License. 64267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * You may obtain a copy of the License at 74267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * 84267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * http://www.apache.org/licenses/LICENSE-2.0 94267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * 104267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * Unless required by applicable law or agreed to in writing, software 114267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * distributed under the License is distributed on an "AS IS" BASIS, 124267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * See the License for the specific language governing permissions and 144267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * limitations under the License. 154267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell */ 164267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 174267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellpackage com.android.internal.view.menu; 184267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 194267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport com.android.internal.view.menu.MenuBuilder.MenuAdapter; 204267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 214267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.content.Context; 22e2b03a62e5014ea60e24a989544fa549b493a520Adam Powellimport android.os.Handler; 234267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.util.DisplayMetrics; 248028dd32a4a04154050220dd0693583d5b750330Adam Powellimport android.view.KeyEvent; 258028dd32a4a04154050220dd0693583d5b750330Adam Powellimport android.view.MenuItem; 264267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.view.View; 274267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.view.View.MeasureSpec; 280458796f1401732b38660794148f4c5e5602f432Adam Powellimport android.view.ViewTreeObserver; 294267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.widget.AdapterView; 304267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.widget.ListPopupWindow; 316c6f575423d6718c3ff322224c1520901ce881e1Adam Powellimport android.widget.PopupWindow; 324267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 338028dd32a4a04154050220dd0693583d5b750330Adam Powellimport java.lang.ref.WeakReference; 348028dd32a4a04154050220dd0693583d5b750330Adam Powell 354267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell/** 364267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * @hide 374267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell */ 380458796f1401732b38660794148f4c5e5602f432Adam Powellpublic class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener, 398515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener { 404267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell private static final String TAG = "MenuPopupHelper"; 414267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 424267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell private Context mContext; 434267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell private ListPopupWindow mPopup; 448028dd32a4a04154050220dd0693583d5b750330Adam Powell private MenuBuilder mMenu; 454267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell private int mPopupMaxWidth; 468028dd32a4a04154050220dd0693583d5b750330Adam Powell private WeakReference<View> mAnchorView; 478028dd32a4a04154050220dd0693583d5b750330Adam Powell private boolean mOverflowOnly; 480458796f1401732b38660794148f4c5e5602f432Adam Powell private ViewTreeObserver mTreeObserver; 498028dd32a4a04154050220dd0693583d5b750330Adam Powell 50e2b03a62e5014ea60e24a989544fa549b493a520Adam Powell private final Handler mHandler = new Handler(); 51e2b03a62e5014ea60e24a989544fa549b493a520Adam Powell 528028dd32a4a04154050220dd0693583d5b750330Adam Powell public MenuPopupHelper(Context context, MenuBuilder menu) { 538028dd32a4a04154050220dd0693583d5b750330Adam Powell this(context, menu, null, false); 548028dd32a4a04154050220dd0693583d5b750330Adam Powell } 554267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 568028dd32a4a04154050220dd0693583d5b750330Adam Powell public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) { 578028dd32a4a04154050220dd0693583d5b750330Adam Powell this(context, menu, anchorView, false); 588028dd32a4a04154050220dd0693583d5b750330Adam Powell } 598028dd32a4a04154050220dd0693583d5b750330Adam Powell 608028dd32a4a04154050220dd0693583d5b750330Adam Powell public MenuPopupHelper(Context context, MenuBuilder menu, 618028dd32a4a04154050220dd0693583d5b750330Adam Powell View anchorView, boolean overflowOnly) { 624267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mContext = context; 638028dd32a4a04154050220dd0693583d5b750330Adam Powell mMenu = menu; 648028dd32a4a04154050220dd0693583d5b750330Adam Powell mOverflowOnly = overflowOnly; 654267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 664267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 674267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mPopupMaxWidth = metrics.widthPixels / 2; 688028dd32a4a04154050220dd0693583d5b750330Adam Powell 698028dd32a4a04154050220dd0693583d5b750330Adam Powell if (anchorView != null) { 708028dd32a4a04154050220dd0693583d5b750330Adam Powell mAnchorView = new WeakReference<View>(anchorView); 718028dd32a4a04154050220dd0693583d5b750330Adam Powell } 724267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 734267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 74f0ad6e6eaf48ac8f4007232ad0a8511a7b5cfc0eAdam Powell public void setAnchorView(View anchor) { 75f0ad6e6eaf48ac8f4007232ad0a8511a7b5cfc0eAdam Powell mAnchorView = new WeakReference<View>(anchor); 76f0ad6e6eaf48ac8f4007232ad0a8511a7b5cfc0eAdam Powell } 77f0ad6e6eaf48ac8f4007232ad0a8511a7b5cfc0eAdam Powell 784267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell public void show() { 795e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell if (!tryShow()) { 805e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); 815e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell } 825e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell } 835e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell 845e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell public boolean tryShow() { 850b2d306e7000f4c0c6ad4e00d494bb401d8a9fc2Adam Powell mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle); 864267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mPopup.setOnItemClickListener(this); 878515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell mPopup.setOnDismissListener(this); 884267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 898028dd32a4a04154050220dd0693583d5b750330Adam Powell final MenuAdapter adapter = mOverflowOnly ? 908028dd32a4a04154050220dd0693583d5b750330Adam Powell mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) : 918028dd32a4a04154050220dd0693583d5b750330Adam Powell mMenu.getMenuAdapter(MenuBuilder.TYPE_POPUP); 924267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mPopup.setAdapter(adapter); 934267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mPopup.setModal(true); 944267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 950458796f1401732b38660794148f4c5e5602f432Adam Powell View anchor = mAnchorView != null ? mAnchorView.get() : null; 960458796f1401732b38660794148f4c5e5602f432Adam Powell if (anchor == null && mMenu instanceof SubMenuBuilder) { 978028dd32a4a04154050220dd0693583d5b750330Adam Powell SubMenuBuilder subMenu = (SubMenuBuilder) mMenu; 988028dd32a4a04154050220dd0693583d5b750330Adam Powell final MenuItemImpl itemImpl = (MenuItemImpl) subMenu.getItem(); 990458796f1401732b38660794148f4c5e5602f432Adam Powell anchor = itemImpl.getItemView(MenuBuilder.TYPE_ACTION_BUTTON, null); 1000458796f1401732b38660794148f4c5e5602f432Adam Powell mAnchorView = new WeakReference<View>(anchor); 1010458796f1401732b38660794148f4c5e5602f432Adam Powell } 1020458796f1401732b38660794148f4c5e5602f432Adam Powell 1030458796f1401732b38660794148f4c5e5602f432Adam Powell if (anchor != null) { 104ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell if (mTreeObserver == null) { 105ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell mTreeObserver = anchor.getViewTreeObserver(); 106ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell mTreeObserver.addOnGlobalLayoutListener(this); 107ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell } 1080458796f1401732b38660794148f4c5e5602f432Adam Powell mPopup.setAnchorView(anchor); 1094be0d52125b88dc992a4c500edbe95bf55484e0bAdam Powell } else { 1105e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell return false; 1118028dd32a4a04154050220dd0693583d5b750330Adam Powell } 1124267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 1134267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mPopup.setContentWidth(Math.min(measureContentWidth(adapter), mPopupMaxWidth)); 114aa0b92ce2b51987e9c864164234fe968ab5b9311Adam Powell mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); 1154267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mPopup.show(); 1168028dd32a4a04154050220dd0693583d5b750330Adam Powell mPopup.getListView().setOnKeyListener(this); 1175e3f284baa271cb0fbf90e504d19fdd2e385382eAdam Powell return true; 1184267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 1194267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 1204267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell public void dismiss() { 1213d3da27ab394108fd51771616cca3279baae99d1Adam Powell if (isShowing()) { 1223d3da27ab394108fd51771616cca3279baae99d1Adam Powell mPopup.dismiss(); 1233d3da27ab394108fd51771616cca3279baae99d1Adam Powell } 1248515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell } 1258515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell 1268515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell public void onDismiss() { 1278515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell mPopup = null; 128ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell if (mTreeObserver != null && mTreeObserver.isAlive()) { 129ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell mTreeObserver.removeGlobalOnLayoutListener(this); 130ed8b403cc8066bf76cdf98f8d9906ff810defc5bAdam Powell } 131ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell mTreeObserver = null; 1324267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 1334267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 1348028dd32a4a04154050220dd0693583d5b750330Adam Powell public boolean isShowing() { 1358028dd32a4a04154050220dd0693583d5b750330Adam Powell return mPopup != null && mPopup.isShowing(); 1368028dd32a4a04154050220dd0693583d5b750330Adam Powell } 1378028dd32a4a04154050220dd0693583d5b750330Adam Powell 1384267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 139ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell if (!isShowing()) return; 140ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell 1418028dd32a4a04154050220dd0693583d5b750330Adam Powell MenuItem item = null; 1428028dd32a4a04154050220dd0693583d5b750330Adam Powell if (mOverflowOnly) { 1438028dd32a4a04154050220dd0693583d5b750330Adam Powell item = mMenu.getOverflowItem(position); 1448028dd32a4a04154050220dd0693583d5b750330Adam Powell } else { 1451700ae0aef4e6a3f412b758389955abd049060dbAdam Powell item = mMenu.getVisibleItems().get(position); 1468028dd32a4a04154050220dd0693583d5b750330Adam Powell } 1470458796f1401732b38660794148f4c5e5602f432Adam Powell dismiss(); 148e2b03a62e5014ea60e24a989544fa549b493a520Adam Powell 149e2b03a62e5014ea60e24a989544fa549b493a520Adam Powell final MenuItem performItem = item; 150e2b03a62e5014ea60e24a989544fa549b493a520Adam Powell mHandler.post(new Runnable() { 151e2b03a62e5014ea60e24a989544fa549b493a520Adam Powell public void run() { 152e2b03a62e5014ea60e24a989544fa549b493a520Adam Powell mMenu.performItemAction(performItem, 0); 153e2b03a62e5014ea60e24a989544fa549b493a520Adam Powell } 154e2b03a62e5014ea60e24a989544fa549b493a520Adam Powell }); 1554267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 1564267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 1578028dd32a4a04154050220dd0693583d5b750330Adam Powell public boolean onKey(View v, int keyCode, KeyEvent event) { 1588028dd32a4a04154050220dd0693583d5b750330Adam Powell if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) { 1598028dd32a4a04154050220dd0693583d5b750330Adam Powell dismiss(); 1608028dd32a4a04154050220dd0693583d5b750330Adam Powell return true; 1618028dd32a4a04154050220dd0693583d5b750330Adam Powell } 1628028dd32a4a04154050220dd0693583d5b750330Adam Powell return false; 1638028dd32a4a04154050220dd0693583d5b750330Adam Powell } 1648028dd32a4a04154050220dd0693583d5b750330Adam Powell 1654267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell private int measureContentWidth(MenuAdapter adapter) { 1664267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell // Menus don't tend to be long, so this is more sane than it looks. 1674267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell int width = 0; 1684267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell View itemView = null; 16950f784cf2dc2dea8061153ac3a843f60a9d88781Adam Powell int itemType = 0; 1704267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell final int widthMeasureSpec = 1714267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 1724267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell final int heightMeasureSpec = 1734267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 1744267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell final int count = adapter.getCount(); 1754267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell for (int i = 0; i < count; i++) { 17650f784cf2dc2dea8061153ac3a843f60a9d88781Adam Powell final int positionType = adapter.getItemViewType(i); 17750f784cf2dc2dea8061153ac3a843f60a9d88781Adam Powell if (positionType != itemType) { 17850f784cf2dc2dea8061153ac3a843f60a9d88781Adam Powell itemType = positionType; 17950f784cf2dc2dea8061153ac3a843f60a9d88781Adam Powell itemView = null; 18050f784cf2dc2dea8061153ac3a843f60a9d88781Adam Powell } 1814267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell itemView = adapter.getView(i, itemView, null); 1824267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell itemView.measure(widthMeasureSpec, heightMeasureSpec); 1834267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell width = Math.max(width, itemView.getMeasuredWidth()); 1844267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 1854267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell return width; 1864267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 1870458796f1401732b38660794148f4c5e5602f432Adam Powell 1880458796f1401732b38660794148f4c5e5602f432Adam Powell @Override 1890458796f1401732b38660794148f4c5e5602f432Adam Powell public void onGlobalLayout() { 1900458796f1401732b38660794148f4c5e5602f432Adam Powell if (!isShowing()) { 191ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell if (mTreeObserver.isAlive()) { 192ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell mTreeObserver.removeGlobalOnLayoutListener(this); 193ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell } 1940458796f1401732b38660794148f4c5e5602f432Adam Powell mTreeObserver = null; 1950458796f1401732b38660794148f4c5e5602f432Adam Powell } else { 1960458796f1401732b38660794148f4c5e5602f432Adam Powell final View anchor = mAnchorView != null ? mAnchorView.get() : null; 197ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell if (anchor == null || !anchor.isShown()) { 1980458796f1401732b38660794148f4c5e5602f432Adam Powell dismiss(); 199ca51e8788a58f2af3525b7214a675f2d0233e5daAdam Powell } else if (isShowing()) { 200aa0b92ce2b51987e9c864164234fe968ab5b9311Adam Powell // Recompute window size and position 201aa0b92ce2b51987e9c864164234fe968ab5b9311Adam Powell mPopup.show(); 2020458796f1401732b38660794148f4c5e5602f432Adam Powell } 2030458796f1401732b38660794148f4c5e5602f432Adam Powell } 2040458796f1401732b38660794148f4c5e5602f432Adam Powell } 2054267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell} 206