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