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