MenuPopupHelper.java revision ed8b403cc8066bf76cdf98f8d9906ff810defc5b
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 { 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 private PopupWindow.OnDismissListener mDismissListener = new PopupWindow.OnDismissListener() { 50 public void onDismiss() { 51 mPopup = null; 52 } 53 }; 54 55 public MenuPopupHelper(Context context, MenuBuilder menu) { 56 this(context, menu, null, false); 57 } 58 59 public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) { 60 this(context, menu, anchorView, false); 61 } 62 63 public MenuPopupHelper(Context context, MenuBuilder menu, 64 View anchorView, boolean overflowOnly) { 65 mContext = context; 66 mMenu = menu; 67 mOverflowOnly = overflowOnly; 68 69 final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); 70 mPopupMaxWidth = metrics.widthPixels / 2; 71 72 if (anchorView != null) { 73 mAnchorView = new WeakReference<View>(anchorView); 74 } 75 } 76 77 public void show() { 78 mPopup = new ListPopupWindow(mContext, null, com.android.internal.R.attr.popupMenuStyle); 79 mPopup.setOnItemClickListener(this); 80 mPopup.setOnDismissListener(mDismissListener); 81 82 final MenuAdapter adapter = mOverflowOnly ? 83 mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) : 84 mMenu.getMenuAdapter(MenuBuilder.TYPE_POPUP); 85 mPopup.setAdapter(adapter); 86 mPopup.setModal(true); 87 88 View anchor = mAnchorView != null ? mAnchorView.get() : null; 89 if (anchor == null && mMenu instanceof SubMenuBuilder) { 90 SubMenuBuilder subMenu = (SubMenuBuilder) mMenu; 91 final MenuItemImpl itemImpl = (MenuItemImpl) subMenu.getItem(); 92 anchor = itemImpl.getItemView(MenuBuilder.TYPE_ACTION_BUTTON, null); 93 mAnchorView = new WeakReference<View>(anchor); 94 } 95 96 if (anchor != null) { 97 mTreeObserver = anchor.getViewTreeObserver(); 98 mTreeObserver.addOnGlobalLayoutListener(this); 99 mPopup.setAnchorView(anchor); 100 } else { 101 throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); 102 } 103 104 mPopup.setContentWidth(Math.min(measureContentWidth(adapter), mPopupMaxWidth)); 105 mPopup.show(); 106 mPopup.getListView().setOnKeyListener(this); 107 } 108 109 public void dismiss() { 110 if (isShowing()) { 111 mPopup.dismiss(); 112 } 113 if (mTreeObserver != null) { 114 mTreeObserver.removeGlobalOnLayoutListener(this); 115 mTreeObserver = null; 116 } 117 } 118 119 public boolean isShowing() { 120 return mPopup != null && mPopup.isShowing(); 121 } 122 123 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 124 MenuItem item = null; 125 if (mOverflowOnly) { 126 item = mMenu.getOverflowItem(position); 127 } else { 128 item = mMenu.getItem(position); 129 } 130 mMenu.performItemAction(item, 0); 131 dismiss(); 132 } 133 134 public boolean onKey(View v, int keyCode, KeyEvent event) { 135 if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) { 136 dismiss(); 137 return true; 138 } 139 return false; 140 } 141 142 private int measureContentWidth(MenuAdapter adapter) { 143 // Menus don't tend to be long, so this is more sane than it looks. 144 int width = 0; 145 View itemView = null; 146 final int widthMeasureSpec = 147 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 148 final int heightMeasureSpec = 149 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 150 final int count = adapter.getCount(); 151 for (int i = 0; i < count; i++) { 152 itemView = adapter.getView(i, itemView, null); 153 itemView.measure(widthMeasureSpec, heightMeasureSpec); 154 width = Math.max(width, itemView.getMeasuredWidth()); 155 } 156 return width; 157 } 158 159 @Override 160 public void onGlobalLayout() { 161 if (!isShowing()) { 162 mTreeObserver.removeGlobalOnLayoutListener(this); 163 mTreeObserver = null; 164 } else { 165 final View anchor = mAnchorView != null ? mAnchorView.get() : null; 166 if (anchor != null && !anchor.isShown()) { 167 dismiss(); 168 } 169 } 170 } 171} 172