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