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