PopupMenu.java revision 3f9832d3c7b30bf4dc8ed9e5123b02daca96c878
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 android.widget; 18 19import com.android.internal.view.menu.MenuBuilder; 20import com.android.internal.view.menu.MenuPopupHelper; 21import com.android.internal.view.menu.MenuPresenter; 22import com.android.internal.view.menu.SubMenuBuilder; 23 24import android.content.Context; 25import android.view.Menu; 26import android.view.MenuInflater; 27import android.view.MenuItem; 28import android.view.View; 29import android.view.View.OnTouchListener; 30import android.widget.ListPopupWindow.ForwardingListener; 31 32/** 33 * A PopupMenu displays a {@link Menu} in a modal popup window anchored to a {@link View}. 34 * The popup will appear below the anchor view if there is room, or above it if there is not. 35 * If the IME is visible the popup will not overlap it until it is touched. Touching outside 36 * of the popup will dismiss it. 37 */ 38public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback { 39 private Context mContext; 40 private MenuBuilder mMenu; 41 private View mAnchor; 42 private MenuPopupHelper mPopup; 43 private OnMenuItemClickListener mMenuItemClickListener; 44 private OnDismissListener mDismissListener; 45 private OnTouchListener mDragListener; 46 47 /** 48 * Callback interface used to notify the application that the menu has closed. 49 */ 50 public interface OnDismissListener { 51 /** 52 * Called when the associated menu has been dismissed. 53 * 54 * @param menu The PopupMenu that was dismissed. 55 */ 56 public void onDismiss(PopupMenu menu); 57 } 58 59 /** 60 * Construct a new PopupMenu. 61 * 62 * @param context Context for the PopupMenu. 63 * @param anchor Anchor view for this popup. The popup will appear below the anchor if there 64 * is room, or above it if there is not. 65 */ 66 public PopupMenu(Context context, View anchor) { 67 // TODO Theme? 68 mContext = context; 69 mMenu = new MenuBuilder(context); 70 mMenu.setCallback(this); 71 mAnchor = anchor; 72 mPopup = new MenuPopupHelper(context, mMenu, anchor); 73 mPopup.setCallback(this); 74 } 75 76 /** 77 * Returns an {@link OnTouchListener} that can be added to the anchor view 78 * to implement drag-to-open behavior. 79 * <p> 80 * When the listener is set on a view, touching that view and dragging 81 * outside of its bounds will open the popup window. Lifting will select the 82 * currently touched list item. 83 * <p> 84 * Example usage: 85 * <pre> 86 * PopupMenu myPopup = new PopupMenu(context, myAnchor); 87 * myAnchor.setOnTouchListener(myPopup.getDragToOpenListener()); 88 * </pre> 89 * 90 * @return a touch listener that controls drag-to-open behavior 91 */ 92 public OnTouchListener getDragToOpenListener() { 93 if (mDragListener == null) { 94 mDragListener = new ForwardingListener(mAnchor) { 95 @Override 96 public ListPopupWindow getPopup() { 97 return mPopup.getPopup(); 98 } 99 }; 100 } 101 102 return mDragListener; 103 } 104 105 /** 106 * @return the {@link Menu} associated with this popup. Populate the returned Menu with 107 * items before calling {@link #show()}. 108 * 109 * @see #show() 110 * @see #getMenuInflater() 111 */ 112 public Menu getMenu() { 113 return mMenu; 114 } 115 116 /** 117 * @return a {@link MenuInflater} that can be used to inflate menu items from XML into the 118 * menu returned by {@link #getMenu()}. 119 * 120 * @see #getMenu() 121 */ 122 public MenuInflater getMenuInflater() { 123 return new MenuInflater(mContext); 124 } 125 126 /** 127 * Inflate a menu resource into this PopupMenu. This is equivalent to calling 128 * popupMenu.getMenuInflater().inflate(menuRes, popupMenu.getMenu()). 129 * @param menuRes Menu resource to inflate 130 */ 131 public void inflate(int menuRes) { 132 getMenuInflater().inflate(menuRes, mMenu); 133 } 134 135 /** 136 * Show the menu popup anchored to the view specified during construction. 137 * @see #dismiss() 138 */ 139 public void show() { 140 mPopup.show(); 141 } 142 143 /** 144 * Dismiss the menu popup. 145 * @see #show() 146 */ 147 public void dismiss() { 148 mPopup.dismiss(); 149 } 150 151 /** 152 * Set a listener that will be notified when the user selects an item from the menu. 153 * 154 * @param listener Listener to notify 155 */ 156 public void setOnMenuItemClickListener(OnMenuItemClickListener listener) { 157 mMenuItemClickListener = listener; 158 } 159 160 /** 161 * Set a listener that will be notified when this menu is dismissed. 162 * 163 * @param listener Listener to notify 164 */ 165 public void setOnDismissListener(OnDismissListener listener) { 166 mDismissListener = listener; 167 } 168 169 /** 170 * @hide 171 */ 172 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 173 if (mMenuItemClickListener != null) { 174 return mMenuItemClickListener.onMenuItemClick(item); 175 } 176 return false; 177 } 178 179 /** 180 * @hide 181 */ 182 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 183 if (mDismissListener != null) { 184 mDismissListener.onDismiss(this); 185 } 186 } 187 188 /** 189 * @hide 190 */ 191 public boolean onOpenSubMenu(MenuBuilder subMenu) { 192 if (subMenu == null) return false; 193 194 if (!subMenu.hasVisibleItems()) { 195 return true; 196 } 197 198 // Current menu will be dismissed by the normal helper, submenu will be shown in its place. 199 new MenuPopupHelper(mContext, subMenu, mAnchor).show(); 200 return true; 201 } 202 203 /** 204 * @hide 205 */ 206 public void onCloseSubMenu(SubMenuBuilder menu) { 207 } 208 209 /** 210 * @hide 211 */ 212 public void onMenuModeChange(MenuBuilder menu) { 213 } 214 215 /** 216 * Interface responsible for receiving menu item click events if the items themselves 217 * do not have individual item click listeners. 218 */ 219 public interface OnMenuItemClickListener { 220 /** 221 * This method will be invoked when a menu item is clicked if the item itself did 222 * not already handle the event. 223 * 224 * @param item {@link MenuItem} that was clicked 225 * @return <code>true</code> if the event was handled, <code>false</code> otherwise. 226 */ 227 public boolean onMenuItemClick(MenuItem item); 228 } 229} 230