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