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