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