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