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