• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.widget;
18 
19 import com.android.internal.R;
20 import com.android.internal.view.menu.MenuBuilder;
21 import com.android.internal.view.menu.MenuPopupHelper;
22 import com.android.internal.view.menu.MenuPresenter;
23 import com.android.internal.view.menu.SubMenuBuilder;
24 
25 import android.content.Context;
26 import android.view.Gravity;
27 import android.view.Menu;
28 import android.view.MenuInflater;
29 import android.view.MenuItem;
30 import android.view.View;
31 import android.view.View.OnTouchListener;
32 import android.widget.ListPopupWindow.ForwardingListener;
33 
34 /**
35  * A PopupMenu displays a {@link Menu} in a modal popup window anchored to a {@link View}.
36  * The popup will appear below the anchor view if there is room, or above it if there is not.
37  * If the IME is visible the popup will not overlap it until it is touched. Touching outside
38  * of the popup will dismiss it.
39  */
40 public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
41     private final Context mContext;
42     private final MenuBuilder mMenu;
43     private final View mAnchor;
44     private final MenuPopupHelper mPopup;
45 
46     private OnMenuItemClickListener mMenuItemClickListener;
47     private OnDismissListener mDismissListener;
48     private OnTouchListener mDragListener;
49 
50     /**
51      * Callback interface used to notify the application that the menu has closed.
52      */
53     public interface OnDismissListener {
54         /**
55          * Called when the associated menu has been dismissed.
56          *
57          * @param menu The PopupMenu that was dismissed.
58          */
onDismiss(PopupMenu menu)59         public void onDismiss(PopupMenu menu);
60     }
61 
62     /**
63      * Constructor to create a new popup menu with an anchor view.
64      *
65      * @param context Context the popup menu is running in, through which it
66      *        can access the current theme, resources, etc.
67      * @param anchor Anchor view for this popup. The popup will appear below
68      *        the anchor if there is room, or above it if there is not.
69      */
PopupMenu(Context context, View anchor)70     public PopupMenu(Context context, View anchor) {
71         this(context, anchor, Gravity.NO_GRAVITY);
72     }
73 
74     /**
75      * Constructor to create a new popup menu with an anchor view and alignment
76      * gravity.
77      *
78      * @param context Context the popup menu is running in, through which it
79      *        can access the current theme, resources, etc.
80      * @param anchor Anchor view for this popup. The popup will appear below
81      *        the anchor if there is room, or above it if there is not.
82      * @param gravity The {@link Gravity} value for aligning the popup with its
83      *        anchor.
84      */
PopupMenu(Context context, View anchor, int gravity)85     public PopupMenu(Context context, View anchor, int gravity) {
86         this(context, anchor, gravity, R.attr.popupMenuStyle, 0);
87     }
88 
89     /**
90      * Constructor a create a new popup menu with a specific style.
91      *
92      * @param context Context the popup menu is running in, through which it
93      *        can access the current theme, resources, etc.
94      * @param anchor Anchor view for this popup. The popup will appear below
95      *        the anchor if there is room, or above it if there is not.
96      * @param gravity The {@link Gravity} value for aligning the popup with its
97      *        anchor.
98      * @param popupStyleAttr An attribute in the current theme that contains a
99      *        reference to a style resource that supplies default values for
100      *        the popup window. Can be 0 to not look for defaults.
101      * @param popupStyleRes A resource identifier of a style resource that
102      *        supplies default values for the popup window, used only if
103      *        popupStyleAttr is 0 or can not be found in the theme. Can be 0
104      *        to not look for defaults.
105      */
PopupMenu(Context context, View anchor, int gravity, int popupStyleAttr, int popupStyleRes)106     public PopupMenu(Context context, View anchor, int gravity, int popupStyleAttr,
107             int popupStyleRes) {
108         mContext = context;
109         mMenu = new MenuBuilder(context);
110         mMenu.setCallback(this);
111         mAnchor = anchor;
112         mPopup = new MenuPopupHelper(context, mMenu, anchor, false, popupStyleAttr, popupStyleRes);
113         mPopup.setGravity(gravity);
114         mPopup.setCallback(this);
115     }
116 
117     /**
118      * Returns an {@link OnTouchListener} that can be added to the anchor view
119      * to implement drag-to-open behavior.
120      * <p>
121      * When the listener is set on a view, touching that view and dragging
122      * outside of its bounds will open the popup window. Lifting will select the
123      * currently touched list item.
124      * <p>
125      * Example usage:
126      * <pre>
127      * PopupMenu myPopup = new PopupMenu(context, myAnchor);
128      * myAnchor.setOnTouchListener(myPopup.getDragToOpenListener());
129      * </pre>
130      *
131      * @return a touch listener that controls drag-to-open behavior
132      */
getDragToOpenListener()133     public OnTouchListener getDragToOpenListener() {
134         if (mDragListener == null) {
135             mDragListener = new ForwardingListener(mAnchor) {
136                 @Override
137                 protected boolean onForwardingStarted() {
138                     show();
139                     return true;
140                 }
141 
142                 @Override
143                 protected boolean onForwardingStopped() {
144                     dismiss();
145                     return true;
146                 }
147 
148                 @Override
149                 public ListPopupWindow getPopup() {
150                     // This will be null until show() is called.
151                     return mPopup.getPopup();
152                 }
153             };
154         }
155 
156         return mDragListener;
157     }
158 
159     /**
160      * @return the {@link Menu} associated with this popup. Populate the returned Menu with
161      * items before calling {@link #show()}.
162      *
163      * @see #show()
164      * @see #getMenuInflater()
165      */
getMenu()166     public Menu getMenu() {
167         return mMenu;
168     }
169 
170     /**
171      * @return a {@link MenuInflater} that can be used to inflate menu items from XML into the
172      * menu returned by {@link #getMenu()}.
173      *
174      * @see #getMenu()
175      */
getMenuInflater()176     public MenuInflater getMenuInflater() {
177         return new MenuInflater(mContext);
178     }
179 
180     /**
181      * Inflate a menu resource into this PopupMenu. This is equivalent to calling
182      * popupMenu.getMenuInflater().inflate(menuRes, popupMenu.getMenu()).
183      * @param menuRes Menu resource to inflate
184      */
inflate(int menuRes)185     public void inflate(int menuRes) {
186         getMenuInflater().inflate(menuRes, mMenu);
187     }
188 
189     /**
190      * Show the menu popup anchored to the view specified during construction.
191      * @see #dismiss()
192      */
show()193     public void show() {
194         mPopup.show();
195     }
196 
197     /**
198      * Dismiss the menu popup.
199      * @see #show()
200      */
dismiss()201     public void dismiss() {
202         mPopup.dismiss();
203     }
204 
205     /**
206      * Set a listener that will be notified when the user selects an item from the menu.
207      *
208      * @param listener Listener to notify
209      */
setOnMenuItemClickListener(OnMenuItemClickListener listener)210     public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
211         mMenuItemClickListener = listener;
212     }
213 
214     /**
215      * Set a listener that will be notified when this menu is dismissed.
216      *
217      * @param listener Listener to notify
218      */
setOnDismissListener(OnDismissListener listener)219     public void setOnDismissListener(OnDismissListener listener) {
220         mDismissListener = listener;
221     }
222 
223     /**
224      * @hide
225      */
onMenuItemSelected(MenuBuilder menu, MenuItem item)226     public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
227         if (mMenuItemClickListener != null) {
228             return mMenuItemClickListener.onMenuItemClick(item);
229         }
230         return false;
231     }
232 
233     /**
234      * @hide
235      */
onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing)236     public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
237         if (mDismissListener != null) {
238             mDismissListener.onDismiss(this);
239         }
240     }
241 
242     /**
243      * @hide
244      */
onOpenSubMenu(MenuBuilder subMenu)245     public boolean onOpenSubMenu(MenuBuilder subMenu) {
246         if (subMenu == null) return false;
247 
248         if (!subMenu.hasVisibleItems()) {
249             return true;
250         }
251 
252         // Current menu will be dismissed by the normal helper, submenu will be shown in its place.
253         new MenuPopupHelper(mContext, subMenu, mAnchor).show();
254         return true;
255     }
256 
257     /**
258      * @hide
259      */
onCloseSubMenu(SubMenuBuilder menu)260     public void onCloseSubMenu(SubMenuBuilder menu) {
261     }
262 
263     /**
264      * @hide
265      */
onMenuModeChange(MenuBuilder menu)266     public void onMenuModeChange(MenuBuilder menu) {
267     }
268 
269     /**
270      * Interface responsible for receiving menu item click events if the items themselves
271      * do not have individual item click listeners.
272      */
273     public interface OnMenuItemClickListener {
274         /**
275          * This method will be invoked when a menu item is clicked if the item itself did
276          * not already handle the event.
277          *
278          * @param item {@link MenuItem} that was clicked
279          * @return <code>true</code> if the event was handled, <code>false</code> otherwise.
280          */
onMenuItemClick(MenuItem item)281         public boolean onMenuItemClick(MenuItem item);
282     }
283 }
284