MenuPopupHelper.java revision aa0b92ce2b51987e9c864164234fe968ab5b9311
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.internal.view.menu; 18 19import com.android.internal.view.menu.MenuBuilder.MenuAdapter; 20 21import android.content.Context; 22import android.util.DisplayMetrics; 23import android.view.KeyEvent; 24import android.view.MenuItem; 25import android.view.View; 26import android.view.View.MeasureSpec; 27import android.view.ViewTreeObserver; 28import android.widget.AdapterView; 29import android.widget.ListPopupWindow; 30import android.widget.PopupWindow; 31 32import java.lang.ref.WeakReference; 33 34/** 35 * @hide 36 */ 37public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener, 38 ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener { 39 private static final String TAG = "MenuPopupHelper"; 40 41 private Context mContext; 42 private ListPopupWindow mPopup; 43 private MenuBuilder mMenu; 44 private int mPopupMaxWidth; 45 private WeakReference<View> mAnchorView; 46 private boolean mOverflowOnly; 47 private ViewTreeObserver mTreeObserver; 48 49 public MenuPopupHelper(Context context, MenuBuilder menu) { 50 this(context, menu, null, false); 51 } 52 53 public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) { 54 this(context, menu, anchorView, false); 55 } 56 57 public MenuPopupHelper(Context context, MenuBuilder menu, 58 View anchorView, boolean overflowOnly) { 59 mContext = context; 60 mMenu = menu; 61 mOverflowOnly = overflowOnly; 62 63 final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 64 mPopupMaxWidth = metrics.widthPixels / 2; 65 66 if (anchorView != null) { 67 mAnchorView = new WeakReference<View>(anchorView); 68 } 69 } 70 71 public void show() { 72 mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle); 73 mPopup.setOnItemClickListener(this); 74 mPopup.setOnDismissListener(this); 75 76 final MenuAdapter adapter = mOverflowOnly ? 77 mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) : 78 mMenu.getMenuAdapter(MenuBuilder.TYPE_POPUP); 79 mPopup.setAdapter(adapter); 80 mPopup.setModal(true); 81 82 View anchor = mAnchorView != null ? mAnchorView.get() : null; 83 if (anchor == null && mMenu instanceof SubMenuBuilder) { 84 SubMenuBuilder subMenu = (SubMenuBuilder) mMenu; 85 final MenuItemImpl itemImpl = (MenuItemImpl) subMenu.getItem(); 86 anchor = itemImpl.getItemView(MenuBuilder.TYPE_ACTION_BUTTON, null); 87 mAnchorView = new WeakReference<View>(anchor); 88 } 89 90 if (anchor != null) { 91 mTreeObserver = anchor.getViewTreeObserver(); 92 mTreeObserver.addOnGlobalLayoutListener(this); 93 mPopup.setAnchorView(anchor); 94 } else { 95 throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); 96 } 97 98 mPopup.setContentWidth(Math.min(measureContentWidth(adapter), mPopupMaxWidth)); 99 mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); 100 mPopup.show(); 101 mPopup.getListView().setOnKeyListener(this); 102 } 103 104 public void dismiss() { 105 if (isShowing()) { 106 mPopup.dismiss(); 107 } 108 } 109 110 public void onDismiss() { 111 mPopup = null; 112 if (mTreeObserver != null) { 113 mTreeObserver.removeGlobalOnLayoutListener(MenuPopupHelper.this); 114 mTreeObserver = null; 115 } 116 } 117 118 public boolean isShowing() { 119 return mPopup != null && mPopup.isShowing(); 120 } 121 122 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 123 MenuItem item = null; 124 if (mOverflowOnly) { 125 item = mMenu.getOverflowItem(position); 126 } else { 127 item = mMenu.getItem(position); 128 } 129 mMenu.performItemAction(item, 0); 130 dismiss(); 131 } 132 133 public boolean onKey(View v, int keyCode, KeyEvent event) { 134 if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) { 135 dismiss(); 136 return true; 137 } 138 return false; 139 } 140 141 private int measureContentWidth(MenuAdapter adapter) { 142 // Menus don't tend to be long, so this is more sane than it looks. 143 int width = 0; 144 View itemView = null; 145 final int widthMeasureSpec = 146 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 147 final int heightMeasureSpec = 148 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 149 final int count = adapter.getCount(); 150 for (int i = 0; i < count; i++) { 151 itemView = adapter.getView(i, itemView, null); 152 itemView.measure(widthMeasureSpec, heightMeasureSpec); 153 width = Math.max(width, itemView.getMeasuredWidth()); 154 } 155 return width; 156 } 157 158 @Override 159 public void onGlobalLayout() { 160 if (!isShowing()) { 161 mTreeObserver.removeGlobalOnLayoutListener(this); 162 mTreeObserver = null; 163 } else { 164 final View anchor = mAnchorView != null ? mAnchorView.get() : null; 165 if (anchor != null && !anchor.isShown()) { 166 dismiss(); 167 } else { 168 // Recompute window size and position 169 mPopup.show(); 170 } 171 } 172 } 173} 174