MenuPopupHelper.java revision aa0b92ce2b51987e9c864164234fe968ab5b9311
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; 224267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.util.DisplayMetrics; 238028dd32a4a04154050220dd0693583d5b750330Adam Powellimport android.view.KeyEvent; 248028dd32a4a04154050220dd0693583d5b750330Adam Powellimport android.view.MenuItem; 254267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.view.View; 264267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.view.View.MeasureSpec; 270458796f1401732b38660794148f4c5e5602f432Adam Powellimport android.view.ViewTreeObserver; 284267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.widget.AdapterView; 294267534d1c42af847ed0cefd1c88c99f66b36571Adam Powellimport android.widget.ListPopupWindow; 306c6f575423d6718c3ff322224c1520901ce881e1Adam Powellimport android.widget.PopupWindow; 314267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 328028dd32a4a04154050220dd0693583d5b750330Adam Powellimport java.lang.ref.WeakReference; 338028dd32a4a04154050220dd0693583d5b750330Adam Powell 344267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell/** 354267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell * @hide 364267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell */ 370458796f1401732b38660794148f4c5e5602f432Adam Powellpublic class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener, 388515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener { 394267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell private static final String TAG = "MenuPopupHelper"; 404267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 414267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell private Context mContext; 424267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell private ListPopupWindow mPopup; 438028dd32a4a04154050220dd0693583d5b750330Adam Powell private MenuBuilder mMenu; 444267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell private int mPopupMaxWidth; 458028dd32a4a04154050220dd0693583d5b750330Adam Powell private WeakReference<View> mAnchorView; 468028dd32a4a04154050220dd0693583d5b750330Adam Powell private boolean mOverflowOnly; 470458796f1401732b38660794148f4c5e5602f432Adam Powell private ViewTreeObserver mTreeObserver; 488028dd32a4a04154050220dd0693583d5b750330Adam Powell 498028dd32a4a04154050220dd0693583d5b750330Adam Powell public MenuPopupHelper(Context context, MenuBuilder menu) { 508028dd32a4a04154050220dd0693583d5b750330Adam Powell this(context, menu, null, false); 518028dd32a4a04154050220dd0693583d5b750330Adam Powell } 524267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 538028dd32a4a04154050220dd0693583d5b750330Adam Powell public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) { 548028dd32a4a04154050220dd0693583d5b750330Adam Powell this(context, menu, anchorView, false); 558028dd32a4a04154050220dd0693583d5b750330Adam Powell } 568028dd32a4a04154050220dd0693583d5b750330Adam Powell 578028dd32a4a04154050220dd0693583d5b750330Adam Powell public MenuPopupHelper(Context context, MenuBuilder menu, 588028dd32a4a04154050220dd0693583d5b750330Adam Powell View anchorView, boolean overflowOnly) { 594267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mContext = context; 608028dd32a4a04154050220dd0693583d5b750330Adam Powell mMenu = menu; 618028dd32a4a04154050220dd0693583d5b750330Adam Powell mOverflowOnly = overflowOnly; 624267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 634267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 644267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mPopupMaxWidth = metrics.widthPixels / 2; 658028dd32a4a04154050220dd0693583d5b750330Adam Powell 668028dd32a4a04154050220dd0693583d5b750330Adam Powell if (anchorView != null) { 678028dd32a4a04154050220dd0693583d5b750330Adam Powell mAnchorView = new WeakReference<View>(anchorView); 688028dd32a4a04154050220dd0693583d5b750330Adam Powell } 694267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 704267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 714267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell public void show() { 720b2d306e7000f4c0c6ad4e00d494bb401d8a9fc2Adam Powell mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle); 734267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mPopup.setOnItemClickListener(this); 748515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell mPopup.setOnDismissListener(this); 754267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 768028dd32a4a04154050220dd0693583d5b750330Adam Powell final MenuAdapter adapter = mOverflowOnly ? 778028dd32a4a04154050220dd0693583d5b750330Adam Powell mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) : 788028dd32a4a04154050220dd0693583d5b750330Adam Powell mMenu.getMenuAdapter(MenuBuilder.TYPE_POPUP); 794267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mPopup.setAdapter(adapter); 804267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mPopup.setModal(true); 814267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 820458796f1401732b38660794148f4c5e5602f432Adam Powell View anchor = mAnchorView != null ? mAnchorView.get() : null; 830458796f1401732b38660794148f4c5e5602f432Adam Powell if (anchor == null && mMenu instanceof SubMenuBuilder) { 848028dd32a4a04154050220dd0693583d5b750330Adam Powell SubMenuBuilder subMenu = (SubMenuBuilder) mMenu; 858028dd32a4a04154050220dd0693583d5b750330Adam Powell final MenuItemImpl itemImpl = (MenuItemImpl) subMenu.getItem(); 860458796f1401732b38660794148f4c5e5602f432Adam Powell anchor = itemImpl.getItemView(MenuBuilder.TYPE_ACTION_BUTTON, null); 870458796f1401732b38660794148f4c5e5602f432Adam Powell mAnchorView = new WeakReference<View>(anchor); 880458796f1401732b38660794148f4c5e5602f432Adam Powell } 890458796f1401732b38660794148f4c5e5602f432Adam Powell 900458796f1401732b38660794148f4c5e5602f432Adam Powell if (anchor != null) { 910458796f1401732b38660794148f4c5e5602f432Adam Powell mTreeObserver = anchor.getViewTreeObserver(); 920458796f1401732b38660794148f4c5e5602f432Adam Powell mTreeObserver.addOnGlobalLayoutListener(this); 930458796f1401732b38660794148f4c5e5602f432Adam Powell mPopup.setAnchorView(anchor); 944be0d52125b88dc992a4c500edbe95bf55484e0bAdam Powell } else { 954be0d52125b88dc992a4c500edbe95bf55484e0bAdam Powell throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); 968028dd32a4a04154050220dd0693583d5b750330Adam Powell } 974267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 984267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mPopup.setContentWidth(Math.min(measureContentWidth(adapter), mPopupMaxWidth)); 99aa0b92ce2b51987e9c864164234fe968ab5b9311Adam Powell mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); 1004267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell mPopup.show(); 1018028dd32a4a04154050220dd0693583d5b750330Adam Powell mPopup.getListView().setOnKeyListener(this); 1024267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 1034267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 1044267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell public void dismiss() { 1053d3da27ab394108fd51771616cca3279baae99d1Adam Powell if (isShowing()) { 1063d3da27ab394108fd51771616cca3279baae99d1Adam Powell mPopup.dismiss(); 1073d3da27ab394108fd51771616cca3279baae99d1Adam Powell } 1088515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell } 1098515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell 1108515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell public void onDismiss() { 1118515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell mPopup = null; 112ed8b403cc8066bf76cdf98f8d9906ff810defc5bAdam Powell if (mTreeObserver != null) { 1138515ee846bd76aee86ec5ddfcc4dd1e626dd999cAdam Powell mTreeObserver.removeGlobalOnLayoutListener(MenuPopupHelper.this); 114ed8b403cc8066bf76cdf98f8d9906ff810defc5bAdam Powell mTreeObserver = null; 115ed8b403cc8066bf76cdf98f8d9906ff810defc5bAdam Powell } 1164267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 1174267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 1188028dd32a4a04154050220dd0693583d5b750330Adam Powell public boolean isShowing() { 1198028dd32a4a04154050220dd0693583d5b750330Adam Powell return mPopup != null && mPopup.isShowing(); 1208028dd32a4a04154050220dd0693583d5b750330Adam Powell } 1218028dd32a4a04154050220dd0693583d5b750330Adam Powell 1224267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1238028dd32a4a04154050220dd0693583d5b750330Adam Powell MenuItem item = null; 1248028dd32a4a04154050220dd0693583d5b750330Adam Powell if (mOverflowOnly) { 1258028dd32a4a04154050220dd0693583d5b750330Adam Powell item = mMenu.getOverflowItem(position); 1268028dd32a4a04154050220dd0693583d5b750330Adam Powell } else { 1278028dd32a4a04154050220dd0693583d5b750330Adam Powell item = mMenu.getItem(position); 1288028dd32a4a04154050220dd0693583d5b750330Adam Powell } 1298028dd32a4a04154050220dd0693583d5b750330Adam Powell mMenu.performItemAction(item, 0); 1300458796f1401732b38660794148f4c5e5602f432Adam Powell dismiss(); 1314267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 1324267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell 1338028dd32a4a04154050220dd0693583d5b750330Adam Powell public boolean onKey(View v, int keyCode, KeyEvent event) { 1348028dd32a4a04154050220dd0693583d5b750330Adam Powell if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) { 1358028dd32a4a04154050220dd0693583d5b750330Adam Powell dismiss(); 1368028dd32a4a04154050220dd0693583d5b750330Adam Powell return true; 1378028dd32a4a04154050220dd0693583d5b750330Adam Powell } 1388028dd32a4a04154050220dd0693583d5b750330Adam Powell return false; 1398028dd32a4a04154050220dd0693583d5b750330Adam Powell } 1408028dd32a4a04154050220dd0693583d5b750330Adam Powell 1414267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell private int measureContentWidth(MenuAdapter adapter) { 1424267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell // Menus don't tend to be long, so this is more sane than it looks. 1434267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell int width = 0; 1444267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell View itemView = null; 1454267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell final int widthMeasureSpec = 1464267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 1474267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell final int heightMeasureSpec = 1484267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 1494267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell final int count = adapter.getCount(); 1504267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell for (int i = 0; i < count; i++) { 1514267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell itemView = adapter.getView(i, itemView, null); 1524267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell itemView.measure(widthMeasureSpec, heightMeasureSpec); 1534267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell width = Math.max(width, itemView.getMeasuredWidth()); 1544267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 1554267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell return width; 1564267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell } 1570458796f1401732b38660794148f4c5e5602f432Adam Powell 1580458796f1401732b38660794148f4c5e5602f432Adam Powell @Override 1590458796f1401732b38660794148f4c5e5602f432Adam Powell public void onGlobalLayout() { 1600458796f1401732b38660794148f4c5e5602f432Adam Powell if (!isShowing()) { 1610458796f1401732b38660794148f4c5e5602f432Adam Powell mTreeObserver.removeGlobalOnLayoutListener(this); 1620458796f1401732b38660794148f4c5e5602f432Adam Powell mTreeObserver = null; 1630458796f1401732b38660794148f4c5e5602f432Adam Powell } else { 1640458796f1401732b38660794148f4c5e5602f432Adam Powell final View anchor = mAnchorView != null ? mAnchorView.get() : null; 1650458796f1401732b38660794148f4c5e5602f432Adam Powell if (anchor != null && !anchor.isShown()) { 1660458796f1401732b38660794148f4c5e5602f432Adam Powell dismiss(); 167aa0b92ce2b51987e9c864164234fe968ab5b9311Adam Powell } else { 168aa0b92ce2b51987e9c864164234fe968ab5b9311Adam Powell // Recompute window size and position 169aa0b92ce2b51987e9c864164234fe968ab5b9311Adam Powell mPopup.show(); 1700458796f1401732b38660794148f4c5e5602f432Adam Powell } 1710458796f1401732b38660794148f4c5e5602f432Adam Powell } 1720458796f1401732b38660794148f4c5e5602f432Adam Powell } 1734267534d1c42af847ed0cefd1c88c99f66b36571Adam Powell} 174