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