Toolbar.java revision 85085343b378f00a34448b69e582226fa584b005
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.widget; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.app.ActionBar; 22import android.content.Context; 23import android.content.res.TypedArray; 24import android.graphics.drawable.Drawable; 25import android.os.Parcel; 26import android.os.Parcelable; 27import android.text.Layout; 28import android.text.TextUtils; 29import android.util.AttributeSet; 30import android.view.CollapsibleActionView; 31import android.view.ContextThemeWrapper; 32import android.view.Gravity; 33import android.view.Menu; 34import android.view.MenuInflater; 35import android.view.MenuItem; 36import android.view.MotionEvent; 37import android.view.View; 38import android.view.ViewGroup; 39 40import com.android.internal.R; 41import com.android.internal.view.menu.MenuBuilder; 42import com.android.internal.view.menu.MenuItemImpl; 43import com.android.internal.view.menu.MenuPresenter; 44import com.android.internal.view.menu.MenuView; 45import com.android.internal.view.menu.SubMenuBuilder; 46import com.android.internal.widget.DecorToolbar; 47import com.android.internal.widget.ToolbarWidgetWrapper; 48 49import java.util.ArrayList; 50import java.util.List; 51 52/** 53 * A standard toolbar for use within application content. 54 * 55 * <p>A Toolbar is a generalization of {@link android.app.ActionBar action bars} for use 56 * within application layouts. While an action bar is traditionally part of an 57 * {@link android.app.Activity Activity's} opaque window decor controlled by the framework, 58 * a Toolbar may be placed at any arbitrary level of nesting within a view hierarchy. 59 * An application may choose to designate a Toolbar as the action bar for an Activity 60 * using the {@link android.app.Activity#setActionBar(Toolbar) setActionBar()} method.</p> 61 * 62 * <p>Toolbar supports a more focused feature set than ActionBar. From start to end, a toolbar 63 * may contain a combination of the following optional elements: 64 * 65 * <ul> 66 * <li><em>A navigation button.</em> This may be an Up arrow, navigation menu toggle, close, 67 * collapse, done or another glyph of the app's choosing. This button should always be used 68 * to access other navigational destinations within the container of the Toolbar and 69 * its signified content or otherwise leave the current context signified by the Toolbar. 70 * The navigation button is vertically aligned within the Toolbar's 71 * {@link android.R.styleable#View_minHeight minimum height}, if set.</li> 72 * <li><em>A branded logo image.</em> This may extend to the height of the bar and can be 73 * arbitrarily wide.</li> 74 * <li><em>A title and subtitle.</em> The title should be a signpost for the Toolbar's current 75 * position in the navigation hierarchy and the content contained there. The subtitle, 76 * if present should indicate any extended information about the current content. 77 * If an app uses a logo image it should strongly consider omitting a title and subtitle.</li> 78 * <li><em>One or more custom views.</em> The application may add arbitrary child views 79 * to the Toolbar. They will appear at this position within the layout. If a child view's 80 * {@link LayoutParams} indicates a {@link Gravity} value of 81 * {@link Gravity#CENTER_HORIZONTAL CENTER_HORIZONTAL} the view will attempt to center 82 * within the available space remaining in the Toolbar after all other elements have been 83 * measured.</li> 84 * <li><em>An {@link ActionMenuView action menu}.</em> The menu of actions will pin to the 85 * end of the Toolbar offering a few 86 * <a href="http://developer.android.com/design/patterns/actionbar.html#ActionButtons"> 87 * frequent, important or typical</a> actions along with an optional overflow menu for 88 * additional actions. Action buttons are vertically aligned within the Toolbar's 89 * {@link android.R.styleable#View_minHeight minimum height}, if set.</li> 90 * </ul> 91 * </p> 92 * 93 * <p>In modern Android UIs developers should lean more on a visually distinct color scheme for 94 * toolbars than on their application icon. The use of application icon plus title as a standard 95 * layout is discouraged on API 21 devices and newer.</p> 96 */ 97public class Toolbar extends ViewGroup { 98 private static final String TAG = "Toolbar"; 99 100 private ActionMenuView mMenuView; 101 private TextView mTitleTextView; 102 private TextView mSubtitleTextView; 103 private ImageButton mNavButtonView; 104 private ImageView mLogoView; 105 106 private Drawable mCollapseIcon; 107 private CharSequence mCollapseDescription; 108 private ImageButton mCollapseButtonView; 109 View mExpandedActionView; 110 111 /** Context against which to inflate popup menus. */ 112 private Context mPopupContext; 113 114 /** Theme resource against which to inflate popup menus. */ 115 private int mPopupTheme; 116 117 private int mTitleTextAppearance; 118 private int mSubtitleTextAppearance; 119 private int mNavButtonStyle; 120 121 private int mButtonGravity; 122 123 private int mMaxButtonHeight; 124 125 private int mTitleMarginStart; 126 private int mTitleMarginEnd; 127 private int mTitleMarginTop; 128 private int mTitleMarginBottom; 129 130 private final RtlSpacingHelper mContentInsets = new RtlSpacingHelper(); 131 132 private int mGravity = Gravity.START | Gravity.CENTER_VERTICAL; 133 134 private CharSequence mTitleText; 135 private CharSequence mSubtitleText; 136 137 private int mTitleTextColor; 138 private int mSubtitleTextColor; 139 140 private boolean mEatingTouch; 141 142 // Clear me after use. 143 private final ArrayList<View> mTempViews = new ArrayList<View>(); 144 145 private final int[] mTempMargins = new int[2]; 146 147 private OnMenuItemClickListener mOnMenuItemClickListener; 148 149 private final ActionMenuView.OnMenuItemClickListener mMenuViewItemClickListener = 150 new ActionMenuView.OnMenuItemClickListener() { 151 @Override 152 public boolean onMenuItemClick(MenuItem item) { 153 if (mOnMenuItemClickListener != null) { 154 return mOnMenuItemClickListener.onMenuItemClick(item); 155 } 156 return false; 157 } 158 }; 159 160 private ToolbarWidgetWrapper mWrapper; 161 private ActionMenuPresenter mOuterActionMenuPresenter; 162 private ExpandedActionViewMenuPresenter mExpandedMenuPresenter; 163 private MenuPresenter.Callback mActionMenuPresenterCallback; 164 private MenuBuilder.Callback mMenuBuilderCallback; 165 166 private boolean mCollapsible; 167 168 private final Runnable mShowOverflowMenuRunnable = new Runnable() { 169 @Override public void run() { 170 showOverflowMenu(); 171 } 172 }; 173 174 public Toolbar(Context context) { 175 this(context, null); 176 } 177 178 public Toolbar(Context context, AttributeSet attrs) { 179 this(context, attrs, com.android.internal.R.attr.toolbarStyle); 180 } 181 182 public Toolbar(Context context, AttributeSet attrs, int defStyleAttr) { 183 this(context, attrs, defStyleAttr, 0); 184 } 185 186 public Toolbar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 187 super(context, attrs, defStyleAttr, defStyleRes); 188 189 final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Toolbar, 190 defStyleAttr, defStyleRes); 191 192 mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0); 193 mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0); 194 mNavButtonStyle = a.getResourceId(R.styleable.Toolbar_navigationButtonStyle, 0); 195 mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity); 196 mButtonGravity = a.getInteger(R.styleable.Toolbar_buttonGravity, Gravity.TOP); 197 mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom = 198 a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, 0); 199 200 final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1); 201 if (marginStart >= 0) { 202 mTitleMarginStart = marginStart; 203 } 204 205 final int marginEnd = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginEnd, -1); 206 if (marginEnd >= 0) { 207 mTitleMarginEnd = marginEnd; 208 } 209 210 final int marginTop = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginTop, -1); 211 if (marginTop >= 0) { 212 mTitleMarginTop = marginTop; 213 } 214 215 final int marginBottom = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginBottom, 216 -1); 217 if (marginBottom >= 0) { 218 mTitleMarginBottom = marginBottom; 219 } 220 221 mMaxButtonHeight = a.getDimensionPixelSize(R.styleable.Toolbar_maxButtonHeight, -1); 222 223 final int contentInsetStart = 224 a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetStart, 225 RtlSpacingHelper.UNDEFINED); 226 final int contentInsetEnd = 227 a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetEnd, 228 RtlSpacingHelper.UNDEFINED); 229 final int contentInsetLeft = 230 a.getDimensionPixelSize(R.styleable.Toolbar_contentInsetLeft, 0); 231 final int contentInsetRight = 232 a.getDimensionPixelSize(R.styleable.Toolbar_contentInsetRight, 0); 233 234 mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight); 235 236 if (contentInsetStart != RtlSpacingHelper.UNDEFINED || 237 contentInsetEnd != RtlSpacingHelper.UNDEFINED) { 238 mContentInsets.setRelative(contentInsetStart, contentInsetEnd); 239 } 240 241 mCollapseIcon = a.getDrawable(R.styleable.Toolbar_collapseIcon); 242 mCollapseDescription = a.getText(R.styleable.Toolbar_collapseContentDescription); 243 244 final CharSequence title = a.getText(R.styleable.Toolbar_title); 245 if (!TextUtils.isEmpty(title)) { 246 setTitle(title); 247 } 248 249 final CharSequence subtitle = a.getText(R.styleable.Toolbar_subtitle); 250 if (!TextUtils.isEmpty(subtitle)) { 251 setSubtitle(subtitle); 252 } 253 254 // Set the default context, since setPopupTheme() may be a no-op. 255 mPopupContext = mContext; 256 setPopupTheme(a.getResourceId(R.styleable.Toolbar_popupTheme, 0)); 257 258 final Drawable navIcon = a.getDrawable(R.styleable.Toolbar_navigationIcon); 259 if (navIcon != null) { 260 setNavigationIcon(navIcon); 261 } 262 263 final CharSequence navDesc = a.getText( 264 R.styleable.Toolbar_navigationContentDescription); 265 if (!TextUtils.isEmpty(navDesc)) { 266 setNavigationContentDescription(navDesc); 267 } 268 a.recycle(); 269 } 270 271 /** 272 * Specifies the theme to use when inflating popup menus. By default, uses 273 * the same theme as the toolbar itself. 274 * 275 * @param resId theme used to inflate popup menus 276 * @see #getPopupTheme() 277 */ 278 public void setPopupTheme(int resId) { 279 if (mPopupTheme != resId) { 280 mPopupTheme = resId; 281 if (resId == 0) { 282 mPopupContext = mContext; 283 } else { 284 mPopupContext = new ContextThemeWrapper(mContext, resId); 285 } 286 } 287 } 288 289 /** 290 * @return resource identifier of the theme used to inflate popup menus, or 291 * 0 if menus are inflated against the toolbar theme 292 * @see #setPopupTheme(int) 293 */ 294 public int getPopupTheme() { 295 return mPopupTheme; 296 } 297 298 @Override 299 public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) { 300 super.onRtlPropertiesChanged(layoutDirection); 301 mContentInsets.setDirection(layoutDirection == LAYOUT_DIRECTION_RTL); 302 } 303 304 /** 305 * Set a logo drawable from a resource id. 306 * 307 * <p>This drawable should generally take the place of title text. The logo cannot be 308 * clicked. Apps using a logo should also supply a description using 309 * {@link #setLogoDescription(int)}.</p> 310 * 311 * @param resId ID of a drawable resource 312 */ 313 public void setLogo(int resId) { 314 setLogo(getContext().getDrawable(resId)); 315 } 316 317 /** @hide */ 318 public boolean canShowOverflowMenu() { 319 return getVisibility() == VISIBLE && mMenuView != null && mMenuView.isOverflowReserved(); 320 } 321 322 /** 323 * Check whether the overflow menu is currently showing. This may not reflect 324 * a pending show operation in progress. 325 * 326 * @return true if the overflow menu is currently showing 327 */ 328 public boolean isOverflowMenuShowing() { 329 return mMenuView != null && mMenuView.isOverflowMenuShowing(); 330 } 331 332 /** @hide */ 333 public boolean isOverflowMenuShowPending() { 334 return mMenuView != null && mMenuView.isOverflowMenuShowPending(); 335 } 336 337 /** 338 * Show the overflow items from the associated menu. 339 * 340 * @return true if the menu was able to be shown, false otherwise 341 */ 342 public boolean showOverflowMenu() { 343 return mMenuView != null && mMenuView.showOverflowMenu(); 344 } 345 346 /** 347 * Hide the overflow items from the associated menu. 348 * 349 * @return true if the menu was able to be hidden, false otherwise 350 */ 351 public boolean hideOverflowMenu() { 352 return mMenuView != null && mMenuView.hideOverflowMenu(); 353 } 354 355 /** @hide */ 356 public void setMenu(MenuBuilder menu, ActionMenuPresenter outerPresenter) { 357 if (menu == null && mMenuView == null) { 358 return; 359 } 360 361 ensureMenuView(); 362 final MenuBuilder oldMenu = mMenuView.peekMenu(); 363 if (oldMenu == menu) { 364 return; 365 } 366 367 if (oldMenu != null) { 368 oldMenu.removeMenuPresenter(mOuterActionMenuPresenter); 369 oldMenu.removeMenuPresenter(mExpandedMenuPresenter); 370 } 371 372 if (mExpandedMenuPresenter == null) { 373 mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter(); 374 } 375 376 outerPresenter.setExpandedActionViewsExclusive(true); 377 if (menu != null) { 378 menu.addMenuPresenter(outerPresenter, mPopupContext); 379 menu.addMenuPresenter(mExpandedMenuPresenter, mPopupContext); 380 } else { 381 outerPresenter.initForMenu(mPopupContext, null); 382 mExpandedMenuPresenter.initForMenu(mPopupContext, null); 383 outerPresenter.updateMenuView(true); 384 mExpandedMenuPresenter.updateMenuView(true); 385 } 386 mMenuView.setPopupTheme(mPopupTheme); 387 mMenuView.setPresenter(outerPresenter); 388 mOuterActionMenuPresenter = outerPresenter; 389 } 390 391 /** 392 * Dismiss all currently showing popup menus, including overflow or submenus. 393 */ 394 public void dismissPopupMenus() { 395 if (mMenuView != null) { 396 mMenuView.dismissPopupMenus(); 397 } 398 } 399 400 /** @hide */ 401 public boolean isTitleTruncated() { 402 if (mTitleTextView == null) { 403 return false; 404 } 405 406 final Layout titleLayout = mTitleTextView.getLayout(); 407 if (titleLayout == null) { 408 return false; 409 } 410 411 final int lineCount = titleLayout.getLineCount(); 412 for (int i = 0; i < lineCount; i++) { 413 if (titleLayout.getEllipsisCount(i) > 0) { 414 return true; 415 } 416 } 417 return false; 418 } 419 420 /** 421 * Set a logo drawable. 422 * 423 * <p>This drawable should generally take the place of title text. The logo cannot be 424 * clicked. Apps using a logo should also supply a description using 425 * {@link #setLogoDescription(int)}.</p> 426 * 427 * @param drawable Drawable to use as a logo 428 */ 429 public void setLogo(Drawable drawable) { 430 if (drawable != null) { 431 ensureLogoView(); 432 if (mLogoView.getParent() == null) { 433 addSystemView(mLogoView); 434 updateChildVisibilityForExpandedActionView(mLogoView); 435 } 436 } else if (mLogoView != null && mLogoView.getParent() != null) { 437 removeView(mLogoView); 438 } 439 if (mLogoView != null) { 440 mLogoView.setImageDrawable(drawable); 441 } 442 } 443 444 /** 445 * Return the current logo drawable. 446 * 447 * @return The current logo drawable 448 * @see #setLogo(int) 449 * @see #setLogo(android.graphics.drawable.Drawable) 450 */ 451 public Drawable getLogo() { 452 return mLogoView != null ? mLogoView.getDrawable() : null; 453 } 454 455 /** 456 * Set a description of the toolbar's logo. 457 * 458 * <p>This description will be used for accessibility or other similar descriptions 459 * of the UI.</p> 460 * 461 * @param resId String resource id 462 */ 463 public void setLogoDescription(int resId) { 464 setLogoDescription(getContext().getText(resId)); 465 } 466 467 /** 468 * Set a description of the toolbar's logo. 469 * 470 * <p>This description will be used for accessibility or other similar descriptions 471 * of the UI.</p> 472 * 473 * @param description Description to set 474 */ 475 public void setLogoDescription(CharSequence description) { 476 if (!TextUtils.isEmpty(description)) { 477 ensureLogoView(); 478 } 479 if (mLogoView != null) { 480 mLogoView.setContentDescription(description); 481 } 482 } 483 484 /** 485 * Return the description of the toolbar's logo. 486 * 487 * @return A description of the logo 488 */ 489 public CharSequence getLogoDescription() { 490 return mLogoView != null ? mLogoView.getContentDescription() : null; 491 } 492 493 private void ensureLogoView() { 494 if (mLogoView == null) { 495 mLogoView = new ImageView(getContext()); 496 } 497 } 498 499 /** 500 * Check whether this Toolbar is currently hosting an expanded action view. 501 * 502 * <p>An action view may be expanded either directly from the 503 * {@link android.view.MenuItem MenuItem} it belongs to or by user action. If the Toolbar 504 * has an expanded action view it can be collapsed using the {@link #collapseActionView()} 505 * method.</p> 506 * 507 * @return true if the Toolbar has an expanded action view 508 */ 509 public boolean hasExpandedActionView() { 510 return mExpandedMenuPresenter != null && 511 mExpandedMenuPresenter.mCurrentExpandedItem != null; 512 } 513 514 /** 515 * Collapse a currently expanded action view. If this Toolbar does not have an 516 * expanded action view this method has no effect. 517 * 518 * <p>An action view may be expanded either directly from the 519 * {@link android.view.MenuItem MenuItem} it belongs to or by user action.</p> 520 * 521 * @see #hasExpandedActionView() 522 */ 523 public void collapseActionView() { 524 final MenuItemImpl item = mExpandedMenuPresenter == null ? null : 525 mExpandedMenuPresenter.mCurrentExpandedItem; 526 if (item != null) { 527 item.collapseActionView(); 528 } 529 } 530 531 /** 532 * Returns the title of this toolbar. 533 * 534 * @return The current title. 535 */ 536 public CharSequence getTitle() { 537 return mTitleText; 538 } 539 540 /** 541 * Set the title of this toolbar. 542 * 543 * <p>A title should be used as the anchor for a section of content. It should 544 * describe or name the content being viewed.</p> 545 * 546 * @param resId Resource ID of a string to set as the title 547 */ 548 public void setTitle(int resId) { 549 setTitle(getContext().getText(resId)); 550 } 551 552 /** 553 * Set the title of this toolbar. 554 * 555 * <p>A title should be used as the anchor for a section of content. It should 556 * describe or name the content being viewed.</p> 557 * 558 * @param title Title to set 559 */ 560 public void setTitle(CharSequence title) { 561 if (!TextUtils.isEmpty(title)) { 562 if (mTitleTextView == null) { 563 final Context context = getContext(); 564 mTitleTextView = new TextView(context); 565 mTitleTextView.setSingleLine(); 566 mTitleTextView.setEllipsize(TextUtils.TruncateAt.END); 567 if (mTitleTextAppearance != 0) { 568 mTitleTextView.setTextAppearance(context, mTitleTextAppearance); 569 } 570 if (mTitleTextColor != 0) { 571 mTitleTextView.setTextColor(mTitleTextColor); 572 } 573 } 574 if (mTitleTextView.getParent() == null) { 575 addSystemView(mTitleTextView); 576 updateChildVisibilityForExpandedActionView(mTitleTextView); 577 } 578 } else if (mTitleTextView != null && mTitleTextView.getParent() != null) { 579 removeView(mTitleTextView); 580 } 581 if (mTitleTextView != null) { 582 mTitleTextView.setText(title); 583 } 584 mTitleText = title; 585 } 586 587 /** 588 * Return the subtitle of this toolbar. 589 * 590 * @return The current subtitle 591 */ 592 public CharSequence getSubtitle() { 593 return mSubtitleText; 594 } 595 596 /** 597 * Set the subtitle of this toolbar. 598 * 599 * <p>Subtitles should express extended information about the current content.</p> 600 * 601 * @param resId String resource ID 602 */ 603 public void setSubtitle(int resId) { 604 setSubtitle(getContext().getText(resId)); 605 } 606 607 /** 608 * Set the subtitle of this toolbar. 609 * 610 * <p>Subtitles should express extended information about the current content.</p> 611 * 612 * @param subtitle Subtitle to set 613 */ 614 public void setSubtitle(CharSequence subtitle) { 615 if (!TextUtils.isEmpty(subtitle)) { 616 if (mSubtitleTextView == null) { 617 final Context context = getContext(); 618 mSubtitleTextView = new TextView(context); 619 mSubtitleTextView.setSingleLine(); 620 mSubtitleTextView.setEllipsize(TextUtils.TruncateAt.END); 621 if (mSubtitleTextAppearance != 0) { 622 mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance); 623 } 624 if (mSubtitleTextColor != 0) { 625 mSubtitleTextView.setTextColor(mSubtitleTextColor); 626 } 627 } 628 if (mSubtitleTextView.getParent() == null) { 629 addSystemView(mSubtitleTextView); 630 updateChildVisibilityForExpandedActionView(mSubtitleTextView); 631 } 632 } else if (mSubtitleTextView != null && mSubtitleTextView.getParent() != null) { 633 removeView(mSubtitleTextView); 634 } 635 if (mSubtitleTextView != null) { 636 mSubtitleTextView.setText(subtitle); 637 } 638 mSubtitleText = subtitle; 639 } 640 641 /** 642 * Sets the text color, size, style, hint color, and highlight color 643 * from the specified TextAppearance resource. 644 */ 645 public void setTitleTextAppearance(Context context, int resId) { 646 mTitleTextAppearance = resId; 647 if (mTitleTextView != null) { 648 mTitleTextView.setTextAppearance(context, resId); 649 } 650 } 651 652 /** 653 * Sets the text color, size, style, hint color, and highlight color 654 * from the specified TextAppearance resource. 655 */ 656 public void setSubtitleTextAppearance(Context context, int resId) { 657 mSubtitleTextAppearance = resId; 658 if (mSubtitleTextView != null) { 659 mSubtitleTextView.setTextAppearance(context, resId); 660 } 661 } 662 663 /** 664 * Sets the text color of the title, if present. 665 * 666 * @param color The new text color in 0xAARRGGBB format 667 */ 668 public void setTitleTextColor(int color) { 669 mTitleTextColor = color; 670 if (mTitleTextView != null) { 671 mTitleTextView.setTextColor(color); 672 } 673 } 674 675 /** 676 * Sets the text color of the subtitle, if present. 677 * 678 * @param color The new text color in 0xAARRGGBB format 679 */ 680 public void setSubtitleTextColor(int color) { 681 mSubtitleTextColor = color; 682 if (mSubtitleTextView != null) { 683 mSubtitleTextView.setTextColor(color); 684 } 685 } 686 687 /** 688 * Retrieve the currently configured content description for the navigation button view. 689 * This will be used to describe the navigation action to users through mechanisms such 690 * as screen readers or tooltips. 691 * 692 * @return The navigation button's content description 693 * 694 * @attr ref android.R.styleable#Toolbar_navigationContentDescription 695 */ 696 @Nullable 697 public CharSequence getNavigationContentDescription() { 698 return mNavButtonView != null ? mNavButtonView.getContentDescription() : null; 699 } 700 701 /** 702 * Set a content description for the navigation button if one is present. The content 703 * description will be read via screen readers or other accessibility systems to explain 704 * the action of the navigation button. 705 * 706 * @param resId Resource ID of a content description string to set, or 0 to 707 * clear the description 708 * 709 * @attr ref android.R.styleable#Toolbar_navigationContentDescription 710 */ 711 public void setNavigationContentDescription(int resId) { 712 setNavigationContentDescription(resId != 0 ? getContext().getText(resId) : null); 713 } 714 715 /** 716 * Set a content description for the navigation button if one is present. The content 717 * description will be read via screen readers or other accessibility systems to explain 718 * the action of the navigation button. 719 * 720 * @param description Content description to set, or <code>null</code> to 721 * clear the content description 722 * 723 * @attr ref android.R.styleable#Toolbar_navigationContentDescription 724 */ 725 public void setNavigationContentDescription(@Nullable CharSequence description) { 726 if (!TextUtils.isEmpty(description)) { 727 ensureNavButtonView(); 728 } 729 if (mNavButtonView != null) { 730 mNavButtonView.setContentDescription(description); 731 } 732 } 733 734 /** 735 * Set the icon to use for the toolbar's navigation button. 736 * 737 * <p>The navigation button appears at the start of the toolbar if present. Setting an icon 738 * will make the navigation button visible.</p> 739 * 740 * <p>If you use a navigation icon you should also set a description for its action using 741 * {@link #setNavigationContentDescription(int)}. This is used for accessibility and 742 * tooltips.</p> 743 * 744 * @param resId Resource ID of a drawable to set 745 * 746 * @attr ref android.R.styleable#Toolbar_navigationIcon 747 */ 748 public void setNavigationIcon(int resId) { 749 setNavigationIcon(getContext().getDrawable(resId)); 750 } 751 752 /** 753 * Set the icon to use for the toolbar's navigation button. 754 * 755 * <p>The navigation button appears at the start of the toolbar if present. Setting an icon 756 * will make the navigation button visible.</p> 757 * 758 * <p>If you use a navigation icon you should also set a description for its action using 759 * {@link #setNavigationContentDescription(int)}. This is used for accessibility and 760 * tooltips.</p> 761 * 762 * @param icon Drawable to set, may be null to clear the icon 763 * 764 * @attr ref android.R.styleable#Toolbar_navigationIcon 765 */ 766 public void setNavigationIcon(@Nullable Drawable icon) { 767 if (icon != null) { 768 ensureNavButtonView(); 769 if (mNavButtonView.getParent() == null) { 770 addSystemView(mNavButtonView); 771 updateChildVisibilityForExpandedActionView(mNavButtonView); 772 } 773 } else if (mNavButtonView != null && mNavButtonView.getParent() != null) { 774 removeView(mNavButtonView); 775 } 776 if (mNavButtonView != null) { 777 mNavButtonView.setImageDrawable(icon); 778 } 779 } 780 781 /** 782 * Return the current drawable used as the navigation icon. 783 * 784 * @return The navigation icon drawable 785 * 786 * @attr ref android.R.styleable#Toolbar_navigationIcon 787 */ 788 @Nullable 789 public Drawable getNavigationIcon() { 790 return mNavButtonView != null ? mNavButtonView.getDrawable() : null; 791 } 792 793 /** 794 * Set a listener to respond to navigation events. 795 * 796 * <p>This listener will be called whenever the user clicks the navigation button 797 * at the start of the toolbar. An icon must be set for the navigation button to appear.</p> 798 * 799 * @param listener Listener to set 800 * @see #setNavigationIcon(android.graphics.drawable.Drawable) 801 */ 802 public void setNavigationOnClickListener(OnClickListener listener) { 803 ensureNavButtonView(); 804 mNavButtonView.setOnClickListener(listener); 805 } 806 807 /** 808 * Return the Menu shown in the toolbar. 809 * 810 * <p>Applications that wish to populate the toolbar's menu can do so from here. To use 811 * an XML menu resource, use {@link #inflateMenu(int)}.</p> 812 * 813 * @return The toolbar's Menu 814 */ 815 public Menu getMenu() { 816 ensureMenu(); 817 return mMenuView.getMenu(); 818 } 819 820 private void ensureMenu() { 821 ensureMenuView(); 822 if (mMenuView.peekMenu() == null) { 823 // Initialize a new menu for the first time. 824 final MenuBuilder menu = (MenuBuilder) mMenuView.getMenu(); 825 if (mExpandedMenuPresenter == null) { 826 mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter(); 827 } 828 mMenuView.setExpandedActionViewsExclusive(true); 829 menu.addMenuPresenter(mExpandedMenuPresenter, mPopupContext); 830 } 831 } 832 833 private void ensureMenuView() { 834 if (mMenuView == null) { 835 mMenuView = new ActionMenuView(getContext()); 836 mMenuView.setPopupTheme(mPopupTheme); 837 mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener); 838 mMenuView.setMenuCallbacks(mActionMenuPresenterCallback, mMenuBuilderCallback); 839 final LayoutParams lp = generateDefaultLayoutParams(); 840 lp.gravity = Gravity.END | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK); 841 mMenuView.setLayoutParams(lp); 842 addSystemView(mMenuView); 843 } 844 } 845 846 private MenuInflater getMenuInflater() { 847 return new MenuInflater(getContext()); 848 } 849 850 /** 851 * Inflate a menu resource into this toolbar. 852 * 853 * <p>Inflate an XML menu resource into this toolbar. Existing items in the menu will not 854 * be modified or removed.</p> 855 * 856 * @param resId ID of a menu resource to inflate 857 */ 858 public void inflateMenu(int resId) { 859 getMenuInflater().inflate(resId, getMenu()); 860 } 861 862 /** 863 * Set a listener to respond to menu item click events. 864 * 865 * <p>This listener will be invoked whenever a user selects a menu item from 866 * the action buttons presented at the end of the toolbar or the associated overflow.</p> 867 * 868 * @param listener Listener to set 869 */ 870 public void setOnMenuItemClickListener(OnMenuItemClickListener listener) { 871 mOnMenuItemClickListener = listener; 872 } 873 874 /** 875 * Set the content insets for this toolbar relative to layout direction. 876 * 877 * <p>The content inset affects the valid area for Toolbar content other than 878 * the navigation button and menu. Insets define the minimum margin for these components 879 * and can be used to effectively align Toolbar content along well-known gridlines.</p> 880 * 881 * @param contentInsetStart Content inset for the toolbar starting edge 882 * @param contentInsetEnd Content inset for the toolbar ending edge 883 * 884 * @see #setContentInsetsAbsolute(int, int) 885 * @see #getContentInsetStart() 886 * @see #getContentInsetEnd() 887 * @see #getContentInsetLeft() 888 * @see #getContentInsetRight() 889 */ 890 public void setContentInsetsRelative(int contentInsetStart, int contentInsetEnd) { 891 mContentInsets.setRelative(contentInsetStart, contentInsetEnd); 892 } 893 894 /** 895 * Get the starting content inset for this toolbar. 896 * 897 * <p>The content inset affects the valid area for Toolbar content other than 898 * the navigation button and menu. Insets define the minimum margin for these components 899 * and can be used to effectively align Toolbar content along well-known gridlines.</p> 900 * 901 * @return The starting content inset for this toolbar 902 * 903 * @see #setContentInsetsRelative(int, int) 904 * @see #setContentInsetsAbsolute(int, int) 905 * @see #getContentInsetEnd() 906 * @see #getContentInsetLeft() 907 * @see #getContentInsetRight() 908 */ 909 public int getContentInsetStart() { 910 return mContentInsets.getStart(); 911 } 912 913 /** 914 * Get the ending content inset for this toolbar. 915 * 916 * <p>The content inset affects the valid area for Toolbar content other than 917 * the navigation button and menu. Insets define the minimum margin for these components 918 * and can be used to effectively align Toolbar content along well-known gridlines.</p> 919 * 920 * @return The ending content inset for this toolbar 921 * 922 * @see #setContentInsetsRelative(int, int) 923 * @see #setContentInsetsAbsolute(int, int) 924 * @see #getContentInsetStart() 925 * @see #getContentInsetLeft() 926 * @see #getContentInsetRight() 927 */ 928 public int getContentInsetEnd() { 929 return mContentInsets.getEnd(); 930 } 931 932 /** 933 * Set the content insets for this toolbar. 934 * 935 * <p>The content inset affects the valid area for Toolbar content other than 936 * the navigation button and menu. Insets define the minimum margin for these components 937 * and can be used to effectively align Toolbar content along well-known gridlines.</p> 938 * 939 * @param contentInsetLeft Content inset for the toolbar's left edge 940 * @param contentInsetRight Content inset for the toolbar's right edge 941 * 942 * @see #setContentInsetsAbsolute(int, int) 943 * @see #getContentInsetStart() 944 * @see #getContentInsetEnd() 945 * @see #getContentInsetLeft() 946 * @see #getContentInsetRight() 947 */ 948 public void setContentInsetsAbsolute(int contentInsetLeft, int contentInsetRight) { 949 mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight); 950 } 951 952 /** 953 * Get the left content inset for this toolbar. 954 * 955 * <p>The content inset affects the valid area for Toolbar content other than 956 * the navigation button and menu. Insets define the minimum margin for these components 957 * and can be used to effectively align Toolbar content along well-known gridlines.</p> 958 * 959 * @return The left content inset for this toolbar 960 * 961 * @see #setContentInsetsRelative(int, int) 962 * @see #setContentInsetsAbsolute(int, int) 963 * @see #getContentInsetStart() 964 * @see #getContentInsetEnd() 965 * @see #getContentInsetRight() 966 */ 967 public int getContentInsetLeft() { 968 return mContentInsets.getLeft(); 969 } 970 971 /** 972 * Get the right content inset for this toolbar. 973 * 974 * <p>The content inset affects the valid area for Toolbar content other than 975 * the navigation button and menu. Insets define the minimum margin for these components 976 * and can be used to effectively align Toolbar content along well-known gridlines.</p> 977 * 978 * @return The right content inset for this toolbar 979 * 980 * @see #setContentInsetsRelative(int, int) 981 * @see #setContentInsetsAbsolute(int, int) 982 * @see #getContentInsetStart() 983 * @see #getContentInsetEnd() 984 * @see #getContentInsetLeft() 985 */ 986 public int getContentInsetRight() { 987 return mContentInsets.getRight(); 988 } 989 990 private void ensureNavButtonView() { 991 if (mNavButtonView == null) { 992 mNavButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle); 993 final LayoutParams lp = generateDefaultLayoutParams(); 994 lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK); 995 mNavButtonView.setLayoutParams(lp); 996 } 997 } 998 999 private void ensureCollapseButtonView() { 1000 if (mCollapseButtonView == null) { 1001 mCollapseButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle); 1002 mCollapseButtonView.setImageDrawable(mCollapseIcon); 1003 mCollapseButtonView.setContentDescription(mCollapseDescription); 1004 final LayoutParams lp = generateDefaultLayoutParams(); 1005 lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK); 1006 lp.mViewType = LayoutParams.EXPANDED; 1007 mCollapseButtonView.setLayoutParams(lp); 1008 mCollapseButtonView.setOnClickListener(new OnClickListener() { 1009 @Override 1010 public void onClick(View v) { 1011 collapseActionView(); 1012 } 1013 }); 1014 } 1015 } 1016 1017 private void addSystemView(View v) { 1018 final ViewGroup.LayoutParams vlp = v.getLayoutParams(); 1019 final LayoutParams lp; 1020 if (vlp == null) { 1021 lp = generateDefaultLayoutParams(); 1022 } else if (!checkLayoutParams(vlp)) { 1023 lp = generateLayoutParams(vlp); 1024 } else { 1025 lp = (LayoutParams) vlp; 1026 } 1027 lp.mViewType = LayoutParams.SYSTEM; 1028 addView(v, lp); 1029 } 1030 1031 @Override 1032 protected Parcelable onSaveInstanceState() { 1033 SavedState state = new SavedState(super.onSaveInstanceState()); 1034 1035 if (mExpandedMenuPresenter != null && mExpandedMenuPresenter.mCurrentExpandedItem != null) { 1036 state.expandedMenuItemId = mExpandedMenuPresenter.mCurrentExpandedItem.getItemId(); 1037 } 1038 1039 state.isOverflowOpen = isOverflowMenuShowing(); 1040 1041 return state; 1042 } 1043 1044 @Override 1045 protected void onRestoreInstanceState(Parcelable state) { 1046 final SavedState ss = (SavedState) state; 1047 super.onRestoreInstanceState(ss.getSuperState()); 1048 1049 final Menu menu = mMenuView != null ? mMenuView.peekMenu() : null; 1050 if (ss.expandedMenuItemId != 0 && mExpandedMenuPresenter != null && menu != null) { 1051 final MenuItem item = menu.findItem(ss.expandedMenuItemId); 1052 if (item != null) { 1053 item.expandActionView(); 1054 } 1055 } 1056 1057 if (ss.isOverflowOpen) { 1058 postShowOverflowMenu(); 1059 } 1060 } 1061 1062 private void postShowOverflowMenu() { 1063 removeCallbacks(mShowOverflowMenuRunnable); 1064 post(mShowOverflowMenuRunnable); 1065 } 1066 1067 @Override 1068 protected void onDetachedFromWindow() { 1069 super.onDetachedFromWindow(); 1070 removeCallbacks(mShowOverflowMenuRunnable); 1071 } 1072 1073 @Override 1074 public boolean onTouchEvent(MotionEvent ev) { 1075 // Toolbars always eat touch events, but should still respect the touch event dispatch 1076 // contract. If the normal View implementation doesn't want the events, we'll just silently 1077 // eat the rest of the gesture without reporting the events to the default implementation 1078 // since that's what it expects. 1079 1080 final int action = ev.getActionMasked(); 1081 if (action == MotionEvent.ACTION_DOWN) { 1082 mEatingTouch = false; 1083 } 1084 1085 if (!mEatingTouch) { 1086 final boolean handled = super.onTouchEvent(ev); 1087 if (action == MotionEvent.ACTION_DOWN && !handled) { 1088 mEatingTouch = true; 1089 } 1090 } 1091 1092 if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { 1093 mEatingTouch = false; 1094 } 1095 1096 return true; 1097 } 1098 1099 /** 1100 * @hide 1101 */ 1102 @Override 1103 protected void onSetLayoutParams(View child, ViewGroup.LayoutParams lp) { 1104 /* 1105 * Apps may set ActionBar.LayoutParams on their action bar custom views when 1106 * a Toolbar is actually acting in the role of the action bar. Perform a quick 1107 * switch with Toolbar.LayoutParams whenever this happens. This does leave open 1108 * one potential gotcha: if an app retains the ActionBar.LayoutParams reference 1109 * and attempts to keep making changes to it before layout those changes won't 1110 * be reflected in the final results. 1111 */ 1112 if (!checkLayoutParams(lp)) { 1113 child.setLayoutParams(generateLayoutParams(lp)); 1114 } 1115 } 1116 1117 private void measureChildConstrained(View child, int parentWidthSpec, int widthUsed, 1118 int parentHeightSpec, int heightUsed, int heightConstraint) { 1119 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 1120 1121 int childWidthSpec = getChildMeasureSpec(parentWidthSpec, 1122 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin 1123 + widthUsed, lp.width); 1124 int childHeightSpec = getChildMeasureSpec(parentHeightSpec, 1125 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin 1126 + heightUsed, lp.height); 1127 1128 final int childHeightMode = MeasureSpec.getMode(childHeightSpec); 1129 if (childHeightMode != MeasureSpec.EXACTLY && heightConstraint >= 0) { 1130 final int size = childHeightMode != MeasureSpec.UNSPECIFIED ? 1131 Math.min(MeasureSpec.getSize(childHeightSpec), heightConstraint) : 1132 heightConstraint; 1133 childHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); 1134 } 1135 child.measure(childWidthSpec, childHeightSpec); 1136 } 1137 1138 /** 1139 * Returns the width + uncollapsed margins 1140 */ 1141 private int measureChildCollapseMargins(View child, 1142 int parentWidthMeasureSpec, int widthUsed, 1143 int parentHeightMeasureSpec, int heightUsed, int[] collapsingMargins) { 1144 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 1145 1146 final int leftDiff = lp.leftMargin - collapsingMargins[0]; 1147 final int rightDiff = lp.rightMargin - collapsingMargins[1]; 1148 final int leftMargin = Math.max(0, leftDiff); 1149 final int rightMargin = Math.max(0, rightDiff); 1150 final int hMargins = leftMargin + rightMargin; 1151 collapsingMargins[0] = Math.max(0, -leftDiff); 1152 collapsingMargins[1] = Math.max(0, -rightDiff); 1153 1154 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 1155 mPaddingLeft + mPaddingRight + hMargins + widthUsed, lp.width); 1156 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 1157 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin 1158 + heightUsed, lp.height); 1159 1160 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 1161 return child.getMeasuredWidth() + hMargins; 1162 } 1163 1164 /** 1165 * Returns true if the Toolbar is collapsible and has no child views with a measured size > 0. 1166 */ 1167 private boolean shouldCollapse() { 1168 if (!mCollapsible) return false; 1169 1170 final int childCount = getChildCount(); 1171 for (int i = 0; i < childCount; i++) { 1172 final View child = getChildAt(i); 1173 if (shouldLayout(child) && child.getMeasuredWidth() > 0 && 1174 child.getMeasuredHeight() > 0) { 1175 return false; 1176 } 1177 } 1178 return true; 1179 } 1180 1181 @Override 1182 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1183 int width = 0; 1184 int height = 0; 1185 int childState = 0; 1186 1187 final int[] collapsingMargins = mTempMargins; 1188 final int marginStartIndex; 1189 final int marginEndIndex; 1190 if (isLayoutRtl()) { 1191 marginStartIndex = 1; 1192 marginEndIndex = 0; 1193 } else { 1194 marginStartIndex = 0; 1195 marginEndIndex = 1; 1196 } 1197 1198 // System views measure first. 1199 1200 int navWidth = 0; 1201 if (shouldLayout(mNavButtonView)) { 1202 measureChildConstrained(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0, 1203 mMaxButtonHeight); 1204 navWidth = mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView); 1205 height = Math.max(height, mNavButtonView.getMeasuredHeight() + 1206 getVerticalMargins(mNavButtonView)); 1207 childState = combineMeasuredStates(childState, mNavButtonView.getMeasuredState()); 1208 } 1209 1210 if (shouldLayout(mCollapseButtonView)) { 1211 measureChildConstrained(mCollapseButtonView, widthMeasureSpec, width, 1212 heightMeasureSpec, 0, mMaxButtonHeight); 1213 navWidth = mCollapseButtonView.getMeasuredWidth() + 1214 getHorizontalMargins(mCollapseButtonView); 1215 height = Math.max(height, mCollapseButtonView.getMeasuredHeight() + 1216 getVerticalMargins(mCollapseButtonView)); 1217 childState = combineMeasuredStates(childState, mCollapseButtonView.getMeasuredState()); 1218 } 1219 1220 final int contentInsetStart = getContentInsetStart(); 1221 width += Math.max(contentInsetStart, navWidth); 1222 collapsingMargins[marginStartIndex] = Math.max(0, contentInsetStart - navWidth); 1223 1224 int menuWidth = 0; 1225 if (shouldLayout(mMenuView)) { 1226 measureChildConstrained(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0, 1227 mMaxButtonHeight); 1228 menuWidth = mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView); 1229 height = Math.max(height, mMenuView.getMeasuredHeight() + 1230 getVerticalMargins(mMenuView)); 1231 childState = combineMeasuredStates(childState, mMenuView.getMeasuredState()); 1232 } 1233 1234 final int contentInsetEnd = getContentInsetEnd(); 1235 width += Math.max(contentInsetEnd, menuWidth); 1236 collapsingMargins[marginEndIndex] = Math.max(0, contentInsetEnd - menuWidth); 1237 1238 if (shouldLayout(mExpandedActionView)) { 1239 width += measureChildCollapseMargins(mExpandedActionView, widthMeasureSpec, width, 1240 heightMeasureSpec, 0, collapsingMargins); 1241 height = Math.max(height, mExpandedActionView.getMeasuredHeight() + 1242 getVerticalMargins(mExpandedActionView)); 1243 childState = combineMeasuredStates(childState, mExpandedActionView.getMeasuredState()); 1244 } 1245 1246 if (shouldLayout(mLogoView)) { 1247 width += measureChildCollapseMargins(mLogoView, widthMeasureSpec, width, 1248 heightMeasureSpec, 0, collapsingMargins); 1249 height = Math.max(height, mLogoView.getMeasuredHeight() + 1250 getVerticalMargins(mLogoView)); 1251 childState = combineMeasuredStates(childState, mLogoView.getMeasuredState()); 1252 } 1253 1254 final int childCount = getChildCount(); 1255 for (int i = 0; i < childCount; i++) { 1256 final View child = getChildAt(i); 1257 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1258 if (lp.mViewType != LayoutParams.CUSTOM || !shouldLayout(child)) { 1259 // We already got all system views above. Skip them and GONE views. 1260 continue; 1261 } 1262 1263 width += measureChildCollapseMargins(child, widthMeasureSpec, width, 1264 heightMeasureSpec, 0, collapsingMargins); 1265 height = Math.max(height, child.getMeasuredHeight() + getVerticalMargins(child)); 1266 childState = combineMeasuredStates(childState, child.getMeasuredState()); 1267 } 1268 1269 int titleWidth = 0; 1270 int titleHeight = 0; 1271 final int titleVertMargins = mTitleMarginTop + mTitleMarginBottom; 1272 final int titleHorizMargins = mTitleMarginStart + mTitleMarginEnd; 1273 if (shouldLayout(mTitleTextView)) { 1274 titleWidth = measureChildCollapseMargins(mTitleTextView, widthMeasureSpec, 1275 width + titleHorizMargins, heightMeasureSpec, titleVertMargins, 1276 collapsingMargins); 1277 titleWidth = mTitleTextView.getMeasuredWidth() + getHorizontalMargins(mTitleTextView); 1278 titleHeight = mTitleTextView.getMeasuredHeight() + getVerticalMargins(mTitleTextView); 1279 childState = combineMeasuredStates(childState, mTitleTextView.getMeasuredState()); 1280 } 1281 if (shouldLayout(mSubtitleTextView)) { 1282 titleWidth = Math.max(titleWidth, measureChildCollapseMargins(mSubtitleTextView, 1283 widthMeasureSpec, width + titleHorizMargins, 1284 heightMeasureSpec, titleHeight + titleVertMargins, 1285 collapsingMargins)); 1286 titleHeight += mSubtitleTextView.getMeasuredHeight() + 1287 getVerticalMargins(mSubtitleTextView); 1288 childState = combineMeasuredStates(childState, mSubtitleTextView.getMeasuredState()); 1289 } 1290 1291 width += titleWidth; 1292 height = Math.max(height, titleHeight); 1293 1294 // Measurement already took padding into account for available space for the children, 1295 // add it in for the final size. 1296 width += getPaddingLeft() + getPaddingRight(); 1297 height += getPaddingTop() + getPaddingBottom(); 1298 1299 final int measuredWidth = resolveSizeAndState( 1300 Math.max(width, getSuggestedMinimumWidth()), 1301 widthMeasureSpec, childState & MEASURED_STATE_MASK); 1302 final int measuredHeight = resolveSizeAndState( 1303 Math.max(height, getSuggestedMinimumHeight()), 1304 heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT); 1305 1306 setMeasuredDimension(measuredWidth, shouldCollapse() ? 0 : measuredHeight); 1307 } 1308 1309 @Override 1310 protected void onLayout(boolean changed, int l, int t, int r, int b) { 1311 final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; 1312 final int width = getWidth(); 1313 final int height = getHeight(); 1314 final int paddingLeft = getPaddingLeft(); 1315 final int paddingRight = getPaddingRight(); 1316 final int paddingTop = getPaddingTop(); 1317 final int paddingBottom = getPaddingBottom(); 1318 int left = paddingLeft; 1319 int right = width - paddingRight; 1320 1321 final int[] collapsingMargins = mTempMargins; 1322 collapsingMargins[0] = collapsingMargins[1] = 0; 1323 1324 // Align views within the minimum toolbar height, if set. 1325 final int alignmentHeight = getMinimumHeight(); 1326 1327 if (shouldLayout(mNavButtonView)) { 1328 if (isRtl) { 1329 right = layoutChildRight(mNavButtonView, right, collapsingMargins, 1330 alignmentHeight); 1331 } else { 1332 left = layoutChildLeft(mNavButtonView, left, collapsingMargins, 1333 alignmentHeight); 1334 } 1335 } 1336 1337 if (shouldLayout(mCollapseButtonView)) { 1338 if (isRtl) { 1339 right = layoutChildRight(mCollapseButtonView, right, collapsingMargins, 1340 alignmentHeight); 1341 } else { 1342 left = layoutChildLeft(mCollapseButtonView, left, collapsingMargins, 1343 alignmentHeight); 1344 } 1345 } 1346 1347 if (shouldLayout(mMenuView)) { 1348 if (isRtl) { 1349 left = layoutChildLeft(mMenuView, left, collapsingMargins, 1350 alignmentHeight); 1351 } else { 1352 right = layoutChildRight(mMenuView, right, collapsingMargins, 1353 alignmentHeight); 1354 } 1355 } 1356 1357 collapsingMargins[0] = Math.max(0, getContentInsetLeft() - left); 1358 collapsingMargins[1] = Math.max(0, getContentInsetRight() - (width - paddingRight - right)); 1359 left = Math.max(left, getContentInsetLeft()); 1360 right = Math.min(right, width - paddingRight - getContentInsetRight()); 1361 1362 if (shouldLayout(mExpandedActionView)) { 1363 if (isRtl) { 1364 right = layoutChildRight(mExpandedActionView, right, collapsingMargins, 1365 alignmentHeight); 1366 } else { 1367 left = layoutChildLeft(mExpandedActionView, left, collapsingMargins, 1368 alignmentHeight); 1369 } 1370 } 1371 1372 if (shouldLayout(mLogoView)) { 1373 if (isRtl) { 1374 right = layoutChildRight(mLogoView, right, collapsingMargins, 1375 alignmentHeight); 1376 } else { 1377 left = layoutChildLeft(mLogoView, left, collapsingMargins, 1378 alignmentHeight); 1379 } 1380 } 1381 1382 final boolean layoutTitle = shouldLayout(mTitleTextView); 1383 final boolean layoutSubtitle = shouldLayout(mSubtitleTextView); 1384 int titleHeight = 0; 1385 if (layoutTitle) { 1386 final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); 1387 titleHeight += lp.topMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin; 1388 } 1389 if (layoutSubtitle) { 1390 final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams(); 1391 titleHeight += lp.topMargin + mSubtitleTextView.getMeasuredHeight() + lp.bottomMargin; 1392 } 1393 1394 if (layoutTitle || layoutSubtitle) { 1395 int titleTop; 1396 final View topChild = layoutTitle ? mTitleTextView : mSubtitleTextView; 1397 final View bottomChild = layoutSubtitle ? mSubtitleTextView : mTitleTextView; 1398 final LayoutParams toplp = (LayoutParams) topChild.getLayoutParams(); 1399 final LayoutParams bottomlp = (LayoutParams) bottomChild.getLayoutParams(); 1400 final boolean titleHasWidth = layoutTitle && mTitleTextView.getMeasuredWidth() > 0 1401 || layoutSubtitle && mSubtitleTextView.getMeasuredWidth() > 0; 1402 1403 switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) { 1404 case Gravity.TOP: 1405 titleTop = getPaddingTop() + toplp.topMargin + mTitleMarginTop; 1406 break; 1407 default: 1408 case Gravity.CENTER_VERTICAL: 1409 final int space = height - paddingTop - paddingBottom; 1410 int spaceAbove = (space - titleHeight) / 2; 1411 if (spaceAbove < toplp.topMargin + mTitleMarginTop) { 1412 spaceAbove = toplp.topMargin + mTitleMarginTop; 1413 } else { 1414 final int spaceBelow = height - paddingBottom - titleHeight - 1415 spaceAbove - paddingTop; 1416 if (spaceBelow < toplp.bottomMargin + mTitleMarginBottom) { 1417 spaceAbove = Math.max(0, spaceAbove - 1418 (bottomlp.bottomMargin + mTitleMarginBottom - spaceBelow)); 1419 } 1420 } 1421 titleTop = paddingTop + spaceAbove; 1422 break; 1423 case Gravity.BOTTOM: 1424 titleTop = height - paddingBottom - bottomlp.bottomMargin - mTitleMarginBottom - 1425 titleHeight; 1426 break; 1427 } 1428 if (isRtl) { 1429 final int rd = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[1]; 1430 right -= Math.max(0, rd); 1431 collapsingMargins[1] = Math.max(0, -rd); 1432 int titleRight = right; 1433 int subtitleRight = right; 1434 1435 if (layoutTitle) { 1436 final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); 1437 final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth(); 1438 final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight(); 1439 mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom); 1440 titleRight = titleLeft - mTitleMarginEnd; 1441 titleTop = titleBottom + lp.bottomMargin; 1442 } 1443 if (layoutSubtitle) { 1444 final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams(); 1445 titleTop += lp.topMargin; 1446 final int subtitleLeft = subtitleRight - mSubtitleTextView.getMeasuredWidth(); 1447 final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight(); 1448 mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom); 1449 subtitleRight = subtitleRight - mTitleMarginEnd; 1450 titleTop = subtitleBottom + lp.bottomMargin; 1451 } 1452 if (titleHasWidth) { 1453 right = Math.min(titleRight, subtitleRight); 1454 } 1455 } else { 1456 final int ld = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[0]; 1457 left += Math.max(0, ld); 1458 collapsingMargins[0] = Math.max(0, -ld); 1459 int titleLeft = left; 1460 int subtitleLeft = left; 1461 1462 if (layoutTitle) { 1463 final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); 1464 final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth(); 1465 final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight(); 1466 mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom); 1467 titleLeft = titleRight + mTitleMarginEnd; 1468 titleTop = titleBottom + lp.bottomMargin; 1469 } 1470 if (layoutSubtitle) { 1471 final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams(); 1472 titleTop += lp.topMargin; 1473 final int subtitleRight = subtitleLeft + mSubtitleTextView.getMeasuredWidth(); 1474 final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight(); 1475 mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom); 1476 subtitleLeft = subtitleRight + mTitleMarginEnd; 1477 titleTop = subtitleBottom + lp.bottomMargin; 1478 } 1479 if (titleHasWidth) { 1480 left = Math.max(titleLeft, subtitleLeft); 1481 } 1482 } 1483 } 1484 1485 // Get all remaining children sorted for layout. This is all prepared 1486 // such that absolute layout direction can be used below. 1487 1488 addCustomViewsWithGravity(mTempViews, Gravity.LEFT); 1489 final int leftViewsCount = mTempViews.size(); 1490 for (int i = 0; i < leftViewsCount; i++) { 1491 left = layoutChildLeft(mTempViews.get(i), left, collapsingMargins, 1492 alignmentHeight); 1493 } 1494 1495 addCustomViewsWithGravity(mTempViews, Gravity.RIGHT); 1496 final int rightViewsCount = mTempViews.size(); 1497 for (int i = 0; i < rightViewsCount; i++) { 1498 right = layoutChildRight(mTempViews.get(i), right, collapsingMargins, 1499 alignmentHeight); 1500 } 1501 1502 // Centered views try to center with respect to the whole bar, but views pinned 1503 // to the left or right can push the mass of centered views to one side or the other. 1504 addCustomViewsWithGravity(mTempViews, Gravity.CENTER_HORIZONTAL); 1505 final int centerViewsWidth = getViewListMeasuredWidth(mTempViews, collapsingMargins); 1506 final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2; 1507 final int halfCenterViewsWidth = centerViewsWidth / 2; 1508 int centerLeft = parentCenter - halfCenterViewsWidth; 1509 final int centerRight = centerLeft + centerViewsWidth; 1510 if (centerLeft < left) { 1511 centerLeft = left; 1512 } else if (centerRight > right) { 1513 centerLeft -= centerRight - right; 1514 } 1515 1516 final int centerViewsCount = mTempViews.size(); 1517 for (int i = 0; i < centerViewsCount; i++) { 1518 centerLeft = layoutChildLeft(mTempViews.get(i), centerLeft, collapsingMargins, 1519 alignmentHeight); 1520 } 1521 1522 mTempViews.clear(); 1523 } 1524 1525 private int getViewListMeasuredWidth(List<View> views, int[] collapsingMargins) { 1526 int collapseLeft = collapsingMargins[0]; 1527 int collapseRight = collapsingMargins[1]; 1528 int width = 0; 1529 final int count = views.size(); 1530 for (int i = 0; i < count; i++) { 1531 final View v = views.get(i); 1532 final LayoutParams lp = (LayoutParams) v.getLayoutParams(); 1533 final int l = lp.leftMargin - collapseLeft; 1534 final int r = lp.rightMargin - collapseRight; 1535 final int leftMargin = Math.max(0, l); 1536 final int rightMargin = Math.max(0, r); 1537 collapseLeft = Math.max(0, -l); 1538 collapseRight = Math.max(0, -r); 1539 width += leftMargin + v.getMeasuredWidth() + rightMargin; 1540 } 1541 return width; 1542 } 1543 1544 private int layoutChildLeft(View child, int left, int[] collapsingMargins, 1545 int alignmentHeight) { 1546 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1547 final int l = lp.leftMargin - collapsingMargins[0]; 1548 left += Math.max(0, l); 1549 collapsingMargins[0] = Math.max(0, -l); 1550 final int top = getChildTop(child, alignmentHeight); 1551 final int childWidth = child.getMeasuredWidth(); 1552 child.layout(left, top, left + childWidth, top + child.getMeasuredHeight()); 1553 left += childWidth + lp.rightMargin; 1554 return left; 1555 } 1556 1557 private int layoutChildRight(View child, int right, int[] collapsingMargins, 1558 int alignmentHeight) { 1559 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1560 final int r = lp.rightMargin - collapsingMargins[1]; 1561 right -= Math.max(0, r); 1562 collapsingMargins[1] = Math.max(0, -r); 1563 final int top = getChildTop(child, alignmentHeight); 1564 final int childWidth = child.getMeasuredWidth(); 1565 child.layout(right - childWidth, top, right, top + child.getMeasuredHeight()); 1566 right -= childWidth + lp.leftMargin; 1567 return right; 1568 } 1569 1570 private int getChildTop(View child, int alignmentHeight) { 1571 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1572 final int childHeight = child.getMeasuredHeight(); 1573 final int alignmentOffset = alignmentHeight > 0 ? (childHeight - alignmentHeight) / 2 : 0; 1574 switch (getChildVerticalGravity(lp.gravity)) { 1575 case Gravity.TOP: 1576 return getPaddingTop() - alignmentOffset; 1577 1578 case Gravity.BOTTOM: 1579 return getHeight() - getPaddingBottom() - childHeight 1580 - lp.bottomMargin - alignmentOffset; 1581 1582 default: 1583 case Gravity.CENTER_VERTICAL: 1584 final int paddingTop = getPaddingTop(); 1585 final int paddingBottom = getPaddingBottom(); 1586 final int height = getHeight(); 1587 final int space = height - paddingTop - paddingBottom; 1588 int spaceAbove = (space - childHeight) / 2; 1589 if (spaceAbove < lp.topMargin) { 1590 spaceAbove = lp.topMargin; 1591 } else { 1592 final int spaceBelow = height - paddingBottom - childHeight - 1593 spaceAbove - paddingTop; 1594 if (spaceBelow < lp.bottomMargin) { 1595 spaceAbove = Math.max(0, spaceAbove - (lp.bottomMargin - spaceBelow)); 1596 } 1597 } 1598 return paddingTop + spaceAbove; 1599 } 1600 } 1601 1602 private int getChildVerticalGravity(int gravity) { 1603 final int vgrav = gravity & Gravity.VERTICAL_GRAVITY_MASK; 1604 switch (vgrav) { 1605 case Gravity.TOP: 1606 case Gravity.BOTTOM: 1607 case Gravity.CENTER_VERTICAL: 1608 return vgrav; 1609 default: 1610 return mGravity & Gravity.VERTICAL_GRAVITY_MASK; 1611 } 1612 } 1613 1614 /** 1615 * Prepare a list of non-SYSTEM child views. If the layout direction is RTL 1616 * this will be in reverse child order. 1617 * 1618 * @param views List to populate. It will be cleared before use. 1619 * @param gravity Horizontal gravity to match against 1620 */ 1621 private void addCustomViewsWithGravity(List<View> views, int gravity) { 1622 final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; 1623 final int childCount = getChildCount(); 1624 final int absGrav = Gravity.getAbsoluteGravity(gravity, getLayoutDirection()); 1625 1626 views.clear(); 1627 1628 if (isRtl) { 1629 for (int i = childCount - 1; i >= 0; i--) { 1630 final View child = getChildAt(i); 1631 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1632 if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) && 1633 getChildHorizontalGravity(lp.gravity) == absGrav) { 1634 views.add(child); 1635 } 1636 } 1637 } else { 1638 for (int i = 0; i < childCount; i++) { 1639 final View child = getChildAt(i); 1640 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1641 if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) && 1642 getChildHorizontalGravity(lp.gravity) == absGrav) { 1643 views.add(child); 1644 } 1645 } 1646 } 1647 } 1648 1649 private int getChildHorizontalGravity(int gravity) { 1650 final int ld = getLayoutDirection(); 1651 final int absGrav = Gravity.getAbsoluteGravity(gravity, ld); 1652 final int hGrav = absGrav & Gravity.HORIZONTAL_GRAVITY_MASK; 1653 switch (hGrav) { 1654 case Gravity.LEFT: 1655 case Gravity.RIGHT: 1656 case Gravity.CENTER_HORIZONTAL: 1657 return hGrav; 1658 default: 1659 return ld == LAYOUT_DIRECTION_RTL ? Gravity.RIGHT : Gravity.LEFT; 1660 } 1661 } 1662 1663 private boolean shouldLayout(View view) { 1664 return view != null && view.getParent() == this && view.getVisibility() != GONE; 1665 } 1666 1667 private int getHorizontalMargins(View v) { 1668 final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); 1669 return mlp.getMarginStart() + mlp.getMarginEnd(); 1670 } 1671 1672 private int getVerticalMargins(View v) { 1673 final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); 1674 return mlp.topMargin + mlp.bottomMargin; 1675 } 1676 1677 @Override 1678 public LayoutParams generateLayoutParams(AttributeSet attrs) { 1679 return new LayoutParams(getContext(), attrs); 1680 } 1681 1682 @Override 1683 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 1684 if (p instanceof LayoutParams) { 1685 return new LayoutParams((LayoutParams) p); 1686 } else if (p instanceof ActionBar.LayoutParams) { 1687 return new LayoutParams((ActionBar.LayoutParams) p); 1688 } else if (p instanceof MarginLayoutParams) { 1689 return new LayoutParams((MarginLayoutParams) p); 1690 } else { 1691 return new LayoutParams(p); 1692 } 1693 } 1694 1695 @Override 1696 protected LayoutParams generateDefaultLayoutParams() { 1697 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 1698 } 1699 1700 @Override 1701 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 1702 return super.checkLayoutParams(p) && p instanceof LayoutParams; 1703 } 1704 1705 private static boolean isCustomView(View child) { 1706 return ((LayoutParams) child.getLayoutParams()).mViewType == LayoutParams.CUSTOM; 1707 } 1708 1709 /** @hide */ 1710 public DecorToolbar getWrapper() { 1711 if (mWrapper == null) { 1712 mWrapper = new ToolbarWidgetWrapper(this, true); 1713 } 1714 return mWrapper; 1715 } 1716 1717 private void setChildVisibilityForExpandedActionView(boolean expand) { 1718 final int childCount = getChildCount(); 1719 for (int i = 0; i < childCount; i++) { 1720 final View child = getChildAt(i); 1721 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1722 if (lp.mViewType != LayoutParams.EXPANDED && child != mMenuView) { 1723 child.setVisibility(expand ? GONE : VISIBLE); 1724 } 1725 } 1726 } 1727 1728 private void updateChildVisibilityForExpandedActionView(View child) { 1729 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 1730 if (lp.mViewType != LayoutParams.EXPANDED && child != mMenuView) { 1731 child.setVisibility(mExpandedActionView != null ? GONE : VISIBLE); 1732 } 1733 } 1734 1735 /** 1736 * Force the toolbar to collapse to zero-height during measurement if 1737 * it could be considered "empty" (no visible elements with nonzero measured size) 1738 * @hide 1739 */ 1740 public void setCollapsible(boolean collapsible) { 1741 mCollapsible = collapsible; 1742 requestLayout(); 1743 } 1744 1745 /** 1746 * Must be called before the menu is accessed 1747 * @hide 1748 */ 1749 public void setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb) { 1750 mActionMenuPresenterCallback = pcb; 1751 mMenuBuilderCallback = mcb; 1752 } 1753 1754 /** 1755 * Interface responsible for receiving menu item click events if the items themselves 1756 * do not have individual item click listeners. 1757 */ 1758 public interface OnMenuItemClickListener { 1759 /** 1760 * This method will be invoked when a menu item is clicked if the item itself did 1761 * not already handle the event. 1762 * 1763 * @param item {@link MenuItem} that was clicked 1764 * @return <code>true</code> if the event was handled, <code>false</code> otherwise. 1765 */ 1766 public boolean onMenuItemClick(MenuItem item); 1767 } 1768 1769 /** 1770 * Layout information for child views of Toolbars. 1771 * 1772 * <p>Toolbar.LayoutParams extends ActionBar.LayoutParams for compatibility with existing 1773 * ActionBar API. See {@link android.app.Activity#setActionBar(Toolbar) Activity.setActionBar} 1774 * for more info on how to use a Toolbar as your Activity's ActionBar.</p> 1775 * 1776 * @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity 1777 */ 1778 public static class LayoutParams extends ActionBar.LayoutParams { 1779 static final int CUSTOM = 0; 1780 static final int SYSTEM = 1; 1781 static final int EXPANDED = 2; 1782 1783 int mViewType = CUSTOM; 1784 1785 public LayoutParams(@NonNull Context c, AttributeSet attrs) { 1786 super(c, attrs); 1787 } 1788 1789 public LayoutParams(int width, int height) { 1790 super(width, height); 1791 this.gravity = Gravity.CENTER_VERTICAL | Gravity.START; 1792 } 1793 1794 public LayoutParams(int width, int height, int gravity) { 1795 super(width, height); 1796 this.gravity = gravity; 1797 } 1798 1799 public LayoutParams(int gravity) { 1800 this(WRAP_CONTENT, MATCH_PARENT, gravity); 1801 } 1802 1803 public LayoutParams(LayoutParams source) { 1804 super(source); 1805 1806 mViewType = source.mViewType; 1807 } 1808 1809 public LayoutParams(ActionBar.LayoutParams source) { 1810 super(source); 1811 } 1812 1813 public LayoutParams(MarginLayoutParams source) { 1814 super(source); 1815 // ActionBar.LayoutParams doesn't have a MarginLayoutParams constructor. 1816 // Fake it here and copy over the relevant data. 1817 copyMarginsFrom(source); 1818 } 1819 1820 public LayoutParams(ViewGroup.LayoutParams source) { 1821 super(source); 1822 } 1823 } 1824 1825 static class SavedState extends BaseSavedState { 1826 public int expandedMenuItemId; 1827 public boolean isOverflowOpen; 1828 1829 public SavedState(Parcel source) { 1830 super(source); 1831 expandedMenuItemId = source.readInt(); 1832 isOverflowOpen = source.readInt() != 0; 1833 } 1834 1835 public SavedState(Parcelable superState) { 1836 super(superState); 1837 } 1838 1839 @Override 1840 public void writeToParcel(Parcel out, int flags) { 1841 super.writeToParcel(out, flags); 1842 out.writeInt(expandedMenuItemId); 1843 out.writeInt(isOverflowOpen ? 1 : 0); 1844 } 1845 1846 public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { 1847 1848 @Override 1849 public SavedState createFromParcel(Parcel source) { 1850 return new SavedState(source); 1851 } 1852 1853 @Override 1854 public SavedState[] newArray(int size) { 1855 return new SavedState[size]; 1856 } 1857 }; 1858 } 1859 1860 private class ExpandedActionViewMenuPresenter implements MenuPresenter { 1861 MenuBuilder mMenu; 1862 MenuItemImpl mCurrentExpandedItem; 1863 1864 @Override 1865 public void initForMenu(Context context, MenuBuilder menu) { 1866 // Clear the expanded action view when menus change. 1867 if (mMenu != null && mCurrentExpandedItem != null) { 1868 mMenu.collapseItemActionView(mCurrentExpandedItem); 1869 } 1870 mMenu = menu; 1871 } 1872 1873 @Override 1874 public MenuView getMenuView(ViewGroup root) { 1875 return null; 1876 } 1877 1878 @Override 1879 public void updateMenuView(boolean cleared) { 1880 // Make sure the expanded item we have is still there. 1881 if (mCurrentExpandedItem != null) { 1882 boolean found = false; 1883 1884 if (mMenu != null) { 1885 final int count = mMenu.size(); 1886 for (int i = 0; i < count; i++) { 1887 final MenuItem item = mMenu.getItem(i); 1888 if (item == mCurrentExpandedItem) { 1889 found = true; 1890 break; 1891 } 1892 } 1893 } 1894 1895 if (!found) { 1896 // The item we had expanded disappeared. Collapse. 1897 collapseItemActionView(mMenu, mCurrentExpandedItem); 1898 } 1899 } 1900 } 1901 1902 @Override 1903 public void setCallback(Callback cb) { 1904 } 1905 1906 @Override 1907 public boolean onSubMenuSelected(SubMenuBuilder subMenu) { 1908 return false; 1909 } 1910 1911 @Override 1912 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 1913 } 1914 1915 @Override 1916 public boolean flagActionItems() { 1917 return false; 1918 } 1919 1920 @Override 1921 public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { 1922 ensureCollapseButtonView(); 1923 if (mCollapseButtonView.getParent() != Toolbar.this) { 1924 addView(mCollapseButtonView); 1925 } 1926 mExpandedActionView = item.getActionView(); 1927 mCurrentExpandedItem = item; 1928 if (mExpandedActionView.getParent() != Toolbar.this) { 1929 final LayoutParams lp = generateDefaultLayoutParams(); 1930 lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK); 1931 lp.mViewType = LayoutParams.EXPANDED; 1932 mExpandedActionView.setLayoutParams(lp); 1933 addView(mExpandedActionView); 1934 } 1935 1936 setChildVisibilityForExpandedActionView(true); 1937 requestLayout(); 1938 item.setActionViewExpanded(true); 1939 1940 if (mExpandedActionView instanceof CollapsibleActionView) { 1941 ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded(); 1942 } 1943 1944 return true; 1945 } 1946 1947 @Override 1948 public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { 1949 // Do this before detaching the actionview from the hierarchy, in case 1950 // it needs to dismiss the soft keyboard, etc. 1951 if (mExpandedActionView instanceof CollapsibleActionView) { 1952 ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed(); 1953 } 1954 1955 removeView(mExpandedActionView); 1956 removeView(mCollapseButtonView); 1957 mExpandedActionView = null; 1958 1959 setChildVisibilityForExpandedActionView(false); 1960 mCurrentExpandedItem = null; 1961 requestLayout(); 1962 item.setActionViewExpanded(false); 1963 1964 return true; 1965 } 1966 1967 @Override 1968 public int getId() { 1969 return 0; 1970 } 1971 1972 @Override 1973 public Parcelable onSaveInstanceState() { 1974 return null; 1975 } 1976 1977 @Override 1978 public void onRestoreInstanceState(Parcelable state) { 1979 } 1980 } 1981} 1982