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