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