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