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