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