1 /* 2 * Copyright (C) 2011 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.support.v4.view; 18 19 import android.support.v4.internal.view.SupportMenuItem; 20 import android.util.Log; 21 import android.view.MenuItem; 22 import android.view.View; 23 24 /** 25 * Helper for accessing features in {@link android.view.MenuItem} 26 * introduced after API level 4 in a backwards compatible fashion. 27 * <p class="note"><strong>Note:</strong> You cannot get an instance of this class. Instead, 28 * it provides <em>static</em> methods that correspond to the methods in {@link 29 * android.view.MenuItem}, but take a {@link android.view.MenuItem} object as an additional 30 * argument.</p> 31 */ 32 public final class MenuItemCompat { 33 private static final String TAG = "MenuItemCompat"; 34 35 /** 36 * Never show this item as a button in an Action Bar. 37 */ 38 public static final int SHOW_AS_ACTION_NEVER = 0; 39 40 /** 41 * Show this item as a button in an Action Bar if the system 42 * decides there is room for it. 43 */ 44 public static final int SHOW_AS_ACTION_IF_ROOM = 1; 45 46 /** 47 * Always show this item as a button in an Action Bar. Use sparingly! 48 * If too many items are set to always show in the Action Bar it can 49 * crowd the Action Bar and degrade the user experience on devices with 50 * smaller screens. A good rule of thumb is to have no more than 2 51 * items set to always show at a time. 52 */ 53 public static final int SHOW_AS_ACTION_ALWAYS = 2; 54 55 /** 56 * When this item is in the action bar, always show it with a 57 * text label even if it also has an icon specified. 58 */ 59 public static final int SHOW_AS_ACTION_WITH_TEXT = 4; 60 61 /** 62 * This item's action view collapses to a normal menu item. 63 * When expanded, the action view temporarily takes over 64 * a larger segment of its container. 65 */ 66 public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; 67 68 /** 69 * Interface for the full API. 70 */ 71 interface MenuVersionImpl { setShowAsAction(MenuItem item, int actionEnum)72 void setShowAsAction(MenuItem item, int actionEnum); setActionView(MenuItem item, View view)73 MenuItem setActionView(MenuItem item, View view); setActionView(MenuItem item, int resId)74 MenuItem setActionView(MenuItem item, int resId); getActionView(MenuItem item)75 View getActionView(MenuItem item); expandActionView(MenuItem item)76 boolean expandActionView(MenuItem item); collapseActionView(MenuItem item)77 boolean collapseActionView(MenuItem item); isActionViewExpanded(MenuItem item)78 boolean isActionViewExpanded(MenuItem item); setOnActionExpandListener(MenuItem item, OnActionExpandListener listener)79 MenuItem setOnActionExpandListener(MenuItem item, OnActionExpandListener listener); 80 } 81 82 /** 83 * Interface definition for a callback to be invoked when a menu item marked with {@link 84 * #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} is expanded or collapsed. 85 * 86 * @see #expandActionView(android.view.MenuItem) 87 * @see #collapseActionView(android.view.MenuItem) 88 * @see #setShowAsAction(android.view.MenuItem, int) 89 */ 90 public interface OnActionExpandListener { 91 92 /** 93 * Called when a menu item with {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} 94 * is expanded. 95 * 96 * @param item Item that was expanded 97 * @return true if the item should expand, false if expansion should be suppressed. 98 */ onMenuItemActionExpand(MenuItem item)99 public boolean onMenuItemActionExpand(MenuItem item); 100 101 /** 102 * Called when a menu item with {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} 103 * is collapsed. 104 * 105 * @param item Item that was collapsed 106 * @return true if the item should collapse, false if collapsing should be suppressed. 107 */ onMenuItemActionCollapse(MenuItem item)108 public boolean onMenuItemActionCollapse(MenuItem item); 109 } 110 111 /** 112 * Interface implementation that doesn't use anything about v4 APIs. 113 */ 114 static class BaseMenuVersionImpl implements MenuVersionImpl { 115 @Override setShowAsAction(MenuItem item, int actionEnum)116 public void setShowAsAction(MenuItem item, int actionEnum) { 117 } 118 119 @Override setActionView(MenuItem item, View view)120 public MenuItem setActionView(MenuItem item, View view) { 121 return item; 122 } 123 124 @Override setActionView(MenuItem item, int resId)125 public MenuItem setActionView(MenuItem item, int resId) { 126 return item; 127 } 128 129 @Override getActionView(MenuItem item)130 public View getActionView(MenuItem item) { 131 return null; 132 } 133 134 @Override expandActionView(MenuItem item)135 public boolean expandActionView(MenuItem item) { 136 return false; 137 } 138 139 @Override collapseActionView(MenuItem item)140 public boolean collapseActionView(MenuItem item) { 141 return false; 142 } 143 144 @Override isActionViewExpanded(MenuItem item)145 public boolean isActionViewExpanded(MenuItem item) { 146 return false; 147 } 148 149 @Override setOnActionExpandListener(MenuItem item, OnActionExpandListener listener)150 public MenuItem setOnActionExpandListener(MenuItem item, OnActionExpandListener listener) { 151 return item; 152 } 153 } 154 155 /** 156 * Interface implementation for devices with at least v11 APIs. 157 */ 158 static class HoneycombMenuVersionImpl implements MenuVersionImpl { 159 @Override setShowAsAction(MenuItem item, int actionEnum)160 public void setShowAsAction(MenuItem item, int actionEnum) { 161 MenuItemCompatHoneycomb.setShowAsAction(item, actionEnum); 162 } 163 164 @Override setActionView(MenuItem item, View view)165 public MenuItem setActionView(MenuItem item, View view) { 166 return MenuItemCompatHoneycomb.setActionView(item, view); 167 } 168 169 @Override setActionView(MenuItem item, int resId)170 public MenuItem setActionView(MenuItem item, int resId) { 171 return MenuItemCompatHoneycomb.setActionView(item, resId); 172 } 173 174 @Override getActionView(MenuItem item)175 public View getActionView(MenuItem item) { 176 return MenuItemCompatHoneycomb.getActionView(item); 177 } 178 179 @Override expandActionView(MenuItem item)180 public boolean expandActionView(MenuItem item) { 181 return false; 182 } 183 184 @Override collapseActionView(MenuItem item)185 public boolean collapseActionView(MenuItem item) { 186 return false; 187 } 188 189 @Override isActionViewExpanded(MenuItem item)190 public boolean isActionViewExpanded(MenuItem item) { 191 return false; 192 } 193 194 @Override setOnActionExpandListener(MenuItem item, OnActionExpandListener listener)195 public MenuItem setOnActionExpandListener(MenuItem item, OnActionExpandListener listener) { 196 return item; 197 } 198 } 199 200 static class IcsMenuVersionImpl extends HoneycombMenuVersionImpl { 201 @Override expandActionView(MenuItem item)202 public boolean expandActionView(MenuItem item) { 203 return MenuItemCompatIcs.expandActionView(item); 204 } 205 206 @Override collapseActionView(MenuItem item)207 public boolean collapseActionView(MenuItem item) { 208 return MenuItemCompatIcs.collapseActionView(item); 209 } 210 211 @Override isActionViewExpanded(MenuItem item)212 public boolean isActionViewExpanded(MenuItem item) { 213 return MenuItemCompatIcs.isActionViewExpanded(item); 214 } 215 216 @Override setOnActionExpandListener(MenuItem item, final OnActionExpandListener listener)217 public MenuItem setOnActionExpandListener(MenuItem item, 218 final OnActionExpandListener listener) { 219 if (listener == null) { 220 return MenuItemCompatIcs.setOnActionExpandListener(item, null); 221 } 222 /* 223 * MenuItemCompatIcs is a dependency of this segment of the support lib 224 * but not the other way around, so we need to take an extra step here to proxy 225 * to the right types. 226 */ 227 return MenuItemCompatIcs.setOnActionExpandListener(item, 228 new MenuItemCompatIcs.SupportActionExpandProxy() { 229 @Override 230 public boolean onMenuItemActionExpand(MenuItem item) { 231 return listener.onMenuItemActionExpand(item); 232 } 233 234 @Override 235 public boolean onMenuItemActionCollapse(MenuItem item) { 236 return listener.onMenuItemActionCollapse(item); 237 } 238 }); 239 } 240 } 241 242 /** 243 * Select the correct implementation to use for the current platform. 244 */ 245 static final MenuVersionImpl IMPL; 246 static { 247 final int version = android.os.Build.VERSION.SDK_INT; 248 if (version >= 14) { 249 IMPL = new IcsMenuVersionImpl(); 250 } else if (version >= 11) { 251 IMPL = new HoneycombMenuVersionImpl(); 252 } else { 253 IMPL = new BaseMenuVersionImpl(); 254 } 255 } 256 257 // ------------------------------------------------------------------- 258 259 /** 260 * Sets how this item should display in the presence of a compatible Action Bar. If the given 261 * item is compatible, this will call the item's supported implementation of 262 * {@link MenuItem#setShowAsAction(int)}. 263 * 264 * @param item - the item to change 265 * @param actionEnum - How the item should display. 266 */ 267 public static void setShowAsAction(MenuItem item, int actionEnum) { 268 if (item instanceof SupportMenuItem) { 269 ((SupportMenuItem) item).setShowAsAction(actionEnum); 270 } else { 271 IMPL.setShowAsAction(item, actionEnum); 272 } 273 } 274 275 /** 276 * Set an action view for this menu item. An action view will be displayed in place 277 * of an automatically generated menu item element in the UI when this item is shown 278 * as an action within a parent. 279 * 280 * @param item the item to change 281 * @param view View to use for presenting this item to the user. 282 * @return This Item so additional setters can be called. 283 * 284 * @see #setShowAsAction(MenuItem, int) 285 */ 286 public static MenuItem setActionView(MenuItem item, View view) { 287 if (item instanceof SupportMenuItem) { 288 return ((SupportMenuItem) item).setActionView(view); 289 } 290 return IMPL.setActionView(item, view); 291 } 292 293 /** 294 * Set an action view for this menu item. An action view will be displayed in place 295 * of an automatically generated menu item element in the UI when this item is shown 296 * as an action within a parent. 297 * <p> 298 * <strong>Note:</strong> Setting an action view overrides the action provider 299 * set via {@link #setActionProvider(MenuItem, ActionProvider)}. 300 * </p> 301 * 302 * @param item the item to change 303 * @param resId Layout resource to use for presenting this item to the user. 304 * @return This Item so additional setters can be called. 305 * 306 * @see #setShowAsAction(MenuItem, int) 307 */ 308 public static MenuItem setActionView(MenuItem item, int resId) { 309 if (item instanceof SupportMenuItem) { 310 return ((SupportMenuItem) item).setActionView(resId); 311 } 312 return IMPL.setActionView(item, resId); 313 } 314 315 /** 316 * Returns the currently set action view for this menu item. 317 * 318 * @param item the item to query 319 * @return This item's action view 320 */ 321 public static View getActionView(MenuItem item) { 322 if (item instanceof SupportMenuItem) { 323 return ((SupportMenuItem) item).getActionView(); 324 } 325 return IMPL.getActionView(item); 326 } 327 328 /** 329 * Sets the {@link ActionProvider} responsible for creating an action view if 330 * the item is placed on the action bar. The provider also provides a default 331 * action invoked if the item is placed in the overflow menu. 332 * <p> 333 * <strong>Note:</strong> Setting an action provider overrides the action view 334 * set via {@link #setActionView(MenuItem, View)}. 335 * </p> 336 * 337 * @param item item to change 338 * @param provider The action provider. 339 * @return This Item so additional setters can be called. 340 * 341 * @see ActionProvider 342 */ 343 public static MenuItem setActionProvider(MenuItem item, ActionProvider provider) { 344 if (item instanceof SupportMenuItem) { 345 return ((SupportMenuItem) item).setSupportActionProvider(provider); 346 } 347 // TODO Wrap the support ActionProvider and assign it 348 Log.w(TAG, "setActionProvider: item does not implement SupportMenuItem; ignoring"); 349 return item; 350 } 351 352 /** 353 * Gets the {@link ActionProvider}. 354 * 355 * @return The action provider. 356 * 357 * @see ActionProvider 358 * @see #setActionProvider(MenuItem, ActionProvider) 359 */ 360 public static ActionProvider getActionProvider(MenuItem item) { 361 if (item instanceof SupportMenuItem) { 362 return ((SupportMenuItem) item).getSupportActionProvider(); 363 } 364 365 // TODO Wrap the framework ActionProvider and return it 366 Log.w(TAG, "getActionProvider: item does not implement SupportMenuItem; returning null"); 367 return null; 368 } 369 370 /** 371 * Expand the action view associated with this menu item. 372 * The menu item must have an action view set, as well as 373 * the showAsAction flag {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. 374 * If a listener has been set using 375 * {@link #setOnActionExpandListener(MenuItem, OnActionExpandListener)} 376 * it will have its {@link OnActionExpandListener#onMenuItemActionExpand(MenuItem)} 377 * method invoked. The listener may return false from this method to prevent expanding 378 * the action view. 379 * 380 * @return true if the action view was expanded, false otherwise. 381 */ 382 public static boolean expandActionView(MenuItem item) { 383 if (item instanceof SupportMenuItem) { 384 return ((SupportMenuItem) item).expandActionView(); 385 } 386 return IMPL.expandActionView(item); 387 } 388 389 /** 390 * Collapse the action view associated with this menu item. The menu item must have an action 391 * view set, as well as the showAsAction flag {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. If a 392 * listener has been set using {@link #setOnActionExpandListener(MenuItem, 393 * android.support.v4.view.MenuItemCompat.OnActionExpandListener)} 394 * it will have its {@link 395 * android.support.v4.view.MenuItemCompat.OnActionExpandListener#onMenuItemActionCollapse(MenuItem)} 396 * method invoked. The listener may return false from this method to prevent collapsing 397 * the action view. 398 * 399 * @return true if the action view was collapsed, false otherwise. 400 */ 401 public static boolean collapseActionView(MenuItem item) { 402 if (item instanceof SupportMenuItem) { 403 return ((SupportMenuItem) item).collapseActionView(); 404 } 405 return IMPL.collapseActionView(item); 406 } 407 408 /** 409 * Returns true if this menu item's action view has been expanded. 410 * 411 * @return true if the item's action view is expanded, false otherwise. 412 * @see #expandActionView(MenuItem) 413 * @see #collapseActionView(MenuItem) 414 * @see #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW 415 * @see android.support.v4.view.MenuItemCompat.OnActionExpandListener 416 */ 417 public static boolean isActionViewExpanded(MenuItem item) { 418 if (item instanceof SupportMenuItem) { 419 return ((SupportMenuItem) item).isActionViewExpanded(); 420 } 421 return IMPL.isActionViewExpanded(item); 422 } 423 424 /** 425 * Set an {@link OnActionExpandListener} on this menu 426 * item to be notified when the associated action view is expanded or collapsed. 427 * The menu item must be configured to expand or collapse its action view using the flag 428 * {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. 429 * 430 * @param listener Listener that will respond to expand/collapse events 431 * @return This menu item instance for call chaining 432 */ 433 public static MenuItem setOnActionExpandListener(MenuItem item, 434 OnActionExpandListener listener) { 435 if (item instanceof SupportMenuItem) { 436 return ((SupportMenuItem) item).setSupportOnActionExpandListener(listener); 437 } 438 return IMPL.setOnActionExpandListener(item, listener); 439 } 440 441 private MenuItemCompat() {} 442 } 443