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