ActionMenuView.java revision 04c0d4607897157f01c908475fa0c6d7301cf7e5
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 */ 16package android.widget; 17 18import android.content.Context; 19import android.content.res.Configuration; 20import android.util.AttributeSet; 21import android.view.ContextThemeWrapper; 22import android.view.Gravity; 23import android.view.Menu; 24import android.view.MenuItem; 25import android.view.View; 26import android.view.ViewDebug; 27import android.view.ViewGroup; 28import android.view.accessibility.AccessibilityEvent; 29import com.android.internal.view.menu.ActionMenuItemView; 30import com.android.internal.view.menu.MenuBuilder; 31import com.android.internal.view.menu.MenuItemImpl; 32import com.android.internal.view.menu.MenuPresenter; 33import com.android.internal.view.menu.MenuView; 34 35/** 36 * ActionMenuView is a presentation of a series of menu options as a View. It provides 37 * several top level options as action buttons while spilling remaining options over as 38 * items in an overflow menu. This allows applications to present packs of actions inline with 39 * specific or repeating content. 40 */ 41public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvoker, MenuView { 42 private static final String TAG = "ActionMenuView"; 43 44 static final int MIN_CELL_SIZE = 56; // dips 45 static final int GENERATED_ITEM_PADDING = 4; // dips 46 47 private MenuBuilder mMenu; 48 49 /** Context against which to inflate popup menus. */ 50 private Context mPopupContext; 51 52 /** Theme resource against which to inflate popup menus. */ 53 private int mPopupTheme; 54 55 private boolean mReserveOverflow; 56 private ActionMenuPresenter mPresenter; 57 private MenuPresenter.Callback mActionMenuPresenterCallback; 58 private boolean mFormatItems; 59 private int mFormatItemsWidth; 60 private int mMinCellSize; 61 private int mGeneratedItemPadding; 62 63 private OnMenuItemClickListener mOnMenuItemClickListener; 64 65 public ActionMenuView(Context context) { 66 this(context, null); 67 } 68 69 public ActionMenuView(Context context, AttributeSet attrs) { 70 super(context, attrs); 71 setBaselineAligned(false); 72 final float density = context.getResources().getDisplayMetrics().density; 73 mMinCellSize = (int) (MIN_CELL_SIZE * density); 74 mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density); 75 mPopupContext = context; 76 mPopupTheme = 0; 77 } 78 79 /** 80 * Specifies the theme to use when inflating popup menus. By default, uses 81 * the same theme as the action menu view itself. 82 * 83 * @param resId theme used to inflate popup menus 84 * @see #getPopupTheme() 85 */ 86 public void setPopupTheme(int resId) { 87 if (mPopupTheme != resId) { 88 mPopupTheme = resId; 89 if (resId == 0) { 90 mPopupContext = mContext; 91 } else { 92 mPopupContext = new ContextThemeWrapper(mContext, resId); 93 } 94 } 95 } 96 97 /** 98 * @return resource identifier of the theme used to inflate popup menus, or 99 * 0 if menus are inflated against the action menu view theme 100 * @see #setPopupTheme(int) 101 */ 102 public int getPopupTheme() { 103 return mPopupTheme; 104 } 105 106 /** 107 * @param presenter Menu presenter used to display popup menu 108 * @hide 109 */ 110 public void setPresenter(ActionMenuPresenter presenter) { 111 mPresenter = presenter; 112 mPresenter.setMenuView(this); 113 } 114 115 @Override 116 public void onConfigurationChanged(Configuration newConfig) { 117 super.onConfigurationChanged(newConfig); 118 mPresenter.updateMenuView(false); 119 120 if (mPresenter != null && mPresenter.isOverflowMenuShowing()) { 121 mPresenter.hideOverflowMenu(); 122 mPresenter.showOverflowMenu(); 123 } 124 } 125 126 public void setOnMenuItemClickListener(OnMenuItemClickListener listener) { 127 mOnMenuItemClickListener = listener; 128 } 129 130 @Override 131 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 132 // If we've been given an exact size to match, apply special formatting during layout. 133 final boolean wasFormatted = mFormatItems; 134 mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY; 135 136 if (wasFormatted != mFormatItems) { 137 mFormatItemsWidth = 0; // Reset this when switching modes 138 } 139 140 // Special formatting can change whether items can fit as action buttons. 141 // Kick the menu and update presenters when this changes. 142 final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 143 if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) { 144 mFormatItemsWidth = widthSize; 145 mMenu.onItemsChanged(true); 146 } 147 148 final int childCount = getChildCount(); 149 if (mFormatItems && childCount > 0) { 150 onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec); 151 } else { 152 // Previous measurement at exact format may have set margins - reset them. 153 for (int i = 0; i < childCount; i++) { 154 final View child = getChildAt(i); 155 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 156 lp.leftMargin = lp.rightMargin = 0; 157 } 158 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 159 } 160 } 161 162 private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) { 163 // We already know the width mode is EXACTLY if we're here. 164 final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 165 int widthSize = MeasureSpec.getSize(widthMeasureSpec); 166 int heightSize = MeasureSpec.getSize(heightMeasureSpec); 167 168 final int widthPadding = getPaddingLeft() + getPaddingRight(); 169 final int heightPadding = getPaddingTop() + getPaddingBottom(); 170 171 final int itemHeightSpec = getChildMeasureSpec(heightMeasureSpec, heightPadding, 172 ViewGroup.LayoutParams.WRAP_CONTENT); 173 174 widthSize -= widthPadding; 175 176 // Divide the view into cells. 177 final int cellCount = widthSize / mMinCellSize; 178 final int cellSizeRemaining = widthSize % mMinCellSize; 179 180 if (cellCount == 0) { 181 // Give up, nothing fits. 182 setMeasuredDimension(widthSize, 0); 183 return; 184 } 185 186 final int cellSize = mMinCellSize + cellSizeRemaining / cellCount; 187 188 int cellsRemaining = cellCount; 189 int maxChildHeight = 0; 190 int maxCellsUsed = 0; 191 int expandableItemCount = 0; 192 int visibleItemCount = 0; 193 boolean hasOverflow = false; 194 195 // This is used as a bitfield to locate the smallest items present. Assumes childCount < 64. 196 long smallestItemsAt = 0; 197 198 final int childCount = getChildCount(); 199 for (int i = 0; i < childCount; i++) { 200 final View child = getChildAt(i); 201 if (child.getVisibility() == GONE) continue; 202 203 final boolean isGeneratedItem = child instanceof ActionMenuItemView; 204 visibleItemCount++; 205 206 if (isGeneratedItem) { 207 // Reset padding for generated menu item views; it may change below 208 // and views are recycled. 209 child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0); 210 } 211 212 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 213 lp.expanded = false; 214 lp.extraPixels = 0; 215 lp.cellsUsed = 0; 216 lp.expandable = false; 217 lp.leftMargin = 0; 218 lp.rightMargin = 0; 219 lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText(); 220 221 // Overflow always gets 1 cell. No more, no less. 222 final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining; 223 224 final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable, 225 itemHeightSpec, heightPadding); 226 227 maxCellsUsed = Math.max(maxCellsUsed, cellsUsed); 228 if (lp.expandable) expandableItemCount++; 229 if (lp.isOverflowButton) hasOverflow = true; 230 231 cellsRemaining -= cellsUsed; 232 maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight()); 233 if (cellsUsed == 1) smallestItemsAt |= (1 << i); 234 } 235 236 // When we have overflow and a single expanded (text) item, we want to try centering it 237 // visually in the available space even though overflow consumes some of it. 238 final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2; 239 240 // Divide space for remaining cells if we have items that can expand. 241 // Try distributing whole leftover cells to smaller items first. 242 243 boolean needsExpansion = false; 244 while (expandableItemCount > 0 && cellsRemaining > 0) { 245 int minCells = Integer.MAX_VALUE; 246 long minCellsAt = 0; // Bit locations are indices of relevant child views 247 int minCellsItemCount = 0; 248 for (int i = 0; i < childCount; i++) { 249 final View child = getChildAt(i); 250 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 251 252 // Don't try to expand items that shouldn't. 253 if (!lp.expandable) continue; 254 255 // Mark indices of children that can receive an extra cell. 256 if (lp.cellsUsed < minCells) { 257 minCells = lp.cellsUsed; 258 minCellsAt = 1 << i; 259 minCellsItemCount = 1; 260 } else if (lp.cellsUsed == minCells) { 261 minCellsAt |= 1 << i; 262 minCellsItemCount++; 263 } 264 } 265 266 // Items that get expanded will always be in the set of smallest items when we're done. 267 smallestItemsAt |= minCellsAt; 268 269 if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop. 270 271 // We have enough cells, all minimum size items will be incremented. 272 minCells++; 273 274 for (int i = 0; i < childCount; i++) { 275 final View child = getChildAt(i); 276 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 277 if ((minCellsAt & (1 << i)) == 0) { 278 // If this item is already at our small item count, mark it for later. 279 if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i; 280 continue; 281 } 282 283 if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) { 284 // Add padding to this item such that it centers. 285 child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0); 286 } 287 lp.cellsUsed++; 288 lp.expanded = true; 289 cellsRemaining--; 290 } 291 292 needsExpansion = true; 293 } 294 295 // Divide any space left that wouldn't divide along cell boundaries 296 // evenly among the smallest items 297 298 final boolean singleItem = !hasOverflow && visibleItemCount == 1; 299 if (cellsRemaining > 0 && smallestItemsAt != 0 && 300 (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) { 301 float expandCount = Long.bitCount(smallestItemsAt); 302 303 if (!singleItem) { 304 // The items at the far edges may only expand by half in order to pin to either side. 305 if ((smallestItemsAt & 1) != 0) { 306 LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams(); 307 if (!lp.preventEdgeOffset) expandCount -= 0.5f; 308 } 309 if ((smallestItemsAt & (1 << (childCount - 1))) != 0) { 310 LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams()); 311 if (!lp.preventEdgeOffset) expandCount -= 0.5f; 312 } 313 } 314 315 final int extraPixels = expandCount > 0 ? 316 (int) (cellsRemaining * cellSize / expandCount) : 0; 317 318 for (int i = 0; i < childCount; i++) { 319 if ((smallestItemsAt & (1 << i)) == 0) continue; 320 321 final View child = getChildAt(i); 322 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 323 if (child instanceof ActionMenuItemView) { 324 // If this is one of our views, expand and measure at the larger size. 325 lp.extraPixels = extraPixels; 326 lp.expanded = true; 327 if (i == 0 && !lp.preventEdgeOffset) { 328 // First item gets part of its new padding pushed out of sight. 329 // The last item will get this implicitly from layout. 330 lp.leftMargin = -extraPixels / 2; 331 } 332 needsExpansion = true; 333 } else if (lp.isOverflowButton) { 334 lp.extraPixels = extraPixels; 335 lp.expanded = true; 336 lp.rightMargin = -extraPixels / 2; 337 needsExpansion = true; 338 } else { 339 // If we don't know what it is, give it some margins instead 340 // and let it center within its space. We still want to pin 341 // against the edges. 342 if (i != 0) { 343 lp.leftMargin = extraPixels / 2; 344 } 345 if (i != childCount - 1) { 346 lp.rightMargin = extraPixels / 2; 347 } 348 } 349 } 350 351 cellsRemaining = 0; 352 } 353 354 // Remeasure any items that have had extra space allocated to them. 355 if (needsExpansion) { 356 for (int i = 0; i < childCount; i++) { 357 final View child = getChildAt(i); 358 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 359 360 if (!lp.expanded) continue; 361 362 final int width = lp.cellsUsed * cellSize + lp.extraPixels; 363 child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), 364 itemHeightSpec); 365 } 366 } 367 368 if (heightMode != MeasureSpec.EXACTLY) { 369 heightSize = maxChildHeight; 370 } 371 372 setMeasuredDimension(widthSize, heightSize); 373 } 374 375 /** 376 * Measure a child view to fit within cell-based formatting. The child's width 377 * will be measured to a whole multiple of cellSize. 378 * 379 * <p>Sets the expandable and cellsUsed fields of LayoutParams. 380 * 381 * @param child Child to measure 382 * @param cellSize Size of one cell 383 * @param cellsRemaining Number of cells remaining that this view can expand to fill 384 * @param parentHeightMeasureSpec MeasureSpec used by the parent view 385 * @param parentHeightPadding Padding present in the parent view 386 * @return Number of cells this child was measured to occupy 387 */ 388 static int measureChildForCells(View child, int cellSize, int cellsRemaining, 389 int parentHeightMeasureSpec, int parentHeightPadding) { 390 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 391 392 final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) - 393 parentHeightPadding; 394 final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec); 395 final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode); 396 397 final ActionMenuItemView itemView = child instanceof ActionMenuItemView ? 398 (ActionMenuItemView) child : null; 399 final boolean hasText = itemView != null && itemView.hasText(); 400 401 int cellsUsed = 0; 402 if (cellsRemaining > 0 && (!hasText || cellsRemaining >= 2)) { 403 final int childWidthSpec = MeasureSpec.makeMeasureSpec( 404 cellSize * cellsRemaining, MeasureSpec.AT_MOST); 405 child.measure(childWidthSpec, childHeightSpec); 406 407 final int measuredWidth = child.getMeasuredWidth(); 408 cellsUsed = measuredWidth / cellSize; 409 if (measuredWidth % cellSize != 0) cellsUsed++; 410 if (hasText && cellsUsed < 2) cellsUsed = 2; 411 } 412 413 final boolean expandable = !lp.isOverflowButton && hasText; 414 lp.expandable = expandable; 415 416 lp.cellsUsed = cellsUsed; 417 final int targetWidth = cellsUsed * cellSize; 418 child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY), 419 childHeightSpec); 420 return cellsUsed; 421 } 422 423 @Override 424 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 425 if (!mFormatItems) { 426 super.onLayout(changed, left, top, right, bottom); 427 return; 428 } 429 430 final int childCount = getChildCount(); 431 final int midVertical = (top + bottom) / 2; 432 final int dividerWidth = getDividerWidth(); 433 int overflowWidth = 0; 434 int nonOverflowWidth = 0; 435 int nonOverflowCount = 0; 436 int widthRemaining = right - left - getPaddingRight() - getPaddingLeft(); 437 boolean hasOverflow = false; 438 final boolean isLayoutRtl = isLayoutRtl(); 439 for (int i = 0; i < childCount; i++) { 440 final View v = getChildAt(i); 441 if (v.getVisibility() == GONE) { 442 continue; 443 } 444 445 LayoutParams p = (LayoutParams) v.getLayoutParams(); 446 if (p.isOverflowButton) { 447 overflowWidth = v.getMeasuredWidth(); 448 if (hasDividerBeforeChildAt(i)) { 449 overflowWidth += dividerWidth; 450 } 451 452 int height = v.getMeasuredHeight(); 453 int r; 454 int l; 455 if (isLayoutRtl) { 456 l = getPaddingLeft() + p.leftMargin; 457 r = l + overflowWidth; 458 } else { 459 r = getWidth() - getPaddingRight() - p.rightMargin; 460 l = r - overflowWidth; 461 } 462 int t = midVertical - (height / 2); 463 int b = t + height; 464 v.layout(l, t, r, b); 465 466 widthRemaining -= overflowWidth; 467 hasOverflow = true; 468 } else { 469 final int size = v.getMeasuredWidth() + p.leftMargin + p.rightMargin; 470 nonOverflowWidth += size; 471 widthRemaining -= size; 472 if (hasDividerBeforeChildAt(i)) { 473 nonOverflowWidth += dividerWidth; 474 } 475 nonOverflowCount++; 476 } 477 } 478 479 if (childCount == 1 && !hasOverflow) { 480 // Center a single child 481 final View v = getChildAt(0); 482 final int width = v.getMeasuredWidth(); 483 final int height = v.getMeasuredHeight(); 484 final int midHorizontal = (right - left) / 2; 485 final int l = midHorizontal - width / 2; 486 final int t = midVertical - height / 2; 487 v.layout(l, t, l + width, t + height); 488 return; 489 } 490 491 final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1); 492 final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0); 493 494 if (isLayoutRtl) { 495 int startRight = getWidth() - getPaddingRight(); 496 for (int i = 0; i < childCount; i++) { 497 final View v = getChildAt(i); 498 final LayoutParams lp = (LayoutParams) v.getLayoutParams(); 499 if (v.getVisibility() == GONE || lp.isOverflowButton) { 500 continue; 501 } 502 503 startRight -= lp.rightMargin; 504 int width = v.getMeasuredWidth(); 505 int height = v.getMeasuredHeight(); 506 int t = midVertical - height / 2; 507 v.layout(startRight - width, t, startRight, t + height); 508 startRight -= width + lp.leftMargin + spacerSize; 509 } 510 } else { 511 int startLeft = getPaddingLeft(); 512 for (int i = 0; i < childCount; i++) { 513 final View v = getChildAt(i); 514 final LayoutParams lp = (LayoutParams) v.getLayoutParams(); 515 if (v.getVisibility() == GONE || lp.isOverflowButton) { 516 continue; 517 } 518 519 startLeft += lp.leftMargin; 520 int width = v.getMeasuredWidth(); 521 int height = v.getMeasuredHeight(); 522 int t = midVertical - height / 2; 523 v.layout(startLeft, t, startLeft + width, t + height); 524 startLeft += width + lp.rightMargin + spacerSize; 525 } 526 } 527 } 528 529 @Override 530 public void onDetachedFromWindow() { 531 super.onDetachedFromWindow(); 532 dismissPopupMenus(); 533 } 534 535 /** @hide */ 536 public boolean isOverflowReserved() { 537 return mReserveOverflow; 538 } 539 540 /** @hide */ 541 public void setOverflowReserved(boolean reserveOverflow) { 542 mReserveOverflow = reserveOverflow; 543 } 544 545 @Override 546 protected LayoutParams generateDefaultLayoutParams() { 547 LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, 548 LayoutParams.WRAP_CONTENT); 549 params.gravity = Gravity.CENTER_VERTICAL; 550 return params; 551 } 552 553 @Override 554 public LayoutParams generateLayoutParams(AttributeSet attrs) { 555 return new LayoutParams(getContext(), attrs); 556 } 557 558 @Override 559 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 560 if (p != null) { 561 final LayoutParams result = p instanceof LayoutParams 562 ? new LayoutParams((LayoutParams) p) 563 : new LayoutParams(p); 564 if (result.gravity <= Gravity.NO_GRAVITY) { 565 result.gravity = Gravity.CENTER_VERTICAL; 566 } 567 return result; 568 } 569 return generateDefaultLayoutParams(); 570 } 571 572 @Override 573 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 574 return p != null && p instanceof LayoutParams; 575 } 576 577 /** @hide */ 578 public LayoutParams generateOverflowButtonLayoutParams() { 579 LayoutParams result = generateDefaultLayoutParams(); 580 result.isOverflowButton = true; 581 return result; 582 } 583 584 /** @hide */ 585 public boolean invokeItem(MenuItemImpl item) { 586 return mMenu.performItemAction(item, 0); 587 } 588 589 /** @hide */ 590 public int getWindowAnimations() { 591 return 0; 592 } 593 594 /** @hide */ 595 public void initialize(MenuBuilder menu) { 596 mMenu = menu; 597 } 598 599 /** 600 * Returns the Menu object that this ActionMenuView is currently presenting. 601 * 602 * <p>Applications should use this method to obtain the ActionMenuView's Menu object 603 * and inflate or add content to it as necessary.</p> 604 * 605 * @return the Menu presented by this view 606 */ 607 public Menu getMenu() { 608 if (mMenu == null) { 609 final Context context = getContext(); 610 mMenu = new MenuBuilder(context); 611 mMenu.setCallback(new MenuBuilderCallback()); 612 mPresenter = new ActionMenuPresenter(context); 613 mPresenter.setCallback(mActionMenuPresenterCallback != null 614 ? mActionMenuPresenterCallback : new ActionMenuPresenterCallback()); 615 mMenu.addMenuPresenter(mPresenter, mPopupContext); 616 mPresenter.setMenuView(this); 617 } 618 619 return mMenu; 620 } 621 622 /** 623 * Must be called before the first call to getMenu() 624 * @hide 625 */ 626 public void setActionMenuPresenterCallback(MenuPresenter.Callback cb) { 627 mActionMenuPresenterCallback = cb; 628 } 629 630 /** 631 * Returns the current menu or null if one has not yet been configured. 632 * @hide Internal use only for action bar integration 633 */ 634 public MenuBuilder peekMenu() { 635 return mMenu; 636 } 637 638 /** 639 * Show the overflow items from the associated menu. 640 * 641 * @return true if the menu was able to be shown, false otherwise 642 */ 643 public boolean showOverflowMenu() { 644 return mPresenter != null && mPresenter.showOverflowMenu(); 645 } 646 647 /** 648 * Hide the overflow items from the associated menu. 649 * 650 * @return true if the menu was able to be hidden, false otherwise 651 */ 652 public boolean hideOverflowMenu() { 653 return mPresenter != null && mPresenter.hideOverflowMenu(); 654 } 655 656 /** 657 * Check whether the overflow menu is currently showing. This may not reflect 658 * a pending show operation in progress. 659 * 660 * @return true if the overflow menu is currently showing 661 */ 662 public boolean isOverflowMenuShowing() { 663 return mPresenter != null && mPresenter.isOverflowMenuShowing(); 664 } 665 666 /** @hide */ 667 public boolean isOverflowMenuShowPending() { 668 return mPresenter != null && mPresenter.isOverflowMenuShowPending(); 669 } 670 671 /** 672 * Dismiss any popups associated with this menu view. 673 */ 674 public void dismissPopupMenus() { 675 if (mPresenter != null) { 676 mPresenter.dismissPopupMenus(); 677 } 678 } 679 680 /** 681 * @hide Private LinearLayout (superclass) API. Un-hide if LinearLayout API is made public. 682 */ 683 @Override 684 protected boolean hasDividerBeforeChildAt(int childIndex) { 685 if (childIndex == 0) { 686 return false; 687 } 688 final View childBefore = getChildAt(childIndex - 1); 689 final View child = getChildAt(childIndex); 690 boolean result = false; 691 if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) { 692 result |= ((ActionMenuChildView) childBefore).needsDividerAfter(); 693 } 694 if (childIndex > 0 && child instanceof ActionMenuChildView) { 695 result |= ((ActionMenuChildView) child).needsDividerBefore(); 696 } 697 return result; 698 } 699 700 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 701 return false; 702 } 703 704 /** @hide */ 705 public void setExpandedActionViewsExclusive(boolean exclusive) { 706 mPresenter.setExpandedActionViewsExclusive(exclusive); 707 } 708 709 /** 710 * Interface responsible for receiving menu item click events if the items themselves 711 * do not have individual item click listeners. 712 */ 713 public interface OnMenuItemClickListener { 714 /** 715 * This method will be invoked when a menu item is clicked if the item itself did 716 * not already handle the event. 717 * 718 * @param item {@link MenuItem} that was clicked 719 * @return <code>true</code> if the event was handled, <code>false</code> otherwise. 720 */ 721 public boolean onMenuItemClick(MenuItem item); 722 } 723 724 private class MenuBuilderCallback implements MenuBuilder.Callback { 725 @Override 726 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 727 return mOnMenuItemClickListener != null && 728 mOnMenuItemClickListener.onMenuItemClick(item); 729 } 730 731 @Override 732 public void onMenuModeChange(MenuBuilder menu) { 733 } 734 } 735 736 private class ActionMenuPresenterCallback implements ActionMenuPresenter.Callback { 737 @Override 738 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 739 } 740 741 @Override 742 public boolean onOpenSubMenu(MenuBuilder subMenu) { 743 return false; 744 } 745 } 746 747 /** @hide */ 748 public interface ActionMenuChildView { 749 public boolean needsDividerBefore(); 750 public boolean needsDividerAfter(); 751 } 752 753 public static class LayoutParams extends LinearLayout.LayoutParams { 754 /** @hide */ 755 @ViewDebug.ExportedProperty(category = "layout") 756 public boolean isOverflowButton; 757 758 /** @hide */ 759 @ViewDebug.ExportedProperty(category = "layout") 760 public int cellsUsed; 761 762 /** @hide */ 763 @ViewDebug.ExportedProperty(category = "layout") 764 public int extraPixels; 765 766 /** @hide */ 767 @ViewDebug.ExportedProperty(category = "layout") 768 public boolean expandable; 769 770 /** @hide */ 771 @ViewDebug.ExportedProperty(category = "layout") 772 public boolean preventEdgeOffset; 773 774 /** @hide */ 775 public boolean expanded; 776 777 public LayoutParams(Context c, AttributeSet attrs) { 778 super(c, attrs); 779 } 780 781 public LayoutParams(ViewGroup.LayoutParams other) { 782 super(other); 783 } 784 785 public LayoutParams(LayoutParams other) { 786 super((LinearLayout.LayoutParams) other); 787 isOverflowButton = other.isOverflowButton; 788 } 789 790 public LayoutParams(int width, int height) { 791 super(width, height); 792 isOverflowButton = false; 793 } 794 795 /** @hide */ 796 public LayoutParams(int width, int height, boolean isOverflowButton) { 797 super(width, height); 798 this.isOverflowButton = isOverflowButton; 799 } 800 } 801} 802