blob: a21fb41b7c998508a52e413fc6e5fe7a3adf2d12 [file] [log] [blame]
Adam Powelle43340c2014-03-17 19:10:43 -07001/*
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
Adam Powelle43340c2014-03-17 19:10:43 -070017package android.widget;
18
Tor Norbye80756e32015-03-02 09:39:27 -080019import android.annotation.ColorInt;
Tor Norbye417ee5b2015-03-10 20:57:37 -070020import android.annotation.DrawableRes;
21import android.annotation.MenuRes;
Adam Powelle43340c2014-03-17 19:10:43 -070022import android.annotation.NonNull;
Alan Viverettee88700a2014-07-25 12:57:31 -070023import android.annotation.Nullable;
Tor Norbye417ee5b2015-03-10 20:57:37 -070024import android.annotation.StringRes;
25import android.annotation.StyleRes;
Kirill Grouchnikov67960a02016-04-04 16:30:02 -040026import android.annotation.TestApi;
Mathew Inwood978c6e22018-08-21 15:58:55 +010027import android.annotation.UnsupportedAppUsage;
Adam Powelle021e6e2014-05-23 17:27:24 -070028import android.app.ActionBar;
Adam Powelle43340c2014-03-17 19:10:43 -070029import android.content.Context;
30import android.content.res.TypedArray;
31import android.graphics.drawable.Drawable;
Mathew Inwood8c854f82018-09-14 12:35:36 +010032import android.os.Build;
Adam Powelle43340c2014-03-17 19:10:43 -070033import android.os.Parcel;
34import android.os.Parcelable;
Adam Powelle021e6e2014-05-23 17:27:24 -070035import android.text.Layout;
Adam Powelle43340c2014-03-17 19:10:43 -070036import android.text.TextUtils;
37import android.util.AttributeSet;
Adam Powelle021e6e2014-05-23 17:27:24 -070038import android.view.CollapsibleActionView;
Alan Viverette3d0f21d2014-07-10 16:15:01 -070039import android.view.ContextThemeWrapper;
Adam Powelle43340c2014-03-17 19:10:43 -070040import android.view.Gravity;
41import android.view.Menu;
42import android.view.MenuInflater;
43import android.view.MenuItem;
Adam Powell61d15b92014-09-12 11:01:21 -070044import android.view.MotionEvent;
Adam Powelle43340c2014-03-17 19:10:43 -070045import android.view.View;
Adam Powelle43340c2014-03-17 19:10:43 -070046import android.view.ViewGroup;
Vadim Tryshev1abe85c2017-01-06 19:30:00 -080047import android.view.ViewParent;
Ashley Rose55f9f922019-01-28 19:29:36 -050048import android.view.inspector.InspectableProperty;
Alan Viverette3d0f21d2014-07-10 16:15:01 -070049
Adam Powelle43340c2014-03-17 19:10:43 -070050import com.android.internal.R;
Adam Powelle021e6e2014-05-23 17:27:24 -070051import com.android.internal.view.menu.MenuBuilder;
52import com.android.internal.view.menu.MenuItemImpl;
53import com.android.internal.view.menu.MenuPresenter;
54import com.android.internal.view.menu.MenuView;
55import com.android.internal.view.menu.SubMenuBuilder;
56import com.android.internal.widget.DecorToolbar;
57import com.android.internal.widget.ToolbarWidgetWrapper;
Adam Powelle43340c2014-03-17 19:10:43 -070058
59import java.util.ArrayList;
60import java.util.List;
61
62/**
63 * A standard toolbar for use within application content.
64 *
65 * <p>A Toolbar is a generalization of {@link android.app.ActionBar action bars} for use
66 * within application layouts. While an action bar is traditionally part of an
67 * {@link android.app.Activity Activity's} opaque window decor controlled by the framework,
68 * a Toolbar may be placed at any arbitrary level of nesting within a view hierarchy.
69 * An application may choose to designate a Toolbar as the action bar for an Activity
70 * using the {@link android.app.Activity#setActionBar(Toolbar) setActionBar()} method.</p>
71 *
72 * <p>Toolbar supports a more focused feature set than ActionBar. From start to end, a toolbar
73 * may contain a combination of the following optional elements:
74 *
75 * <ul>
76 * <li><em>A navigation button.</em> This may be an Up arrow, navigation menu toggle, close,
77 * collapse, done or another glyph of the app's choosing. This button should always be used
78 * to access other navigational destinations within the container of the Toolbar and
Alan Viverette157afde2014-10-13 17:25:26 -070079 * its signified content or otherwise leave the current context signified by the Toolbar.
80 * The navigation button is vertically aligned within the Toolbar's
81 * {@link android.R.styleable#View_minHeight minimum height}, if set.</li>
Adam Powelle43340c2014-03-17 19:10:43 -070082 * <li><em>A branded logo image.</em> This may extend to the height of the bar and can be
83 * arbitrarily wide.</li>
84 * <li><em>A title and subtitle.</em> The title should be a signpost for the Toolbar's current
85 * position in the navigation hierarchy and the content contained there. The subtitle,
86 * if present should indicate any extended information about the current content.
87 * If an app uses a logo image it should strongly consider omitting a title and subtitle.</li>
88 * <li><em>One or more custom views.</em> The application may add arbitrary child views
89 * to the Toolbar. They will appear at this position within the layout. If a child view's
90 * {@link LayoutParams} indicates a {@link Gravity} value of
91 * {@link Gravity#CENTER_HORIZONTAL CENTER_HORIZONTAL} the view will attempt to center
92 * within the available space remaining in the Toolbar after all other elements have been
93 * measured.</li>
94 * <li><em>An {@link ActionMenuView action menu}.</em> The menu of actions will pin to the
95 * end of the Toolbar offering a few
96 * <a href="http://developer.android.com/design/patterns/actionbar.html#ActionButtons">
Alan Viverette157afde2014-10-13 17:25:26 -070097 * frequent, important or typical</a> actions along with an optional overflow menu for
98 * additional actions. Action buttons are vertically aligned within the Toolbar's
99 * {@link android.R.styleable#View_minHeight minimum height}, if set.</li>
Adam Powelle43340c2014-03-17 19:10:43 -0700100 * </ul>
101 * </p>
102 *
103 * <p>In modern Android UIs developers should lean more on a visually distinct color scheme for
104 * toolbars than on their application icon. The use of application icon plus title as a standard
105 * layout is discouraged on API 21 devices and newer.</p>
Alan Viverette4db9d1b2015-09-15 16:21:16 -0400106 *
107 * @attr ref android.R.styleable#Toolbar_buttonGravity
108 * @attr ref android.R.styleable#Toolbar_collapseContentDescription
109 * @attr ref android.R.styleable#Toolbar_collapseIcon
110 * @attr ref android.R.styleable#Toolbar_contentInsetEnd
111 * @attr ref android.R.styleable#Toolbar_contentInsetLeft
112 * @attr ref android.R.styleable#Toolbar_contentInsetRight
113 * @attr ref android.R.styleable#Toolbar_contentInsetStart
Adam Powell679be2d2016-03-28 09:38:41 -0700114 * @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation
115 * @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions
Alan Viverette4db9d1b2015-09-15 16:21:16 -0400116 * @attr ref android.R.styleable#Toolbar_gravity
117 * @attr ref android.R.styleable#Toolbar_logo
118 * @attr ref android.R.styleable#Toolbar_logoDescription
119 * @attr ref android.R.styleable#Toolbar_maxButtonHeight
120 * @attr ref android.R.styleable#Toolbar_navigationContentDescription
121 * @attr ref android.R.styleable#Toolbar_navigationIcon
122 * @attr ref android.R.styleable#Toolbar_popupTheme
123 * @attr ref android.R.styleable#Toolbar_subtitle
124 * @attr ref android.R.styleable#Toolbar_subtitleTextAppearance
125 * @attr ref android.R.styleable#Toolbar_subtitleTextColor
126 * @attr ref android.R.styleable#Toolbar_title
127 * @attr ref android.R.styleable#Toolbar_titleMargin
128 * @attr ref android.R.styleable#Toolbar_titleMarginBottom
129 * @attr ref android.R.styleable#Toolbar_titleMarginEnd
130 * @attr ref android.R.styleable#Toolbar_titleMarginStart
131 * @attr ref android.R.styleable#Toolbar_titleMarginTop
132 * @attr ref android.R.styleable#Toolbar_titleTextAppearance
133 * @attr ref android.R.styleable#Toolbar_titleTextColor
Adam Powelle43340c2014-03-17 19:10:43 -0700134 */
135public class Toolbar extends ViewGroup {
Adam Powelle021e6e2014-05-23 17:27:24 -0700136 private static final String TAG = "Toolbar";
137
Adam Powelle43340c2014-03-17 19:10:43 -0700138 private ActionMenuView mMenuView;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100139 @UnsupportedAppUsage
Adam Powelle43340c2014-03-17 19:10:43 -0700140 private TextView mTitleTextView;
141 private TextView mSubtitleTextView;
Mathew Inwood8c854f82018-09-14 12:35:36 +0100142 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Adam Powelle43340c2014-03-17 19:10:43 -0700143 private ImageButton mNavButtonView;
144 private ImageView mLogoView;
145
Adam Powelle021e6e2014-05-23 17:27:24 -0700146 private Drawable mCollapseIcon;
Alan Viverette85085342014-10-28 16:42:24 -0700147 private CharSequence mCollapseDescription;
Adam Powelle021e6e2014-05-23 17:27:24 -0700148 private ImageButton mCollapseButtonView;
149 View mExpandedActionView;
150
Alan Viverette3d0f21d2014-07-10 16:15:01 -0700151 /** Context against which to inflate popup menus. */
152 private Context mPopupContext;
153
154 /** Theme resource against which to inflate popup menus. */
155 private int mPopupTheme;
156
Adam Powelle43340c2014-03-17 19:10:43 -0700157 private int mTitleTextAppearance;
158 private int mSubtitleTextAppearance;
Adam Powelle021e6e2014-05-23 17:27:24 -0700159 private int mNavButtonStyle;
160
161 private int mButtonGravity;
162
163 private int mMaxButtonHeight;
Adam Powell76d8f962014-05-14 20:13:47 -0700164
Mathew Inwood978c6e22018-08-21 15:58:55 +0100165 @UnsupportedAppUsage
Adam Powelle43340c2014-03-17 19:10:43 -0700166 private int mTitleMarginStart;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100167 @UnsupportedAppUsage
Adam Powelle43340c2014-03-17 19:10:43 -0700168 private int mTitleMarginEnd;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100169 @UnsupportedAppUsage
Adam Powelle43340c2014-03-17 19:10:43 -0700170 private int mTitleMarginTop;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100171 @UnsupportedAppUsage
Adam Powelle43340c2014-03-17 19:10:43 -0700172 private int mTitleMarginBottom;
173
Chris Banes3b925c12016-06-20 10:38:02 +0100174 private RtlSpacingHelper mContentInsets;
Adam Powell679be2d2016-03-28 09:38:41 -0700175 private int mContentInsetStartWithNavigation;
176 private int mContentInsetEndWithActions;
Adam Powell76d8f962014-05-14 20:13:47 -0700177
Adam Powelle43340c2014-03-17 19:10:43 -0700178 private int mGravity = Gravity.START | Gravity.CENTER_VERTICAL;
179
180 private CharSequence mTitleText;
181 private CharSequence mSubtitleText;
182
Adam Powell1a264ce2014-06-23 18:16:44 -0700183 private int mTitleTextColor;
184 private int mSubtitleTextColor;
185
Adam Powell61d15b92014-09-12 11:01:21 -0700186 private boolean mEatingTouch;
187
Adam Powelle43340c2014-03-17 19:10:43 -0700188 // Clear me after use.
189 private final ArrayList<View> mTempViews = new ArrayList<View>();
190
Adam Powell5f622842015-06-15 14:14:42 -0700191 // Used to hold views that will be removed while we have an expanded action view.
192 private final ArrayList<View> mHiddenViews = new ArrayList<>();
193
Adam Powell7a324012014-05-30 18:52:37 -0700194 private final int[] mTempMargins = new int[2];
195
Adam Powelle43340c2014-03-17 19:10:43 -0700196 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
Adam Powelle021e6e2014-05-23 17:27:24 -0700209 private ToolbarWidgetWrapper mWrapper;
210 private ActionMenuPresenter mOuterActionMenuPresenter;
211 private ExpandedActionViewMenuPresenter mExpandedMenuPresenter;
Adam Powell04c0d462014-09-04 16:10:24 -0700212 private MenuPresenter.Callback mActionMenuPresenterCallback;
Adam Powellc4612502014-09-04 19:16:39 -0700213 private MenuBuilder.Callback mMenuBuilderCallback;
Adam Powelle021e6e2014-05-23 17:27:24 -0700214
Adam Powell360c1d82014-06-11 19:31:41 -0700215 private boolean mCollapsible;
216
Adam Powell31c91c82014-08-22 17:20:00 -0700217 private final Runnable mShowOverflowMenuRunnable = new Runnable() {
218 @Override public void run() {
219 showOverflowMenu();
220 }
221 };
222
Adam Powelle43340c2014-03-17 19:10:43 -0700223 public Toolbar(Context context) {
224 this(context, null);
225 }
226
227 public Toolbar(Context context, AttributeSet attrs) {
228 this(context, attrs, com.android.internal.R.attr.toolbarStyle);
229 }
230
231 public Toolbar(Context context, AttributeSet attrs, int defStyleAttr) {
232 this(context, attrs, defStyleAttr, 0);
233 }
234
235 public Toolbar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
236 super(context, attrs, defStyleAttr, defStyleRes);
237
238 final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Toolbar,
239 defStyleAttr, defStyleRes);
Aurimas Liutikasab324cf2019-02-07 16:46:38 -0800240 saveAttributeDataForStyleable(context, R.styleable.Toolbar,
241 attrs, a, defStyleAttr, defStyleRes);
Adam Powelle43340c2014-03-17 19:10:43 -0700242
243 mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0);
244 mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0);
Adam Powelle021e6e2014-05-23 17:27:24 -0700245 mNavButtonStyle = a.getResourceId(R.styleable.Toolbar_navigationButtonStyle, 0);
Adam Powelle43340c2014-03-17 19:10:43 -0700246 mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity);
Adam Powelle021e6e2014-05-23 17:27:24 -0700247 mButtonGravity = a.getInteger(R.styleable.Toolbar_buttonGravity, Gravity.TOP);
Adam Powell76d8f962014-05-14 20:13:47 -0700248 mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom =
Alan Viverette4db9d1b2015-09-15 16:21:16 -0400249 a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargin, 0);
Adam Powelle43340c2014-03-17 19:10:43 -0700250
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
Adam Powelle021e6e2014-05-23 17:27:24 -0700272 mMaxButtonHeight = a.getDimensionPixelSize(R.styleable.Toolbar_maxButtonHeight, -1);
273
Adam Powell76d8f962014-05-14 20:13:47 -0700274 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
Chris Banes3b925c12016-06-20 10:38:02 +0100285 ensureContentInsets();
Adam Powell76d8f962014-05-14 20:13:47 -0700286 mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight);
287
288 if (contentInsetStart != RtlSpacingHelper.UNDEFINED ||
289 contentInsetEnd != RtlSpacingHelper.UNDEFINED) {
290 mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
291 }
292
Adam Powell679be2d2016-03-28 09:38:41 -0700293 mContentInsetStartWithNavigation = a.getDimensionPixelOffset(
294 R.styleable.Toolbar_contentInsetStartWithNavigation, RtlSpacingHelper.UNDEFINED);
295 mContentInsetEndWithActions = a.getDimensionPixelOffset(
296 R.styleable.Toolbar_contentInsetEndWithActions, RtlSpacingHelper.UNDEFINED);
297
Adam Powelle021e6e2014-05-23 17:27:24 -0700298 mCollapseIcon = a.getDrawable(R.styleable.Toolbar_collapseIcon);
Alan Viverette85085342014-10-28 16:42:24 -0700299 mCollapseDescription = a.getText(R.styleable.Toolbar_collapseContentDescription);
Adam Powelle021e6e2014-05-23 17:27:24 -0700300
Adam Powelle43340c2014-03-17 19:10:43 -0700301 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)) {
Adam Powell07a74542014-05-30 15:52:44 -0700308 setSubtitle(subtitle);
Adam Powelle43340c2014-03-17 19:10:43 -0700309 }
Alan Viverette3d0f21d2014-07-10 16:15:01 -0700310
Alan Viverette77e50372014-07-22 17:12:56 -0700311 // Set the default context, since setPopupTheme() may be a no-op.
312 mPopupContext = mContext;
Alan Viverette22a117d2014-07-21 19:40:24 -0700313 setPopupTheme(a.getResourceId(R.styleable.Toolbar_popupTheme, 0));
Adam Powell7c13bc22014-08-28 18:19:59 -0700314
315 final Drawable navIcon = a.getDrawable(R.styleable.Toolbar_navigationIcon);
316 if (navIcon != null) {
317 setNavigationIcon(navIcon);
Adam Powell12b03212014-09-02 15:59:28 -0700318 }
319
320 final CharSequence navDesc = a.getText(
321 R.styleable.Toolbar_navigationContentDescription);
322 if (!TextUtils.isEmpty(navDesc)) {
323 setNavigationContentDescription(navDesc);
Adam Powell7c13bc22014-08-28 18:19:59 -0700324 }
Adam Powell6b3fc9a2015-06-16 15:33:48 -0700325
326 final Drawable logo = a.getDrawable(R.styleable.Toolbar_logo);
327 if (logo != null) {
328 setLogo(logo);
329 }
330
331 final CharSequence logoDesc = a.getText(R.styleable.Toolbar_logoDescription);
332 if (!TextUtils.isEmpty(logoDesc)) {
333 setLogoDescription(logoDesc);
334 }
335
336 if (a.hasValue(R.styleable.Toolbar_titleTextColor)) {
337 setTitleTextColor(a.getColor(R.styleable.Toolbar_titleTextColor, 0xffffffff));
338 }
339
340 if (a.hasValue(R.styleable.Toolbar_subtitleTextColor)) {
341 setSubtitleTextColor(a.getColor(R.styleable.Toolbar_subtitleTextColor, 0xffffffff));
342 }
Alan Viverette22a117d2014-07-21 19:40:24 -0700343 a.recycle();
Alan Viverette3d0f21d2014-07-10 16:15:01 -0700344 }
345
Vadim Tryshev1abe85c2017-01-06 19:30:00 -0800346 @Override
347 protected void onAttachedToWindow() {
348 super.onAttachedToWindow();
349
350 // If the container is a cluster, unmark itself as a cluster to avoid having nested
351 // clusters.
352 ViewParent parent = getParent();
353 while (parent != null && parent instanceof ViewGroup) {
354 final ViewGroup vgParent = (ViewGroup) parent;
355 if (vgParent.isKeyboardNavigationCluster()) {
356 setKeyboardNavigationCluster(false);
Evan Roskyaee802f2017-02-15 13:26:51 -0800357 if (vgParent.getTouchscreenBlocksFocus()) {
358 setTouchscreenBlocksFocus(false);
359 }
Vadim Tryshev1abe85c2017-01-06 19:30:00 -0800360 break;
361 }
362 parent = vgParent.getParent();
363 }
364 }
365
Alan Viverette3d0f21d2014-07-10 16:15:01 -0700366 /**
367 * Specifies the theme to use when inflating popup menus. By default, uses
368 * the same theme as the toolbar itself.
369 *
370 * @param resId theme used to inflate popup menus
371 * @see #getPopupTheme()
372 */
Tor Norbye417ee5b2015-03-10 20:57:37 -0700373 public void setPopupTheme(@StyleRes int resId) {
Alan Viverette3d0f21d2014-07-10 16:15:01 -0700374 if (mPopupTheme != resId) {
375 mPopupTheme = resId;
376 if (resId == 0) {
377 mPopupContext = mContext;
378 } else {
379 mPopupContext = new ContextThemeWrapper(mContext, resId);
380 }
381 }
382 }
383
384 /**
385 * @return resource identifier of the theme used to inflate popup menus, or
386 * 0 if menus are inflated against the toolbar theme
387 * @see #setPopupTheme(int)
388 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500389 @InspectableProperty
Alan Viverette3d0f21d2014-07-10 16:15:01 -0700390 public int getPopupTheme() {
391 return mPopupTheme;
Adam Powelle43340c2014-03-17 19:10:43 -0700392 }
393
Alan Viverette4db9d1b2015-09-15 16:21:16 -0400394 /**
395 * Sets the title margin.
396 *
397 * @param start the starting title margin in pixels
398 * @param top the top title margin in pixels
399 * @param end the ending title margin in pixels
400 * @param bottom the bottom title margin in pixels
401 * @see #getTitleMarginStart()
402 * @see #getTitleMarginTop()
403 * @see #getTitleMarginEnd()
404 * @see #getTitleMarginBottom()
405 * @attr ref android.R.styleable#Toolbar_titleMargin
406 */
407 public void setTitleMargin(int start, int top, int end, int bottom) {
408 mTitleMarginStart = start;
409 mTitleMarginTop = top;
410 mTitleMarginEnd = end;
411 mTitleMarginBottom = bottom;
412
413 requestLayout();
414 }
415
416 /**
417 * @return the starting title margin in pixels
418 * @see #setTitleMarginStart(int)
419 * @attr ref android.R.styleable#Toolbar_titleMarginStart
420 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500421 @InspectableProperty
Alan Viverette4db9d1b2015-09-15 16:21:16 -0400422 public int getTitleMarginStart() {
423 return mTitleMarginStart;
424 }
425
426 /**
427 * Sets the starting title margin in pixels.
428 *
429 * @param margin the starting title margin in pixels
430 * @see #getTitleMarginStart()
431 * @attr ref android.R.styleable#Toolbar_titleMarginStart
432 */
433 public void setTitleMarginStart(int margin) {
434 mTitleMarginStart = margin;
435
436 requestLayout();
437 }
438
439 /**
440 * @return the top title margin in pixels
441 * @see #setTitleMarginTop(int)
442 * @attr ref android.R.styleable#Toolbar_titleMarginTop
443 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500444 @InspectableProperty
Alan Viverette4db9d1b2015-09-15 16:21:16 -0400445 public int getTitleMarginTop() {
446 return mTitleMarginTop;
447 }
448
449 /**
450 * Sets the top title margin in pixels.
451 *
452 * @param margin the top title margin in pixels
453 * @see #getTitleMarginTop()
454 * @attr ref android.R.styleable#Toolbar_titleMarginTop
455 */
456 public void setTitleMarginTop(int margin) {
457 mTitleMarginTop = margin;
458
459 requestLayout();
460 }
461
462 /**
463 * @return the ending title margin in pixels
464 * @see #setTitleMarginEnd(int)
465 * @attr ref android.R.styleable#Toolbar_titleMarginEnd
466 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500467 @InspectableProperty
Alan Viverette4db9d1b2015-09-15 16:21:16 -0400468 public int getTitleMarginEnd() {
469 return mTitleMarginEnd;
470 }
471
472 /**
473 * Sets the ending title margin in pixels.
474 *
475 * @param margin the ending title margin in pixels
476 * @see #getTitleMarginEnd()
477 * @attr ref android.R.styleable#Toolbar_titleMarginEnd
478 */
479 public void setTitleMarginEnd(int margin) {
480 mTitleMarginEnd = margin;
481
482 requestLayout();
483 }
484
485 /**
486 * @return the bottom title margin in pixels
487 * @see #setTitleMarginBottom(int)
488 * @attr ref android.R.styleable#Toolbar_titleMarginBottom
489 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500490 @InspectableProperty
Alan Viverette4db9d1b2015-09-15 16:21:16 -0400491 public int getTitleMarginBottom() {
492 return mTitleMarginBottom;
493 }
494
495 /**
496 * Sets the bottom title margin in pixels.
497 *
498 * @param margin the bottom title margin in pixels
499 * @see #getTitleMarginBottom()
500 * @attr ref android.R.styleable#Toolbar_titleMarginBottom
501 */
502 public void setTitleMarginBottom(int margin) {
503 mTitleMarginBottom = margin;
Alan Viverette4db9d1b2015-09-15 16:21:16 -0400504 requestLayout();
505 }
506
Adam Powell76d8f962014-05-14 20:13:47 -0700507 @Override
508 public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
509 super.onRtlPropertiesChanged(layoutDirection);
Chris Banes3b925c12016-06-20 10:38:02 +0100510 ensureContentInsets();
Adam Powell76d8f962014-05-14 20:13:47 -0700511 mContentInsets.setDirection(layoutDirection == LAYOUT_DIRECTION_RTL);
512 }
513
Adam Powelle43340c2014-03-17 19:10:43 -0700514 /**
515 * Set a logo drawable from a resource id.
516 *
517 * <p>This drawable should generally take the place of title text. The logo cannot be
518 * clicked. Apps using a logo should also supply a description using
519 * {@link #setLogoDescription(int)}.</p>
520 *
521 * @param resId ID of a drawable resource
522 */
Tor Norbye417ee5b2015-03-10 20:57:37 -0700523 public void setLogo(@DrawableRes int resId) {
Adam Powelle43340c2014-03-17 19:10:43 -0700524 setLogo(getContext().getDrawable(resId));
525 }
526
Adam Powelle021e6e2014-05-23 17:27:24 -0700527 /** @hide */
528 public boolean canShowOverflowMenu() {
529 return getVisibility() == VISIBLE && mMenuView != null && mMenuView.isOverflowReserved();
530 }
531
532 /**
533 * Check whether the overflow menu is currently showing. This may not reflect
534 * a pending show operation in progress.
535 *
536 * @return true if the overflow menu is currently showing
537 */
538 public boolean isOverflowMenuShowing() {
539 return mMenuView != null && mMenuView.isOverflowMenuShowing();
540 }
541
542 /** @hide */
543 public boolean isOverflowMenuShowPending() {
544 return mMenuView != null && mMenuView.isOverflowMenuShowPending();
545 }
546
547 /**
548 * Show the overflow items from the associated menu.
549 *
550 * @return true if the menu was able to be shown, false otherwise
551 */
552 public boolean showOverflowMenu() {
553 return mMenuView != null && mMenuView.showOverflowMenu();
554 }
555
556 /**
557 * Hide the overflow items from the associated menu.
558 *
559 * @return true if the menu was able to be hidden, false otherwise
560 */
561 public boolean hideOverflowMenu() {
562 return mMenuView != null && mMenuView.hideOverflowMenu();
563 }
564
565 /** @hide */
566 public void setMenu(MenuBuilder menu, ActionMenuPresenter outerPresenter) {
567 if (menu == null && mMenuView == null) {
568 return;
569 }
570
571 ensureMenuView();
572 final MenuBuilder oldMenu = mMenuView.peekMenu();
573 if (oldMenu == menu) {
574 return;
575 }
576
577 if (oldMenu != null) {
578 oldMenu.removeMenuPresenter(mOuterActionMenuPresenter);
579 oldMenu.removeMenuPresenter(mExpandedMenuPresenter);
580 }
581
Adam Powelle021e6e2014-05-23 17:27:24 -0700582 if (mExpandedMenuPresenter == null) {
583 mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
584 }
585
586 outerPresenter.setExpandedActionViewsExclusive(true);
587 if (menu != null) {
Alan Viverette3d0f21d2014-07-10 16:15:01 -0700588 menu.addMenuPresenter(outerPresenter, mPopupContext);
589 menu.addMenuPresenter(mExpandedMenuPresenter, mPopupContext);
Adam Powelle021e6e2014-05-23 17:27:24 -0700590 } else {
Alan Viverette3d0f21d2014-07-10 16:15:01 -0700591 outerPresenter.initForMenu(mPopupContext, null);
592 mExpandedMenuPresenter.initForMenu(mPopupContext, null);
Adam Powelle021e6e2014-05-23 17:27:24 -0700593 outerPresenter.updateMenuView(true);
594 mExpandedMenuPresenter.updateMenuView(true);
595 }
Alan Viverette3d0f21d2014-07-10 16:15:01 -0700596 mMenuView.setPopupTheme(mPopupTheme);
Adam Powelle021e6e2014-05-23 17:27:24 -0700597 mMenuView.setPresenter(outerPresenter);
598 mOuterActionMenuPresenter = outerPresenter;
599 }
600
601 /**
602 * Dismiss all currently showing popup menus, including overflow or submenus.
603 */
604 public void dismissPopupMenus() {
605 if (mMenuView != null) {
606 mMenuView.dismissPopupMenus();
607 }
608 }
609
610 /** @hide */
611 public boolean isTitleTruncated() {
612 if (mTitleTextView == null) {
613 return false;
614 }
615
616 final Layout titleLayout = mTitleTextView.getLayout();
617 if (titleLayout == null) {
618 return false;
619 }
620
621 final int lineCount = titleLayout.getLineCount();
622 for (int i = 0; i < lineCount; i++) {
623 if (titleLayout.getEllipsisCount(i) > 0) {
624 return true;
625 }
626 }
627 return false;
628 }
629
Adam Powelle43340c2014-03-17 19:10:43 -0700630 /**
631 * Set a logo drawable.
632 *
633 * <p>This drawable should generally take the place of title text. The logo cannot be
634 * clicked. Apps using a logo should also supply a description using
635 * {@link #setLogoDescription(int)}.</p>
636 *
637 * @param drawable Drawable to use as a logo
638 */
639 public void setLogo(Drawable drawable) {
640 if (drawable != null) {
Adam Powelle021e6e2014-05-23 17:27:24 -0700641 ensureLogoView();
Adam Powell5f622842015-06-15 14:14:42 -0700642 if (!isChildOrHidden(mLogoView)) {
643 addSystemView(mLogoView, true);
Adam Powelle43340c2014-03-17 19:10:43 -0700644 }
Adam Powell5f622842015-06-15 14:14:42 -0700645 } else if (mLogoView != null && isChildOrHidden(mLogoView)) {
Adam Powelle43340c2014-03-17 19:10:43 -0700646 removeView(mLogoView);
Adam Powell5f622842015-06-15 14:14:42 -0700647 mHiddenViews.remove(mLogoView);
Adam Powelle43340c2014-03-17 19:10:43 -0700648 }
649 if (mLogoView != null) {
650 mLogoView.setImageDrawable(drawable);
651 }
652 }
653
654 /**
655 * Return the current logo drawable.
656 *
657 * @return The current logo drawable
658 * @see #setLogo(int)
659 * @see #setLogo(android.graphics.drawable.Drawable)
660 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500661 @InspectableProperty
Adam Powelle43340c2014-03-17 19:10:43 -0700662 public Drawable getLogo() {
663 return mLogoView != null ? mLogoView.getDrawable() : null;
664 }
665
666 /**
667 * Set a description of the toolbar's logo.
668 *
669 * <p>This description will be used for accessibility or other similar descriptions
670 * of the UI.</p>
671 *
672 * @param resId String resource id
673 */
Tor Norbye417ee5b2015-03-10 20:57:37 -0700674 public void setLogoDescription(@StringRes int resId) {
Adam Powelle43340c2014-03-17 19:10:43 -0700675 setLogoDescription(getContext().getText(resId));
676 }
677
678 /**
679 * Set a description of the toolbar's logo.
680 *
681 * <p>This description will be used for accessibility or other similar descriptions
682 * of the UI.</p>
683 *
684 * @param description Description to set
685 */
686 public void setLogoDescription(CharSequence description) {
Adam Powelle021e6e2014-05-23 17:27:24 -0700687 if (!TextUtils.isEmpty(description)) {
688 ensureLogoView();
Adam Powelle43340c2014-03-17 19:10:43 -0700689 }
690 if (mLogoView != null) {
691 mLogoView.setContentDescription(description);
692 }
693 }
694
695 /**
696 * Return the description of the toolbar's logo.
697 *
698 * @return A description of the logo
699 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500700 @InspectableProperty
Adam Powelle43340c2014-03-17 19:10:43 -0700701 public CharSequence getLogoDescription() {
702 return mLogoView != null ? mLogoView.getContentDescription() : null;
703 }
704
Adam Powelle021e6e2014-05-23 17:27:24 -0700705 private void ensureLogoView() {
706 if (mLogoView == null) {
707 mLogoView = new ImageView(getContext());
708 }
709 }
710
Adam Powelle43340c2014-03-17 19:10:43 -0700711 /**
Adam Powelle021e6e2014-05-23 17:27:24 -0700712 * Check whether this Toolbar is currently hosting an expanded action view.
Adam Powelle43340c2014-03-17 19:10:43 -0700713 *
Adam Powelle021e6e2014-05-23 17:27:24 -0700714 * <p>An action view may be expanded either directly from the
715 * {@link android.view.MenuItem MenuItem} it belongs to or by user action. If the Toolbar
716 * has an expanded action view it can be collapsed using the {@link #collapseActionView()}
717 * method.</p>
718 *
719 * @return true if the Toolbar has an expanded action view
720 */
721 public boolean hasExpandedActionView() {
722 return mExpandedMenuPresenter != null &&
723 mExpandedMenuPresenter.mCurrentExpandedItem != null;
724 }
725
726 /**
727 * Collapse a currently expanded action view. If this Toolbar does not have an
728 * expanded action view this method has no effect.
729 *
730 * <p>An action view may be expanded either directly from the
731 * {@link android.view.MenuItem MenuItem} it belongs to or by user action.</p>
732 *
733 * @see #hasExpandedActionView()
734 */
735 public void collapseActionView() {
736 final MenuItemImpl item = mExpandedMenuPresenter == null ? null :
737 mExpandedMenuPresenter.mCurrentExpandedItem;
738 if (item != null) {
739 item.collapseActionView();
740 }
741 }
742
743 /**
744 * Returns the title of this toolbar.
745 *
746 * @return The current title.
Adam Powelle43340c2014-03-17 19:10:43 -0700747 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500748 @InspectableProperty
Adam Powelle43340c2014-03-17 19:10:43 -0700749 public CharSequence getTitle() {
750 return mTitleText;
751 }
752
753 /**
754 * Set the title of this toolbar.
755 *
756 * <p>A title should be used as the anchor for a section of content. It should
757 * describe or name the content being viewed.</p>
758 *
759 * @param resId Resource ID of a string to set as the title
760 */
Tor Norbye417ee5b2015-03-10 20:57:37 -0700761 public void setTitle(@StringRes int resId) {
Adam Powelle43340c2014-03-17 19:10:43 -0700762 setTitle(getContext().getText(resId));
763 }
764
765 /**
766 * Set the title of this toolbar.
767 *
768 * <p>A title should be used as the anchor for a section of content. It should
769 * describe or name the content being viewed.</p>
770 *
771 * @param title Title to set
772 */
773 public void setTitle(CharSequence title) {
774 if (!TextUtils.isEmpty(title)) {
775 if (mTitleTextView == null) {
776 final Context context = getContext();
777 mTitleTextView = new TextView(context);
Adam Powelle021e6e2014-05-23 17:27:24 -0700778 mTitleTextView.setSingleLine();
779 mTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
Adam Powell1a264ce2014-06-23 18:16:44 -0700780 if (mTitleTextAppearance != 0) {
Alan Viverettebb98ebd2015-05-08 17:17:44 -0700781 mTitleTextView.setTextAppearance(mTitleTextAppearance);
Adam Powell1a264ce2014-06-23 18:16:44 -0700782 }
783 if (mTitleTextColor != 0) {
784 mTitleTextView.setTextColor(mTitleTextColor);
785 }
Adam Powelle43340c2014-03-17 19:10:43 -0700786 }
Adam Powell5f622842015-06-15 14:14:42 -0700787 if (!isChildOrHidden(mTitleTextView)) {
788 addSystemView(mTitleTextView, true);
Adam Powelle43340c2014-03-17 19:10:43 -0700789 }
Adam Powell5f622842015-06-15 14:14:42 -0700790 } else if (mTitleTextView != null && isChildOrHidden(mTitleTextView)) {
Adam Powelle43340c2014-03-17 19:10:43 -0700791 removeView(mTitleTextView);
Adam Powell5f622842015-06-15 14:14:42 -0700792 mHiddenViews.remove(mTitleTextView);
Adam Powelle43340c2014-03-17 19:10:43 -0700793 }
794 if (mTitleTextView != null) {
795 mTitleTextView.setText(title);
796 }
797 mTitleText = title;
798 }
799
800 /**
801 * Return the subtitle of this toolbar.
802 *
803 * @return The current subtitle
804 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500805 @InspectableProperty
Adam Powelle43340c2014-03-17 19:10:43 -0700806 public CharSequence getSubtitle() {
807 return mSubtitleText;
808 }
809
810 /**
811 * Set the subtitle of this toolbar.
812 *
813 * <p>Subtitles should express extended information about the current content.</p>
814 *
815 * @param resId String resource ID
816 */
Tor Norbye417ee5b2015-03-10 20:57:37 -0700817 public void setSubtitle(@StringRes int resId) {
Adam Powelle43340c2014-03-17 19:10:43 -0700818 setSubtitle(getContext().getText(resId));
819 }
820
821 /**
822 * Set the subtitle of this toolbar.
823 *
824 * <p>Subtitles should express extended information about the current content.</p>
825 *
826 * @param subtitle Subtitle to set
827 */
828 public void setSubtitle(CharSequence subtitle) {
829 if (!TextUtils.isEmpty(subtitle)) {
830 if (mSubtitleTextView == null) {
831 final Context context = getContext();
832 mSubtitleTextView = new TextView(context);
Adam Powelle021e6e2014-05-23 17:27:24 -0700833 mSubtitleTextView.setSingleLine();
834 mSubtitleTextView.setEllipsize(TextUtils.TruncateAt.END);
Adam Powell1a264ce2014-06-23 18:16:44 -0700835 if (mSubtitleTextAppearance != 0) {
Alan Viverettebb98ebd2015-05-08 17:17:44 -0700836 mSubtitleTextView.setTextAppearance(mSubtitleTextAppearance);
Adam Powell1a264ce2014-06-23 18:16:44 -0700837 }
838 if (mSubtitleTextColor != 0) {
839 mSubtitleTextView.setTextColor(mSubtitleTextColor);
840 }
Adam Powelle43340c2014-03-17 19:10:43 -0700841 }
Adam Powell5f622842015-06-15 14:14:42 -0700842 if (!isChildOrHidden(mSubtitleTextView)) {
843 addSystemView(mSubtitleTextView, true);
Adam Powelle43340c2014-03-17 19:10:43 -0700844 }
Adam Powell5f622842015-06-15 14:14:42 -0700845 } else if (mSubtitleTextView != null && isChildOrHidden(mSubtitleTextView)) {
Adam Powelle43340c2014-03-17 19:10:43 -0700846 removeView(mSubtitleTextView);
Adam Powell5f622842015-06-15 14:14:42 -0700847 mHiddenViews.remove(mSubtitleTextView);
Adam Powelle43340c2014-03-17 19:10:43 -0700848 }
849 if (mSubtitleTextView != null) {
850 mSubtitleTextView.setText(subtitle);
851 }
852 mSubtitleText = subtitle;
853 }
854
855 /**
Adam Powellebba5d42014-05-30 16:19:16 -0700856 * Sets the text color, size, style, hint color, and highlight color
857 * from the specified TextAppearance resource.
858 */
Tor Norbye417ee5b2015-03-10 20:57:37 -0700859 public void setTitleTextAppearance(Context context, @StyleRes int resId) {
Adam Powellebba5d42014-05-30 16:19:16 -0700860 mTitleTextAppearance = resId;
861 if (mTitleTextView != null) {
Alan Viverettebb98ebd2015-05-08 17:17:44 -0700862 mTitleTextView.setTextAppearance(resId);
Adam Powellebba5d42014-05-30 16:19:16 -0700863 }
864 }
865
866 /**
867 * Sets the text color, size, style, hint color, and highlight color
868 * from the specified TextAppearance resource.
869 */
Tor Norbye417ee5b2015-03-10 20:57:37 -0700870 public void setSubtitleTextAppearance(Context context, @StyleRes int resId) {
Adam Powellebba5d42014-05-30 16:19:16 -0700871 mSubtitleTextAppearance = resId;
872 if (mSubtitleTextView != null) {
Alan Viverettebb98ebd2015-05-08 17:17:44 -0700873 mSubtitleTextView.setTextAppearance(resId);
Adam Powellebba5d42014-05-30 16:19:16 -0700874 }
875 }
876
877 /**
Adam Powell1a264ce2014-06-23 18:16:44 -0700878 * Sets the text color of the title, if present.
879 *
880 * @param color The new text color in 0xAARRGGBB format
881 */
Tor Norbye80756e32015-03-02 09:39:27 -0800882 public void setTitleTextColor(@ColorInt int color) {
Adam Powell1a264ce2014-06-23 18:16:44 -0700883 mTitleTextColor = color;
884 if (mTitleTextView != null) {
885 mTitleTextView.setTextColor(color);
886 }
887 }
888
889 /**
890 * Sets the text color of the subtitle, if present.
891 *
892 * @param color The new text color in 0xAARRGGBB format
893 */
Tor Norbye80756e32015-03-02 09:39:27 -0800894 public void setSubtitleTextColor(@ColorInt int color) {
Adam Powell1a264ce2014-06-23 18:16:44 -0700895 mSubtitleTextColor = color;
896 if (mSubtitleTextView != null) {
897 mSubtitleTextView.setTextColor(color);
898 }
899 }
900
901 /**
Adam Powell7901b2a2014-06-24 17:18:52 -0700902 * Retrieve the currently configured content description for the navigation button view.
903 * This will be used to describe the navigation action to users through mechanisms such
904 * as screen readers or tooltips.
905 *
906 * @return The navigation button's content description
Adam Powelld7602492014-08-28 19:35:36 -0700907 *
908 * @attr ref android.R.styleable#Toolbar_navigationContentDescription
Adam Powell7901b2a2014-06-24 17:18:52 -0700909 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500910 @InspectableProperty
Alan Viverettee88700a2014-07-25 12:57:31 -0700911 @Nullable
Adam Powell7901b2a2014-06-24 17:18:52 -0700912 public CharSequence getNavigationContentDescription() {
913 return mNavButtonView != null ? mNavButtonView.getContentDescription() : null;
914 }
915
916 /**
Adam Powelle021e6e2014-05-23 17:27:24 -0700917 * Set a content description for the navigation button if one is present. The content
918 * description will be read via screen readers or other accessibility systems to explain
919 * the action of the navigation button.
920 *
Alan Viverettee88700a2014-07-25 12:57:31 -0700921 * @param resId Resource ID of a content description string to set, or 0 to
922 * clear the description
Adam Powelld7602492014-08-28 19:35:36 -0700923 *
924 * @attr ref android.R.styleable#Toolbar_navigationContentDescription
Adam Powelle021e6e2014-05-23 17:27:24 -0700925 */
Tor Norbye417ee5b2015-03-10 20:57:37 -0700926 public void setNavigationContentDescription(@StringRes int resId) {
Alan Viverettee88700a2014-07-25 12:57:31 -0700927 setNavigationContentDescription(resId != 0 ? getContext().getText(resId) : null);
Adam Powelle021e6e2014-05-23 17:27:24 -0700928 }
929
930 /**
931 * Set a content description for the navigation button if one is present. The content
932 * description will be read via screen readers or other accessibility systems to explain
933 * the action of the navigation button.
934 *
Alan Viverettee88700a2014-07-25 12:57:31 -0700935 * @param description Content description to set, or <code>null</code> to
936 * clear the content description
Adam Powelld7602492014-08-28 19:35:36 -0700937 *
938 * @attr ref android.R.styleable#Toolbar_navigationContentDescription
Adam Powelle021e6e2014-05-23 17:27:24 -0700939 */
Alan Viverettee88700a2014-07-25 12:57:31 -0700940 public void setNavigationContentDescription(@Nullable CharSequence description) {
941 if (!TextUtils.isEmpty(description)) {
942 ensureNavButtonView();
943 }
944 if (mNavButtonView != null) {
945 mNavButtonView.setContentDescription(description);
946 }
Adam Powelle021e6e2014-05-23 17:27:24 -0700947 }
948
949 /**
Adam Powelle43340c2014-03-17 19:10:43 -0700950 * Set the icon to use for the toolbar's navigation button.
951 *
952 * <p>The navigation button appears at the start of the toolbar if present. Setting an icon
953 * will make the navigation button visible.</p>
954 *
955 * <p>If you use a navigation icon you should also set a description for its action using
Alan Viverettee88700a2014-07-25 12:57:31 -0700956 * {@link #setNavigationContentDescription(int)}. This is used for accessibility and
957 * tooltips.</p>
Adam Powelle43340c2014-03-17 19:10:43 -0700958 *
Alan Viverettee88700a2014-07-25 12:57:31 -0700959 * @param resId Resource ID of a drawable to set
Adam Powelld7602492014-08-28 19:35:36 -0700960 *
961 * @attr ref android.R.styleable#Toolbar_navigationIcon
Adam Powelle43340c2014-03-17 19:10:43 -0700962 */
Tor Norbye417ee5b2015-03-10 20:57:37 -0700963 public void setNavigationIcon(@DrawableRes int resId) {
Alan Viverettee88700a2014-07-25 12:57:31 -0700964 setNavigationIcon(getContext().getDrawable(resId));
965 }
966
967 /**
968 * Set the icon to use for the toolbar's navigation button.
969 *
970 * <p>The navigation button appears at the start of the toolbar if present. Setting an icon
971 * will make the navigation button visible.</p>
972 *
973 * <p>If you use a navigation icon you should also set a description for its action using
974 * {@link #setNavigationContentDescription(int)}. This is used for accessibility and
975 * tooltips.</p>
976 *
977 * @param icon Drawable to set, may be null to clear the icon
Adam Powelld7602492014-08-28 19:35:36 -0700978 *
979 * @attr ref android.R.styleable#Toolbar_navigationIcon
Alan Viverettee88700a2014-07-25 12:57:31 -0700980 */
981 public void setNavigationIcon(@Nullable Drawable icon) {
Adam Powelle43340c2014-03-17 19:10:43 -0700982 if (icon != null) {
983 ensureNavButtonView();
Adam Powell5f622842015-06-15 14:14:42 -0700984 if (!isChildOrHidden(mNavButtonView)) {
985 addSystemView(mNavButtonView, true);
Adam Powelle43340c2014-03-17 19:10:43 -0700986 }
Adam Powell5f622842015-06-15 14:14:42 -0700987 } else if (mNavButtonView != null && isChildOrHidden(mNavButtonView)) {
Adam Powelle43340c2014-03-17 19:10:43 -0700988 removeView(mNavButtonView);
Adam Powell5f622842015-06-15 14:14:42 -0700989 mHiddenViews.remove(mNavButtonView);
Adam Powelle43340c2014-03-17 19:10:43 -0700990 }
991 if (mNavButtonView != null) {
992 mNavButtonView.setImageDrawable(icon);
993 }
994 }
995
996 /**
997 * Return the current drawable used as the navigation icon.
998 *
999 * @return The navigation icon drawable
Adam Powelld7602492014-08-28 19:35:36 -07001000 *
1001 * @attr ref android.R.styleable#Toolbar_navigationIcon
Adam Powelle43340c2014-03-17 19:10:43 -07001002 */
Ashley Rose55f9f922019-01-28 19:29:36 -05001003 @InspectableProperty
Alan Viverettee88700a2014-07-25 12:57:31 -07001004 @Nullable
Adam Powelle43340c2014-03-17 19:10:43 -07001005 public Drawable getNavigationIcon() {
1006 return mNavButtonView != null ? mNavButtonView.getDrawable() : null;
1007 }
1008
1009 /**
Adam Powelle43340c2014-03-17 19:10:43 -07001010 * Set a listener to respond to navigation events.
1011 *
1012 * <p>This listener will be called whenever the user clicks the navigation button
1013 * at the start of the toolbar. An icon must be set for the navigation button to appear.</p>
1014 *
1015 * @param listener Listener to set
1016 * @see #setNavigationIcon(android.graphics.drawable.Drawable)
1017 */
1018 public void setNavigationOnClickListener(OnClickListener listener) {
1019 ensureNavButtonView();
1020 mNavButtonView.setOnClickListener(listener);
1021 }
1022
1023 /**
Kirill Grouchnikov67960a02016-04-04 16:30:02 -04001024 * @hide
1025 */
1026 @Nullable
1027 @TestApi
1028 public View getNavigationView() {
1029 return mNavButtonView;
1030 }
1031
1032 /**
Daniel Santiago Riveraa1b06eb2018-04-26 10:31:27 -07001033 * Retrieve the currently configured content description for the collapse button view.
1034 * This will be used to describe the collapse action to users through mechanisms such
1035 * as screen readers or tooltips.
1036 *
1037 * @return The collapse button's content description
1038 *
1039 * @attr ref android.R.styleable#Toolbar_collapseContentDescription
1040 */
Ashley Rose55f9f922019-01-28 19:29:36 -05001041 @InspectableProperty
Daniel Santiago Riveraa1b06eb2018-04-26 10:31:27 -07001042 @Nullable
1043 public CharSequence getCollapseContentDescription() {
1044 return mCollapseButtonView != null ? mCollapseButtonView.getContentDescription() : null;
1045 }
1046
1047 /**
1048 * Set a content description for the collapse button if one is present. The content description
1049 * will be read via screen readers or other accessibility systems to explain the action of the
1050 * collapse button.
1051 *
1052 * @param resId Resource ID of a content description string to set, or 0 to
1053 * clear the description
1054 *
1055 * @attr ref android.R.styleable#Toolbar_collapseContentDescription
1056 */
1057 public void setCollapseContentDescription(@StringRes int resId) {
1058 setCollapseContentDescription(resId != 0 ? getContext().getText(resId) : null);
1059 }
1060
1061 /**
1062 * Set a content description for the collapse button if one is present. The content description
1063 * will be read via screen readers or other accessibility systems to explain the action of the
1064 * navigation button.
1065 *
1066 * @param description Content description to set, or <code>null</code> to
1067 * clear the content description
1068 *
1069 * @attr ref android.R.styleable#Toolbar_collapseContentDescription
1070 */
1071 public void setCollapseContentDescription(@Nullable CharSequence description) {
1072 if (!TextUtils.isEmpty(description)) {
1073 ensureCollapseButtonView();
1074 }
1075 if (mCollapseButtonView != null) {
1076 mCollapseButtonView.setContentDescription(description);
1077 }
1078 }
1079
1080 /**
1081 * Return the current drawable used as the collapse icon.
1082 *
1083 * @return The collapse icon drawable
1084 *
1085 * @attr ref android.R.styleable#Toolbar_collapseIcon
1086 */
Ashley Rose55f9f922019-01-28 19:29:36 -05001087 @InspectableProperty
Daniel Santiago Riveraa1b06eb2018-04-26 10:31:27 -07001088 @Nullable
1089 public Drawable getCollapseIcon() {
1090 return mCollapseButtonView != null ? mCollapseButtonView.getDrawable() : null;
1091 }
1092
1093 /**
1094 * Set the icon to use for the toolbar's collapse button.
1095 *
1096 * <p>The collapse button appears at the start of the toolbar when an action view is present
1097 * .</p>
1098 *
1099 * @param resId Resource ID of a drawable to set
1100 *
1101 * @attr ref android.R.styleable#Toolbar_collapseIcon
1102 */
1103 public void setCollapseIcon(@DrawableRes int resId) {
1104 setCollapseIcon(getContext().getDrawable(resId));
1105 }
1106
1107 /**
1108 * Set the icon to use for the toolbar's collapse button.
1109 *
1110 * <p>The collapse button appears at the start of the toolbar when an action view is present
1111 * .</p>
1112 *
1113 * @param icon Drawable to set, may be null to use the default icon
1114 *
1115 * @attr ref android.R.styleable#Toolbar_collapseIcon
1116 */
1117 public void setCollapseIcon(@Nullable Drawable icon) {
1118 if (icon != null) {
1119 ensureCollapseButtonView();
1120 mCollapseButtonView.setImageDrawable(icon);
1121 } else if (mCollapseButtonView != null) {
1122 mCollapseButtonView.setImageDrawable(mCollapseIcon);
1123 }
1124 }
1125
1126 /**
Adam Powelle43340c2014-03-17 19:10:43 -07001127 * Return the Menu shown in the toolbar.
1128 *
1129 * <p>Applications that wish to populate the toolbar's menu can do so from here. To use
1130 * an XML menu resource, use {@link #inflateMenu(int)}.</p>
1131 *
1132 * @return The toolbar's Menu
1133 */
1134 public Menu getMenu() {
Adam Powell07a74542014-05-30 15:52:44 -07001135 ensureMenu();
Adam Powelle021e6e2014-05-23 17:27:24 -07001136 return mMenuView.getMenu();
1137 }
1138
Chris Banesa41b7892015-06-09 13:36:44 +00001139 /**
1140 * Set the icon to use for the overflow button.
1141 *
1142 * @param icon Drawable to set, may be null to clear the icon
1143 */
1144 public void setOverflowIcon(@Nullable Drawable icon) {
1145 ensureMenu();
1146 mMenuView.setOverflowIcon(icon);
1147 }
1148
1149 /**
1150 * Return the current drawable used as the overflow icon.
1151 *
1152 * @return The overflow icon drawable
1153 */
1154 @Nullable
1155 public Drawable getOverflowIcon() {
1156 ensureMenu();
1157 return mMenuView.getOverflowIcon();
1158 }
1159
Adam Powell07a74542014-05-30 15:52:44 -07001160 private void ensureMenu() {
1161 ensureMenuView();
1162 if (mMenuView.peekMenu() == null) {
1163 // Initialize a new menu for the first time.
1164 final MenuBuilder menu = (MenuBuilder) mMenuView.getMenu();
1165 if (mExpandedMenuPresenter == null) {
1166 mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
1167 }
1168 mMenuView.setExpandedActionViewsExclusive(true);
Alan Viverette3d0f21d2014-07-10 16:15:01 -07001169 menu.addMenuPresenter(mExpandedMenuPresenter, mPopupContext);
Adam Powell07a74542014-05-30 15:52:44 -07001170 }
1171 }
1172
Adam Powelle021e6e2014-05-23 17:27:24 -07001173 private void ensureMenuView() {
Adam Powelle43340c2014-03-17 19:10:43 -07001174 if (mMenuView == null) {
1175 mMenuView = new ActionMenuView(getContext());
Alan Viverette3d0f21d2014-07-10 16:15:01 -07001176 mMenuView.setPopupTheme(mPopupTheme);
Adam Powelle43340c2014-03-17 19:10:43 -07001177 mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener);
Adam Powellc4612502014-09-04 19:16:39 -07001178 mMenuView.setMenuCallbacks(mActionMenuPresenterCallback, mMenuBuilderCallback);
Adam Powelle021e6e2014-05-23 17:27:24 -07001179 final LayoutParams lp = generateDefaultLayoutParams();
1180 lp.gravity = Gravity.END | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
1181 mMenuView.setLayoutParams(lp);
Adam Powell5f622842015-06-15 14:14:42 -07001182 addSystemView(mMenuView, false);
Adam Powelle43340c2014-03-17 19:10:43 -07001183 }
Adam Powelle43340c2014-03-17 19:10:43 -07001184 }
1185
1186 private MenuInflater getMenuInflater() {
1187 return new MenuInflater(getContext());
1188 }
1189
1190 /**
1191 * Inflate a menu resource into this toolbar.
1192 *
1193 * <p>Inflate an XML menu resource into this toolbar. Existing items in the menu will not
1194 * be modified or removed.</p>
1195 *
1196 * @param resId ID of a menu resource to inflate
1197 */
Tor Norbye417ee5b2015-03-10 20:57:37 -07001198 public void inflateMenu(@MenuRes int resId) {
Adam Powelle43340c2014-03-17 19:10:43 -07001199 getMenuInflater().inflate(resId, getMenu());
1200 }
1201
1202 /**
1203 * Set a listener to respond to menu item click events.
1204 *
1205 * <p>This listener will be invoked whenever a user selects a menu item from
1206 * the action buttons presented at the end of the toolbar or the associated overflow.</p>
1207 *
1208 * @param listener Listener to set
1209 */
1210 public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
1211 mOnMenuItemClickListener = listener;
1212 }
1213
Adam Powell76d8f962014-05-14 20:13:47 -07001214 /**
Adam Powell679be2d2016-03-28 09:38:41 -07001215 * Sets the content insets for this toolbar relative to layout direction.
Adam Powell76d8f962014-05-14 20:13:47 -07001216 *
1217 * <p>The content inset affects the valid area for Toolbar content other than
1218 * the navigation button and menu. Insets define the minimum margin for these components
1219 * and can be used to effectively align Toolbar content along well-known gridlines.</p>
1220 *
1221 * @param contentInsetStart Content inset for the toolbar starting edge
1222 * @param contentInsetEnd Content inset for the toolbar ending edge
1223 *
1224 * @see #setContentInsetsAbsolute(int, int)
1225 * @see #getContentInsetStart()
1226 * @see #getContentInsetEnd()
1227 * @see #getContentInsetLeft()
1228 * @see #getContentInsetRight()
Adam Powell679be2d2016-03-28 09:38:41 -07001229 * @attr ref android.R.styleable#Toolbar_contentInsetEnd
1230 * @attr ref android.R.styleable#Toolbar_contentInsetStart
Adam Powell76d8f962014-05-14 20:13:47 -07001231 */
1232 public void setContentInsetsRelative(int contentInsetStart, int contentInsetEnd) {
Chris Banes3b925c12016-06-20 10:38:02 +01001233 ensureContentInsets();
Adam Powell76d8f962014-05-14 20:13:47 -07001234 mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
1235 }
1236
1237 /**
Adam Powell679be2d2016-03-28 09:38:41 -07001238 * Gets the starting content inset for this toolbar.
Adam Powell76d8f962014-05-14 20:13:47 -07001239 *
1240 * <p>The content inset affects the valid area for Toolbar content other than
1241 * the navigation button and menu. Insets define the minimum margin for these components
1242 * and can be used to effectively align Toolbar content along well-known gridlines.</p>
1243 *
1244 * @return The starting content inset for this toolbar
1245 *
1246 * @see #setContentInsetsRelative(int, int)
1247 * @see #setContentInsetsAbsolute(int, int)
1248 * @see #getContentInsetEnd()
1249 * @see #getContentInsetLeft()
1250 * @see #getContentInsetRight()
Adam Powell679be2d2016-03-28 09:38:41 -07001251 * @attr ref android.R.styleable#Toolbar_contentInsetStart
Adam Powell76d8f962014-05-14 20:13:47 -07001252 */
Ashley Rose55f9f922019-01-28 19:29:36 -05001253 @InspectableProperty
Adam Powell76d8f962014-05-14 20:13:47 -07001254 public int getContentInsetStart() {
Chris Banes3b925c12016-06-20 10:38:02 +01001255 return mContentInsets != null ? mContentInsets.getStart() : 0;
Adam Powell76d8f962014-05-14 20:13:47 -07001256 }
1257
1258 /**
Adam Powell679be2d2016-03-28 09:38:41 -07001259 * Gets the ending content inset for this toolbar.
Adam Powell76d8f962014-05-14 20:13:47 -07001260 *
1261 * <p>The content inset affects the valid area for Toolbar content other than
1262 * the navigation button and menu. Insets define the minimum margin for these components
1263 * and can be used to effectively align Toolbar content along well-known gridlines.</p>
1264 *
1265 * @return The ending content inset for this toolbar
1266 *
1267 * @see #setContentInsetsRelative(int, int)
1268 * @see #setContentInsetsAbsolute(int, int)
1269 * @see #getContentInsetStart()
1270 * @see #getContentInsetLeft()
1271 * @see #getContentInsetRight()
Adam Powell679be2d2016-03-28 09:38:41 -07001272 * @attr ref android.R.styleable#Toolbar_contentInsetEnd
Adam Powell76d8f962014-05-14 20:13:47 -07001273 */
Ashley Rose55f9f922019-01-28 19:29:36 -05001274 @InspectableProperty
Adam Powell76d8f962014-05-14 20:13:47 -07001275 public int getContentInsetEnd() {
Chris Banes3b925c12016-06-20 10:38:02 +01001276 return mContentInsets != null ? mContentInsets.getEnd() : 0;
Adam Powell76d8f962014-05-14 20:13:47 -07001277 }
1278
1279 /**
Adam Powell679be2d2016-03-28 09:38:41 -07001280 * Sets the content insets for this toolbar.
Adam Powell76d8f962014-05-14 20:13:47 -07001281 *
1282 * <p>The content inset affects the valid area for Toolbar content other than
1283 * the navigation button and menu. Insets define the minimum margin for these components
1284 * and can be used to effectively align Toolbar content along well-known gridlines.</p>
1285 *
1286 * @param contentInsetLeft Content inset for the toolbar's left edge
1287 * @param contentInsetRight Content inset for the toolbar's right edge
1288 *
1289 * @see #setContentInsetsAbsolute(int, int)
1290 * @see #getContentInsetStart()
1291 * @see #getContentInsetEnd()
1292 * @see #getContentInsetLeft()
1293 * @see #getContentInsetRight()
Adam Powell679be2d2016-03-28 09:38:41 -07001294 * @attr ref android.R.styleable#Toolbar_contentInsetLeft
1295 * @attr ref android.R.styleable#Toolbar_contentInsetRight
Adam Powell76d8f962014-05-14 20:13:47 -07001296 */
1297 public void setContentInsetsAbsolute(int contentInsetLeft, int contentInsetRight) {
Chris Banes3b925c12016-06-20 10:38:02 +01001298 ensureContentInsets();
Adam Powell76d8f962014-05-14 20:13:47 -07001299 mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight);
1300 }
1301
1302 /**
Adam Powell679be2d2016-03-28 09:38:41 -07001303 * Gets the left content inset for this toolbar.
Adam Powell76d8f962014-05-14 20:13:47 -07001304 *
1305 * <p>The content inset affects the valid area for Toolbar content other than
1306 * the navigation button and menu. Insets define the minimum margin for these components
1307 * and can be used to effectively align Toolbar content along well-known gridlines.</p>
1308 *
1309 * @return The left content inset for this toolbar
1310 *
1311 * @see #setContentInsetsRelative(int, int)
1312 * @see #setContentInsetsAbsolute(int, int)
1313 * @see #getContentInsetStart()
1314 * @see #getContentInsetEnd()
1315 * @see #getContentInsetRight()
Adam Powell679be2d2016-03-28 09:38:41 -07001316 * @attr ref android.R.styleable#Toolbar_contentInsetLeft
Adam Powell76d8f962014-05-14 20:13:47 -07001317 */
Ashley Rose55f9f922019-01-28 19:29:36 -05001318 @InspectableProperty
Adam Powell76d8f962014-05-14 20:13:47 -07001319 public int getContentInsetLeft() {
Chris Banes3b925c12016-06-20 10:38:02 +01001320 return mContentInsets != null ? mContentInsets.getLeft() : 0;
Adam Powell76d8f962014-05-14 20:13:47 -07001321 }
1322
1323 /**
Adam Powell679be2d2016-03-28 09:38:41 -07001324 * Gets the right content inset for this toolbar.
Adam Powell76d8f962014-05-14 20:13:47 -07001325 *
1326 * <p>The content inset affects the valid area for Toolbar content other than
1327 * the navigation button and menu. Insets define the minimum margin for these components
1328 * and can be used to effectively align Toolbar content along well-known gridlines.</p>
1329 *
1330 * @return The right content inset for this toolbar
1331 *
1332 * @see #setContentInsetsRelative(int, int)
1333 * @see #setContentInsetsAbsolute(int, int)
1334 * @see #getContentInsetStart()
1335 * @see #getContentInsetEnd()
1336 * @see #getContentInsetLeft()
Adam Powell679be2d2016-03-28 09:38:41 -07001337 * @attr ref android.R.styleable#Toolbar_contentInsetRight
Adam Powell76d8f962014-05-14 20:13:47 -07001338 */
Ashley Rose55f9f922019-01-28 19:29:36 -05001339 @InspectableProperty
Adam Powell76d8f962014-05-14 20:13:47 -07001340 public int getContentInsetRight() {
Chris Banes3b925c12016-06-20 10:38:02 +01001341 return mContentInsets != null ? mContentInsets.getRight() : 0;
Adam Powell76d8f962014-05-14 20:13:47 -07001342 }
1343
Adam Powell679be2d2016-03-28 09:38:41 -07001344 /**
1345 * Gets the start content inset to use when a navigation button is present.
1346 *
1347 * <p>Different content insets are often called for when additional buttons are present
1348 * in the toolbar, as well as at different toolbar sizes. The larger value of
1349 * {@link #getContentInsetStart()} and this value will be used during layout.</p>
1350 *
1351 * @return the start content inset used when a navigation icon has been set in pixels
1352 *
1353 * @see #setContentInsetStartWithNavigation(int)
1354 * @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation
1355 */
Ashley Rose55f9f922019-01-28 19:29:36 -05001356 @InspectableProperty
Adam Powell679be2d2016-03-28 09:38:41 -07001357 public int getContentInsetStartWithNavigation() {
1358 return mContentInsetStartWithNavigation != RtlSpacingHelper.UNDEFINED
1359 ? mContentInsetStartWithNavigation
1360 : getContentInsetStart();
1361 }
1362
1363 /**
1364 * Sets the start content inset to use when a navigation button is present.
1365 *
1366 * <p>Different content insets are often called for when additional buttons are present
1367 * in the toolbar, as well as at different toolbar sizes. The larger value of
1368 * {@link #getContentInsetStart()} and this value will be used during layout.</p>
1369 *
1370 * @param insetStartWithNavigation the inset to use when a navigation icon has been set
1371 * in pixels
1372 *
1373 * @see #getContentInsetStartWithNavigation()
1374 * @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation
1375 */
1376 public void setContentInsetStartWithNavigation(int insetStartWithNavigation) {
1377 if (insetStartWithNavigation < 0) {
1378 insetStartWithNavigation = RtlSpacingHelper.UNDEFINED;
1379 }
1380 if (insetStartWithNavigation != mContentInsetStartWithNavigation) {
1381 mContentInsetStartWithNavigation = insetStartWithNavigation;
1382 if (getNavigationIcon() != null) {
1383 requestLayout();
1384 }
1385 }
1386 }
1387
1388 /**
1389 * Gets the end content inset to use when action buttons are present.
1390 *
1391 * <p>Different content insets are often called for when additional buttons are present
1392 * in the toolbar, as well as at different toolbar sizes. The larger value of
1393 * {@link #getContentInsetEnd()} and this value will be used during layout.</p>
1394 *
1395 * @return the end content inset used when a menu has been set in pixels
1396 *
1397 * @see #setContentInsetEndWithActions(int)
1398 * @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions
1399 */
Ashley Rose55f9f922019-01-28 19:29:36 -05001400 @InspectableProperty
Adam Powell679be2d2016-03-28 09:38:41 -07001401 public int getContentInsetEndWithActions() {
1402 return mContentInsetEndWithActions != RtlSpacingHelper.UNDEFINED
1403 ? mContentInsetEndWithActions
1404 : getContentInsetEnd();
1405 }
1406
1407 /**
1408 * Sets the start content inset to use when action buttons are present.
1409 *
1410 * <p>Different content insets are often called for when additional buttons are present
1411 * in the toolbar, as well as at different toolbar sizes. The larger value of
1412 * {@link #getContentInsetEnd()} and this value will be used during layout.</p>
1413 *
1414 * @param insetEndWithActions the inset to use when a menu has been set in pixels
1415 *
1416 * @see #setContentInsetEndWithActions(int)
1417 * @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions
1418 */
1419 public void setContentInsetEndWithActions(int insetEndWithActions) {
1420 if (insetEndWithActions < 0) {
1421 insetEndWithActions = RtlSpacingHelper.UNDEFINED;
1422 }
1423 if (insetEndWithActions != mContentInsetEndWithActions) {
1424 mContentInsetEndWithActions = insetEndWithActions;
1425 if (getNavigationIcon() != null) {
1426 requestLayout();
1427 }
1428 }
1429 }
1430
1431 /**
1432 * Gets the content inset that will be used on the starting side of the bar in the current
1433 * toolbar configuration.
1434 *
1435 * @return the current content inset start in pixels
1436 *
1437 * @see #getContentInsetStartWithNavigation()
1438 */
1439 public int getCurrentContentInsetStart() {
1440 return getNavigationIcon() != null
1441 ? Math.max(getContentInsetStart(), Math.max(mContentInsetStartWithNavigation, 0))
1442 : getContentInsetStart();
1443 }
1444
1445 /**
1446 * Gets the content inset that will be used on the ending side of the bar in the current
1447 * toolbar configuration.
1448 *
1449 * @return the current content inset end in pixels
1450 *
1451 * @see #getContentInsetEndWithActions()
1452 */
1453 public int getCurrentContentInsetEnd() {
1454 boolean hasActions = false;
1455 if (mMenuView != null) {
1456 final MenuBuilder mb = mMenuView.peekMenu();
1457 hasActions = mb != null && mb.hasVisibleItems();
1458 }
1459 return hasActions
1460 ? Math.max(getContentInsetEnd(), Math.max(mContentInsetEndWithActions, 0))
1461 : getContentInsetEnd();
1462 }
1463
1464 /**
1465 * Gets the content inset that will be used on the left side of the bar in the current
1466 * toolbar configuration.
1467 *
1468 * @return the current content inset left in pixels
1469 *
1470 * @see #getContentInsetStartWithNavigation()
1471 * @see #getContentInsetEndWithActions()
1472 */
1473 public int getCurrentContentInsetLeft() {
1474 return isLayoutRtl()
1475 ? getCurrentContentInsetEnd()
1476 : getCurrentContentInsetStart();
1477 }
1478
1479 /**
1480 * Gets the content inset that will be used on the right side of the bar in the current
1481 * toolbar configuration.
1482 *
1483 * @return the current content inset right in pixels
1484 *
1485 * @see #getContentInsetStartWithNavigation()
1486 * @see #getContentInsetEndWithActions()
1487 */
1488 public int getCurrentContentInsetRight() {
1489 return isLayoutRtl()
1490 ? getCurrentContentInsetStart()
1491 : getCurrentContentInsetEnd();
1492 }
1493
Adam Powelle43340c2014-03-17 19:10:43 -07001494 private void ensureNavButtonView() {
1495 if (mNavButtonView == null) {
Adam Powelle021e6e2014-05-23 17:27:24 -07001496 mNavButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle);
1497 final LayoutParams lp = generateDefaultLayoutParams();
1498 lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
1499 mNavButtonView.setLayoutParams(lp);
1500 }
1501 }
1502
1503 private void ensureCollapseButtonView() {
1504 if (mCollapseButtonView == null) {
1505 mCollapseButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle);
1506 mCollapseButtonView.setImageDrawable(mCollapseIcon);
Alan Viverette85085342014-10-28 16:42:24 -07001507 mCollapseButtonView.setContentDescription(mCollapseDescription);
Adam Powelle021e6e2014-05-23 17:27:24 -07001508 final LayoutParams lp = generateDefaultLayoutParams();
1509 lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
1510 lp.mViewType = LayoutParams.EXPANDED;
1511 mCollapseButtonView.setLayoutParams(lp);
1512 mCollapseButtonView.setOnClickListener(new OnClickListener() {
1513 @Override
1514 public void onClick(View v) {
1515 collapseActionView();
1516 }
1517 });
Adam Powelle43340c2014-03-17 19:10:43 -07001518 }
1519 }
1520
Adam Powell5f622842015-06-15 14:14:42 -07001521 private void addSystemView(View v, boolean allowHide) {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001522 final ViewGroup.LayoutParams vlp = v.getLayoutParams();
1523 final LayoutParams lp;
1524 if (vlp == null) {
1525 lp = generateDefaultLayoutParams();
1526 } else if (!checkLayoutParams(vlp)) {
1527 lp = generateLayoutParams(vlp);
1528 } else {
1529 lp = (LayoutParams) vlp;
1530 }
Adam Powelle43340c2014-03-17 19:10:43 -07001531 lp.mViewType = LayoutParams.SYSTEM;
Adam Powell5f622842015-06-15 14:14:42 -07001532
1533 if (allowHide && mExpandedActionView != null) {
1534 v.setLayoutParams(lp);
1535 mHiddenViews.add(v);
1536 } else {
1537 addView(v, lp);
1538 }
Adam Powelle43340c2014-03-17 19:10:43 -07001539 }
1540
1541 @Override
1542 protected Parcelable onSaveInstanceState() {
1543 SavedState state = new SavedState(super.onSaveInstanceState());
Adam Powell31c91c82014-08-22 17:20:00 -07001544
1545 if (mExpandedMenuPresenter != null && mExpandedMenuPresenter.mCurrentExpandedItem != null) {
1546 state.expandedMenuItemId = mExpandedMenuPresenter.mCurrentExpandedItem.getItemId();
1547 }
1548
1549 state.isOverflowOpen = isOverflowMenuShowing();
1550
Adam Powelle43340c2014-03-17 19:10:43 -07001551 return state;
1552 }
1553
1554 @Override
1555 protected void onRestoreInstanceState(Parcelable state) {
1556 final SavedState ss = (SavedState) state;
1557 super.onRestoreInstanceState(ss.getSuperState());
Adam Powell31c91c82014-08-22 17:20:00 -07001558
1559 final Menu menu = mMenuView != null ? mMenuView.peekMenu() : null;
1560 if (ss.expandedMenuItemId != 0 && mExpandedMenuPresenter != null && menu != null) {
1561 final MenuItem item = menu.findItem(ss.expandedMenuItemId);
1562 if (item != null) {
1563 item.expandActionView();
1564 }
1565 }
1566
1567 if (ss.isOverflowOpen) {
1568 postShowOverflowMenu();
1569 }
1570 }
1571
1572 private void postShowOverflowMenu() {
1573 removeCallbacks(mShowOverflowMenuRunnable);
1574 post(mShowOverflowMenuRunnable);
1575 }
1576
1577 @Override
1578 protected void onDetachedFromWindow() {
1579 super.onDetachedFromWindow();
1580 removeCallbacks(mShowOverflowMenuRunnable);
Adam Powelle43340c2014-03-17 19:10:43 -07001581 }
1582
Adam Powell61d15b92014-09-12 11:01:21 -07001583 @Override
1584 public boolean onTouchEvent(MotionEvent ev) {
1585 // Toolbars always eat touch events, but should still respect the touch event dispatch
1586 // contract. If the normal View implementation doesn't want the events, we'll just silently
1587 // eat the rest of the gesture without reporting the events to the default implementation
1588 // since that's what it expects.
1589
1590 final int action = ev.getActionMasked();
1591 if (action == MotionEvent.ACTION_DOWN) {
1592 mEatingTouch = false;
1593 }
1594
1595 if (!mEatingTouch) {
1596 final boolean handled = super.onTouchEvent(ev);
1597 if (action == MotionEvent.ACTION_DOWN && !handled) {
1598 mEatingTouch = true;
1599 }
1600 }
1601
1602 if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
1603 mEatingTouch = false;
1604 }
1605
1606 return true;
1607 }
1608
Adam Powelleb97f472014-07-19 11:57:21 -07001609 /**
1610 * @hide
1611 */
1612 @Override
1613 protected void onSetLayoutParams(View child, ViewGroup.LayoutParams lp) {
1614 /*
1615 * Apps may set ActionBar.LayoutParams on their action bar custom views when
1616 * a Toolbar is actually acting in the role of the action bar. Perform a quick
1617 * switch with Toolbar.LayoutParams whenever this happens. This does leave open
1618 * one potential gotcha: if an app retains the ActionBar.LayoutParams reference
1619 * and attempts to keep making changes to it before layout those changes won't
1620 * be reflected in the final results.
1621 */
1622 if (!checkLayoutParams(lp)) {
1623 child.setLayoutParams(generateLayoutParams(lp));
1624 }
1625 }
1626
Adam Powelle021e6e2014-05-23 17:27:24 -07001627 private void measureChildConstrained(View child, int parentWidthSpec, int widthUsed,
1628 int parentHeightSpec, int heightUsed, int heightConstraint) {
1629 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
1630
1631 int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
1632 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
1633 + widthUsed, lp.width);
1634 int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
1635 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
1636 + heightUsed, lp.height);
1637
1638 final int childHeightMode = MeasureSpec.getMode(childHeightSpec);
1639 if (childHeightMode != MeasureSpec.EXACTLY && heightConstraint >= 0) {
1640 final int size = childHeightMode != MeasureSpec.UNSPECIFIED ?
1641 Math.min(MeasureSpec.getSize(childHeightSpec), heightConstraint) :
1642 heightConstraint;
1643 childHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
1644 }
1645 child.measure(childWidthSpec, childHeightSpec);
1646 }
1647
Adam Powell7a324012014-05-30 18:52:37 -07001648 /**
1649 * Returns the width + uncollapsed margins
1650 */
1651 private int measureChildCollapseMargins(View child,
1652 int parentWidthMeasureSpec, int widthUsed,
1653 int parentHeightMeasureSpec, int heightUsed, int[] collapsingMargins) {
1654 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
1655
1656 final int leftDiff = lp.leftMargin - collapsingMargins[0];
1657 final int rightDiff = lp.rightMargin - collapsingMargins[1];
1658 final int leftMargin = Math.max(0, leftDiff);
1659 final int rightMargin = Math.max(0, rightDiff);
1660 final int hMargins = leftMargin + rightMargin;
1661 collapsingMargins[0] = Math.max(0, -leftDiff);
1662 collapsingMargins[1] = Math.max(0, -rightDiff);
1663
1664 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
1665 mPaddingLeft + mPaddingRight + hMargins + widthUsed, lp.width);
1666 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
1667 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
1668 + heightUsed, lp.height);
1669
1670 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
1671 return child.getMeasuredWidth() + hMargins;
1672 }
1673
Adam Powell360c1d82014-06-11 19:31:41 -07001674 /**
1675 * Returns true if the Toolbar is collapsible and has no child views with a measured size > 0.
1676 */
1677 private boolean shouldCollapse() {
1678 if (!mCollapsible) return false;
1679
1680 final int childCount = getChildCount();
1681 for (int i = 0; i < childCount; i++) {
1682 final View child = getChildAt(i);
1683 if (shouldLayout(child) && child.getMeasuredWidth() > 0 &&
1684 child.getMeasuredHeight() > 0) {
1685 return false;
1686 }
1687 }
1688 return true;
1689 }
1690
Adam Powelle43340c2014-03-17 19:10:43 -07001691 @Override
1692 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1693 int width = 0;
1694 int height = 0;
1695 int childState = 0;
1696
Adam Powell7a324012014-05-30 18:52:37 -07001697 final int[] collapsingMargins = mTempMargins;
1698 final int marginStartIndex;
1699 final int marginEndIndex;
1700 if (isLayoutRtl()) {
1701 marginStartIndex = 1;
1702 marginEndIndex = 0;
1703 } else {
1704 marginStartIndex = 0;
1705 marginEndIndex = 1;
1706 }
1707
Adam Powelle43340c2014-03-17 19:10:43 -07001708 // System views measure first.
1709
Adam Powell76d8f962014-05-14 20:13:47 -07001710 int navWidth = 0;
Adam Powelle43340c2014-03-17 19:10:43 -07001711 if (shouldLayout(mNavButtonView)) {
Adam Powelle021e6e2014-05-23 17:27:24 -07001712 measureChildConstrained(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0,
1713 mMaxButtonHeight);
Adam Powell76d8f962014-05-14 20:13:47 -07001714 navWidth = mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView);
Adam Powelle43340c2014-03-17 19:10:43 -07001715 height = Math.max(height, mNavButtonView.getMeasuredHeight() +
1716 getVerticalMargins(mNavButtonView));
1717 childState = combineMeasuredStates(childState, mNavButtonView.getMeasuredState());
1718 }
1719
Adam Powelle021e6e2014-05-23 17:27:24 -07001720 if (shouldLayout(mCollapseButtonView)) {
1721 measureChildConstrained(mCollapseButtonView, widthMeasureSpec, width,
1722 heightMeasureSpec, 0, mMaxButtonHeight);
1723 navWidth = mCollapseButtonView.getMeasuredWidth() +
1724 getHorizontalMargins(mCollapseButtonView);
1725 height = Math.max(height, mCollapseButtonView.getMeasuredHeight() +
1726 getVerticalMargins(mCollapseButtonView));
1727 childState = combineMeasuredStates(childState, mCollapseButtonView.getMeasuredState());
1728 }
1729
Adam Powell679be2d2016-03-28 09:38:41 -07001730 final int contentInsetStart = getCurrentContentInsetStart();
Adam Powell7a324012014-05-30 18:52:37 -07001731 width += Math.max(contentInsetStart, navWidth);
1732 collapsingMargins[marginStartIndex] = Math.max(0, contentInsetStart - navWidth);
Adam Powell76d8f962014-05-14 20:13:47 -07001733
1734 int menuWidth = 0;
Adam Powelle43340c2014-03-17 19:10:43 -07001735 if (shouldLayout(mMenuView)) {
Adam Powelle021e6e2014-05-23 17:27:24 -07001736 measureChildConstrained(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0,
1737 mMaxButtonHeight);
Adam Powell76d8f962014-05-14 20:13:47 -07001738 menuWidth = mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView);
Adam Powelle43340c2014-03-17 19:10:43 -07001739 height = Math.max(height, mMenuView.getMeasuredHeight() +
1740 getVerticalMargins(mMenuView));
1741 childState = combineMeasuredStates(childState, mMenuView.getMeasuredState());
1742 }
1743
Adam Powell679be2d2016-03-28 09:38:41 -07001744 final int contentInsetEnd = getCurrentContentInsetEnd();
Adam Powell7a324012014-05-30 18:52:37 -07001745 width += Math.max(contentInsetEnd, menuWidth);
1746 collapsingMargins[marginEndIndex] = Math.max(0, contentInsetEnd - menuWidth);
Adam Powell76d8f962014-05-14 20:13:47 -07001747
Adam Powelle021e6e2014-05-23 17:27:24 -07001748 if (shouldLayout(mExpandedActionView)) {
Adam Powell7a324012014-05-30 18:52:37 -07001749 width += measureChildCollapseMargins(mExpandedActionView, widthMeasureSpec, width,
1750 heightMeasureSpec, 0, collapsingMargins);
Adam Powelle021e6e2014-05-23 17:27:24 -07001751 height = Math.max(height, mExpandedActionView.getMeasuredHeight() +
1752 getVerticalMargins(mExpandedActionView));
1753 childState = combineMeasuredStates(childState, mExpandedActionView.getMeasuredState());
1754 }
1755
Adam Powelle43340c2014-03-17 19:10:43 -07001756 if (shouldLayout(mLogoView)) {
Adam Powell7a324012014-05-30 18:52:37 -07001757 width += measureChildCollapseMargins(mLogoView, widthMeasureSpec, width,
1758 heightMeasureSpec, 0, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001759 height = Math.max(height, mLogoView.getMeasuredHeight() +
1760 getVerticalMargins(mLogoView));
1761 childState = combineMeasuredStates(childState, mLogoView.getMeasuredState());
1762 }
1763
Adam Powellb606a3a2014-07-19 13:26:16 -07001764 final int childCount = getChildCount();
1765 for (int i = 0; i < childCount; i++) {
1766 final View child = getChildAt(i);
1767 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1768 if (lp.mViewType != LayoutParams.CUSTOM || !shouldLayout(child)) {
1769 // We already got all system views above. Skip them and GONE views.
1770 continue;
1771 }
1772
1773 width += measureChildCollapseMargins(child, widthMeasureSpec, width,
1774 heightMeasureSpec, 0, collapsingMargins);
1775 height = Math.max(height, child.getMeasuredHeight() + getVerticalMargins(child));
1776 childState = combineMeasuredStates(childState, child.getMeasuredState());
1777 }
1778
Adam Powelle43340c2014-03-17 19:10:43 -07001779 int titleWidth = 0;
1780 int titleHeight = 0;
1781 final int titleVertMargins = mTitleMarginTop + mTitleMarginBottom;
1782 final int titleHorizMargins = mTitleMarginStart + mTitleMarginEnd;
1783 if (shouldLayout(mTitleTextView)) {
Adam Powell7a324012014-05-30 18:52:37 -07001784 titleWidth = measureChildCollapseMargins(mTitleTextView, widthMeasureSpec,
1785 width + titleHorizMargins, heightMeasureSpec, titleVertMargins,
1786 collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001787 titleWidth = mTitleTextView.getMeasuredWidth() + getHorizontalMargins(mTitleTextView);
1788 titleHeight = mTitleTextView.getMeasuredHeight() + getVerticalMargins(mTitleTextView);
1789 childState = combineMeasuredStates(childState, mTitleTextView.getMeasuredState());
1790 }
1791 if (shouldLayout(mSubtitleTextView)) {
Adam Powell7a324012014-05-30 18:52:37 -07001792 titleWidth = Math.max(titleWidth, measureChildCollapseMargins(mSubtitleTextView,
1793 widthMeasureSpec, width + titleHorizMargins,
1794 heightMeasureSpec, titleHeight + titleVertMargins,
1795 collapsingMargins));
Adam Powelle43340c2014-03-17 19:10:43 -07001796 titleHeight += mSubtitleTextView.getMeasuredHeight() +
1797 getVerticalMargins(mSubtitleTextView);
1798 childState = combineMeasuredStates(childState, mSubtitleTextView.getMeasuredState());
1799 }
1800
1801 width += titleWidth;
1802 height = Math.max(height, titleHeight);
1803
Adam Powelle43340c2014-03-17 19:10:43 -07001804 // Measurement already took padding into account for available space for the children,
1805 // add it in for the final size.
1806 width += getPaddingLeft() + getPaddingRight();
1807 height += getPaddingTop() + getPaddingBottom();
1808
1809 final int measuredWidth = resolveSizeAndState(
1810 Math.max(width, getSuggestedMinimumWidth()),
1811 widthMeasureSpec, childState & MEASURED_STATE_MASK);
1812 final int measuredHeight = resolveSizeAndState(
1813 Math.max(height, getSuggestedMinimumHeight()),
1814 heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT);
Adam Powell360c1d82014-06-11 19:31:41 -07001815
1816 setMeasuredDimension(measuredWidth, shouldCollapse() ? 0 : measuredHeight);
Adam Powelle43340c2014-03-17 19:10:43 -07001817 }
1818
1819 @Override
1820 protected void onLayout(boolean changed, int l, int t, int r, int b) {
1821 final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
1822 final int width = getWidth();
1823 final int height = getHeight();
1824 final int paddingLeft = getPaddingLeft();
1825 final int paddingRight = getPaddingRight();
1826 final int paddingTop = getPaddingTop();
1827 final int paddingBottom = getPaddingBottom();
1828 int left = paddingLeft;
1829 int right = width - paddingRight;
1830
Adam Powell7a324012014-05-30 18:52:37 -07001831 final int[] collapsingMargins = mTempMargins;
1832 collapsingMargins[0] = collapsingMargins[1] = 0;
1833
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001834 // Align views within the minimum toolbar height, if set.
Chris Banes8053117a2016-06-28 09:38:29 +01001835 final int minHeight = getMinimumHeight();
1836 final int alignmentHeight = minHeight >= 0 ? Math.min(minHeight, b - t) : 0;
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001837
Adam Powelle43340c2014-03-17 19:10:43 -07001838 if (shouldLayout(mNavButtonView)) {
1839 if (isRtl) {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001840 right = layoutChildRight(mNavButtonView, right, collapsingMargins,
1841 alignmentHeight);
Adam Powelle43340c2014-03-17 19:10:43 -07001842 } else {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001843 left = layoutChildLeft(mNavButtonView, left, collapsingMargins,
1844 alignmentHeight);
Adam Powelle43340c2014-03-17 19:10:43 -07001845 }
1846 }
1847
Adam Powelle021e6e2014-05-23 17:27:24 -07001848 if (shouldLayout(mCollapseButtonView)) {
1849 if (isRtl) {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001850 right = layoutChildRight(mCollapseButtonView, right, collapsingMargins,
1851 alignmentHeight);
Adam Powelle021e6e2014-05-23 17:27:24 -07001852 } else {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001853 left = layoutChildLeft(mCollapseButtonView, left, collapsingMargins,
1854 alignmentHeight);
Adam Powelle021e6e2014-05-23 17:27:24 -07001855 }
1856 }
1857
Adam Powelle43340c2014-03-17 19:10:43 -07001858 if (shouldLayout(mMenuView)) {
1859 if (isRtl) {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001860 left = layoutChildLeft(mMenuView, left, collapsingMargins,
1861 alignmentHeight);
Adam Powelle43340c2014-03-17 19:10:43 -07001862 } else {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001863 right = layoutChildRight(mMenuView, right, collapsingMargins,
1864 alignmentHeight);
Adam Powelle43340c2014-03-17 19:10:43 -07001865 }
1866 }
1867
Adam Powell679be2d2016-03-28 09:38:41 -07001868 final int contentInsetLeft = getCurrentContentInsetLeft();
1869 final int contentInsetRight = getCurrentContentInsetRight();
1870 collapsingMargins[0] = Math.max(0, contentInsetLeft - left);
1871 collapsingMargins[1] = Math.max(0, contentInsetRight - (width - paddingRight - right));
1872 left = Math.max(left, contentInsetLeft);
1873 right = Math.min(right, width - paddingRight - contentInsetRight);
Adam Powell76d8f962014-05-14 20:13:47 -07001874
Adam Powelle021e6e2014-05-23 17:27:24 -07001875 if (shouldLayout(mExpandedActionView)) {
1876 if (isRtl) {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001877 right = layoutChildRight(mExpandedActionView, right, collapsingMargins,
1878 alignmentHeight);
Adam Powelle021e6e2014-05-23 17:27:24 -07001879 } else {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001880 left = layoutChildLeft(mExpandedActionView, left, collapsingMargins,
1881 alignmentHeight);
Adam Powelle021e6e2014-05-23 17:27:24 -07001882 }
1883 }
1884
Adam Powelle43340c2014-03-17 19:10:43 -07001885 if (shouldLayout(mLogoView)) {
1886 if (isRtl) {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001887 right = layoutChildRight(mLogoView, right, collapsingMargins,
1888 alignmentHeight);
Adam Powelle43340c2014-03-17 19:10:43 -07001889 } else {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07001890 left = layoutChildLeft(mLogoView, left, collapsingMargins,
1891 alignmentHeight);
Adam Powelle43340c2014-03-17 19:10:43 -07001892 }
1893 }
1894
1895 final boolean layoutTitle = shouldLayout(mTitleTextView);
1896 final boolean layoutSubtitle = shouldLayout(mSubtitleTextView);
1897 int titleHeight = 0;
1898 if (layoutTitle) {
1899 final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
1900 titleHeight += lp.topMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin;
1901 }
1902 if (layoutSubtitle) {
1903 final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
Adam Powellb3e41142014-07-01 17:38:18 -07001904 titleHeight += lp.topMargin + mSubtitleTextView.getMeasuredHeight() + lp.bottomMargin;
Adam Powelle43340c2014-03-17 19:10:43 -07001905 }
1906
1907 if (layoutTitle || layoutSubtitle) {
1908 int titleTop;
Adam Powelle021e6e2014-05-23 17:27:24 -07001909 final View topChild = layoutTitle ? mTitleTextView : mSubtitleTextView;
1910 final View bottomChild = layoutSubtitle ? mSubtitleTextView : mTitleTextView;
1911 final LayoutParams toplp = (LayoutParams) topChild.getLayoutParams();
1912 final LayoutParams bottomlp = (LayoutParams) bottomChild.getLayoutParams();
Adam Powell4c9765a2014-08-28 19:21:18 -07001913 final boolean titleHasWidth = layoutTitle && mTitleTextView.getMeasuredWidth() > 0
1914 || layoutSubtitle && mSubtitleTextView.getMeasuredWidth() > 0;
Adam Powelle021e6e2014-05-23 17:27:24 -07001915
Adam Powelle43340c2014-03-17 19:10:43 -07001916 switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
1917 case Gravity.TOP:
Adam Powelle021e6e2014-05-23 17:27:24 -07001918 titleTop = getPaddingTop() + toplp.topMargin + mTitleMarginTop;
Adam Powelle43340c2014-03-17 19:10:43 -07001919 break;
1920 default:
1921 case Gravity.CENTER_VERTICAL:
Adam Powelle43340c2014-03-17 19:10:43 -07001922 final int space = height - paddingTop - paddingBottom;
1923 int spaceAbove = (space - titleHeight) / 2;
Adam Powelle021e6e2014-05-23 17:27:24 -07001924 if (spaceAbove < toplp.topMargin + mTitleMarginTop) {
1925 spaceAbove = toplp.topMargin + mTitleMarginTop;
Adam Powelle43340c2014-03-17 19:10:43 -07001926 } else {
1927 final int spaceBelow = height - paddingBottom - titleHeight -
1928 spaceAbove - paddingTop;
Adam Powelle021e6e2014-05-23 17:27:24 -07001929 if (spaceBelow < toplp.bottomMargin + mTitleMarginBottom) {
Adam Powelle43340c2014-03-17 19:10:43 -07001930 spaceAbove = Math.max(0, spaceAbove -
Adam Powelle021e6e2014-05-23 17:27:24 -07001931 (bottomlp.bottomMargin + mTitleMarginBottom - spaceBelow));
Adam Powelle43340c2014-03-17 19:10:43 -07001932 }
1933 }
1934 titleTop = paddingTop + spaceAbove;
1935 break;
1936 case Gravity.BOTTOM:
Adam Powelle021e6e2014-05-23 17:27:24 -07001937 titleTop = height - paddingBottom - bottomlp.bottomMargin - mTitleMarginBottom -
1938 titleHeight;
Adam Powelle43340c2014-03-17 19:10:43 -07001939 break;
1940 }
1941 if (isRtl) {
Adam Powell4c9765a2014-08-28 19:21:18 -07001942 final int rd = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[1];
Adam Powell7a324012014-05-30 18:52:37 -07001943 right -= Math.max(0, rd);
1944 collapsingMargins[1] = Math.max(0, -rd);
Adam Powelle43340c2014-03-17 19:10:43 -07001945 int titleRight = right;
1946 int subtitleRight = right;
Adam Powell7a324012014-05-30 18:52:37 -07001947
Adam Powelle43340c2014-03-17 19:10:43 -07001948 if (layoutTitle) {
1949 final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
Adam Powelle43340c2014-03-17 19:10:43 -07001950 final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth();
1951 final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
1952 mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
Adam Powell7a324012014-05-30 18:52:37 -07001953 titleRight = titleLeft - mTitleMarginEnd;
Adam Powelle43340c2014-03-17 19:10:43 -07001954 titleTop = titleBottom + lp.bottomMargin;
1955 }
1956 if (layoutSubtitle) {
1957 final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
Adam Powelle43340c2014-03-17 19:10:43 -07001958 titleTop += lp.topMargin;
1959 final int subtitleLeft = subtitleRight - mSubtitleTextView.getMeasuredWidth();
1960 final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
1961 mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
Adam Powell7a324012014-05-30 18:52:37 -07001962 subtitleRight = subtitleRight - mTitleMarginEnd;
Adam Powelle43340c2014-03-17 19:10:43 -07001963 titleTop = subtitleBottom + lp.bottomMargin;
1964 }
Adam Powell4c9765a2014-08-28 19:21:18 -07001965 if (titleHasWidth) {
1966 right = Math.min(titleRight, subtitleRight);
1967 }
Adam Powelle43340c2014-03-17 19:10:43 -07001968 } else {
Adam Powell4c9765a2014-08-28 19:21:18 -07001969 final int ld = (titleHasWidth ? mTitleMarginStart : 0) - collapsingMargins[0];
Adam Powell7a324012014-05-30 18:52:37 -07001970 left += Math.max(0, ld);
1971 collapsingMargins[0] = Math.max(0, -ld);
Adam Powelle43340c2014-03-17 19:10:43 -07001972 int titleLeft = left;
1973 int subtitleLeft = left;
Adam Powell7a324012014-05-30 18:52:37 -07001974
Adam Powelle43340c2014-03-17 19:10:43 -07001975 if (layoutTitle) {
1976 final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
Adam Powelle43340c2014-03-17 19:10:43 -07001977 final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth();
1978 final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
1979 mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
Adam Powell7a324012014-05-30 18:52:37 -07001980 titleLeft = titleRight + mTitleMarginEnd;
Adam Powelle43340c2014-03-17 19:10:43 -07001981 titleTop = titleBottom + lp.bottomMargin;
1982 }
1983 if (layoutSubtitle) {
1984 final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
Adam Powelle43340c2014-03-17 19:10:43 -07001985 titleTop += lp.topMargin;
1986 final int subtitleRight = subtitleLeft + mSubtitleTextView.getMeasuredWidth();
1987 final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
1988 mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
Adam Powell7a324012014-05-30 18:52:37 -07001989 subtitleLeft = subtitleRight + mTitleMarginEnd;
Adam Powelle43340c2014-03-17 19:10:43 -07001990 titleTop = subtitleBottom + lp.bottomMargin;
1991 }
Adam Powell4c9765a2014-08-28 19:21:18 -07001992 if (titleHasWidth) {
1993 left = Math.max(titleLeft, subtitleLeft);
1994 }
Adam Powelle43340c2014-03-17 19:10:43 -07001995 }
1996 }
1997
1998 // Get all remaining children sorted for layout. This is all prepared
1999 // such that absolute layout direction can be used below.
2000
2001 addCustomViewsWithGravity(mTempViews, Gravity.LEFT);
2002 final int leftViewsCount = mTempViews.size();
2003 for (int i = 0; i < leftViewsCount; i++) {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07002004 left = layoutChildLeft(mTempViews.get(i), left, collapsingMargins,
2005 alignmentHeight);
Adam Powelle43340c2014-03-17 19:10:43 -07002006 }
2007
2008 addCustomViewsWithGravity(mTempViews, Gravity.RIGHT);
2009 final int rightViewsCount = mTempViews.size();
2010 for (int i = 0; i < rightViewsCount; i++) {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07002011 right = layoutChildRight(mTempViews.get(i), right, collapsingMargins,
2012 alignmentHeight);
Adam Powelle43340c2014-03-17 19:10:43 -07002013 }
2014
2015 // Centered views try to center with respect to the whole bar, but views pinned
2016 // to the left or right can push the mass of centered views to one side or the other.
Adam Powelle021e6e2014-05-23 17:27:24 -07002017 addCustomViewsWithGravity(mTempViews, Gravity.CENTER_HORIZONTAL);
Adam Powell7a324012014-05-30 18:52:37 -07002018 final int centerViewsWidth = getViewListMeasuredWidth(mTempViews, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07002019 final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2;
2020 final int halfCenterViewsWidth = centerViewsWidth / 2;
2021 int centerLeft = parentCenter - halfCenterViewsWidth;
2022 final int centerRight = centerLeft + centerViewsWidth;
2023 if (centerLeft < left) {
2024 centerLeft = left;
2025 } else if (centerRight > right) {
2026 centerLeft -= centerRight - right;
2027 }
2028
2029 final int centerViewsCount = mTempViews.size();
2030 for (int i = 0; i < centerViewsCount; i++) {
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07002031 centerLeft = layoutChildLeft(mTempViews.get(i), centerLeft, collapsingMargins,
2032 alignmentHeight);
Adam Powelle43340c2014-03-17 19:10:43 -07002033 }
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07002034
Adam Powelle43340c2014-03-17 19:10:43 -07002035 mTempViews.clear();
2036 }
2037
Adam Powell7a324012014-05-30 18:52:37 -07002038 private int getViewListMeasuredWidth(List<View> views, int[] collapsingMargins) {
2039 int collapseLeft = collapsingMargins[0];
2040 int collapseRight = collapsingMargins[1];
Adam Powelle43340c2014-03-17 19:10:43 -07002041 int width = 0;
2042 final int count = views.size();
2043 for (int i = 0; i < count; i++) {
2044 final View v = views.get(i);
2045 final LayoutParams lp = (LayoutParams) v.getLayoutParams();
Adam Powell7a324012014-05-30 18:52:37 -07002046 final int l = lp.leftMargin - collapseLeft;
2047 final int r = lp.rightMargin - collapseRight;
2048 final int leftMargin = Math.max(0, l);
2049 final int rightMargin = Math.max(0, r);
2050 collapseLeft = Math.max(0, -l);
2051 collapseRight = Math.max(0, -r);
2052 width += leftMargin + v.getMeasuredWidth() + rightMargin;
Adam Powelle43340c2014-03-17 19:10:43 -07002053 }
2054 return width;
2055 }
2056
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07002057 private int layoutChildLeft(View child, int left, int[] collapsingMargins,
2058 int alignmentHeight) {
Adam Powelle43340c2014-03-17 19:10:43 -07002059 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Adam Powell7a324012014-05-30 18:52:37 -07002060 final int l = lp.leftMargin - collapsingMargins[0];
2061 left += Math.max(0, l);
2062 collapsingMargins[0] = Math.max(0, -l);
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07002063 final int top = getChildTop(child, alignmentHeight);
Adam Powell7a8a2842014-05-14 16:04:03 -07002064 final int childWidth = child.getMeasuredWidth();
2065 child.layout(left, top, left + childWidth, top + child.getMeasuredHeight());
2066 left += childWidth + lp.rightMargin;
Adam Powelle43340c2014-03-17 19:10:43 -07002067 return left;
2068 }
2069
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07002070 private int layoutChildRight(View child, int right, int[] collapsingMargins,
2071 int alignmentHeight) {
Adam Powelle43340c2014-03-17 19:10:43 -07002072 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Adam Powell7a324012014-05-30 18:52:37 -07002073 final int r = lp.rightMargin - collapsingMargins[1];
2074 right -= Math.max(0, r);
2075 collapsingMargins[1] = Math.max(0, -r);
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07002076 final int top = getChildTop(child, alignmentHeight);
Adam Powell7a8a2842014-05-14 16:04:03 -07002077 final int childWidth = child.getMeasuredWidth();
2078 child.layout(right - childWidth, top, right, top + child.getMeasuredHeight());
2079 right -= childWidth + lp.leftMargin;
Adam Powelle43340c2014-03-17 19:10:43 -07002080 return right;
2081 }
2082
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07002083 private int getChildTop(View child, int alignmentHeight) {
Adam Powelle43340c2014-03-17 19:10:43 -07002084 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07002085 final int childHeight = child.getMeasuredHeight();
2086 final int alignmentOffset = alignmentHeight > 0 ? (childHeight - alignmentHeight) / 2 : 0;
Adam Powelle43340c2014-03-17 19:10:43 -07002087 switch (getChildVerticalGravity(lp.gravity)) {
2088 case Gravity.TOP:
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07002089 return getPaddingTop() - alignmentOffset;
Adam Powelle43340c2014-03-17 19:10:43 -07002090
2091 case Gravity.BOTTOM:
Alan Viverettee3cb7ba2014-09-11 16:23:29 -07002092 return getHeight() - getPaddingBottom() - childHeight
2093 - lp.bottomMargin - alignmentOffset;
Adam Powelle43340c2014-03-17 19:10:43 -07002094
2095 default:
2096 case Gravity.CENTER_VERTICAL:
2097 final int paddingTop = getPaddingTop();
2098 final int paddingBottom = getPaddingBottom();
2099 final int height = getHeight();
Adam Powelle43340c2014-03-17 19:10:43 -07002100 final int space = height - paddingTop - paddingBottom;
2101 int spaceAbove = (space - childHeight) / 2;
2102 if (spaceAbove < lp.topMargin) {
2103 spaceAbove = lp.topMargin;
2104 } else {
2105 final int spaceBelow = height - paddingBottom - childHeight -
2106 spaceAbove - paddingTop;
2107 if (spaceBelow < lp.bottomMargin) {
2108 spaceAbove = Math.max(0, spaceAbove - (lp.bottomMargin - spaceBelow));
2109 }
2110 }
2111 return paddingTop + spaceAbove;
2112 }
2113 }
2114
2115 private int getChildVerticalGravity(int gravity) {
2116 final int vgrav = gravity & Gravity.VERTICAL_GRAVITY_MASK;
2117 switch (vgrav) {
2118 case Gravity.TOP:
2119 case Gravity.BOTTOM:
2120 case Gravity.CENTER_VERTICAL:
2121 return vgrav;
2122 default:
2123 return mGravity & Gravity.VERTICAL_GRAVITY_MASK;
2124 }
2125 }
2126
2127 /**
2128 * Prepare a list of non-SYSTEM child views. If the layout direction is RTL
2129 * this will be in reverse child order.
2130 *
2131 * @param views List to populate. It will be cleared before use.
2132 * @param gravity Horizontal gravity to match against
2133 */
2134 private void addCustomViewsWithGravity(List<View> views, int gravity) {
2135 final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
2136 final int childCount = getChildCount();
2137 final int absGrav = Gravity.getAbsoluteGravity(gravity, getLayoutDirection());
2138
2139 views.clear();
2140
2141 if (isRtl) {
2142 for (int i = childCount - 1; i >= 0; i--) {
2143 final View child = getChildAt(i);
2144 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Adam Powelle021e6e2014-05-23 17:27:24 -07002145 if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) &&
Adam Powelle43340c2014-03-17 19:10:43 -07002146 getChildHorizontalGravity(lp.gravity) == absGrav) {
2147 views.add(child);
2148 }
Adam Powelle43340c2014-03-17 19:10:43 -07002149 }
2150 } else {
2151 for (int i = 0; i < childCount; i++) {
2152 final View child = getChildAt(i);
2153 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Adam Powelle021e6e2014-05-23 17:27:24 -07002154 if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) &&
Adam Powelle43340c2014-03-17 19:10:43 -07002155 getChildHorizontalGravity(lp.gravity) == absGrav) {
2156 views.add(child);
2157 }
2158 }
2159 }
2160 }
2161
2162 private int getChildHorizontalGravity(int gravity) {
2163 final int ld = getLayoutDirection();
2164 final int absGrav = Gravity.getAbsoluteGravity(gravity, ld);
2165 final int hGrav = absGrav & Gravity.HORIZONTAL_GRAVITY_MASK;
2166 switch (hGrav) {
2167 case Gravity.LEFT:
2168 case Gravity.RIGHT:
2169 case Gravity.CENTER_HORIZONTAL:
2170 return hGrav;
2171 default:
2172 return ld == LAYOUT_DIRECTION_RTL ? Gravity.RIGHT : Gravity.LEFT;
2173 }
2174 }
2175
2176 private boolean shouldLayout(View view) {
2177 return view != null && view.getParent() == this && view.getVisibility() != GONE;
2178 }
2179
2180 private int getHorizontalMargins(View v) {
2181 final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
2182 return mlp.getMarginStart() + mlp.getMarginEnd();
2183 }
2184
2185 private int getVerticalMargins(View v) {
2186 final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
2187 return mlp.topMargin + mlp.bottomMargin;
2188 }
2189
2190 @Override
Adam Powelle021e6e2014-05-23 17:27:24 -07002191 public LayoutParams generateLayoutParams(AttributeSet attrs) {
2192 return new LayoutParams(getContext(), attrs);
Adam Powelle43340c2014-03-17 19:10:43 -07002193 }
2194
2195 @Override
Adam Powelle021e6e2014-05-23 17:27:24 -07002196 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
Adam Powelle43340c2014-03-17 19:10:43 -07002197 if (p instanceof LayoutParams) {
2198 return new LayoutParams((LayoutParams) p);
Adam Powelle021e6e2014-05-23 17:27:24 -07002199 } else if (p instanceof ActionBar.LayoutParams) {
2200 return new LayoutParams((ActionBar.LayoutParams) p);
Adam Powelle43340c2014-03-17 19:10:43 -07002201 } else if (p instanceof MarginLayoutParams) {
2202 return new LayoutParams((MarginLayoutParams) p);
2203 } else {
2204 return new LayoutParams(p);
2205 }
2206 }
2207
2208 @Override
Adam Powelle021e6e2014-05-23 17:27:24 -07002209 protected LayoutParams generateDefaultLayoutParams() {
Adam Powelle43340c2014-03-17 19:10:43 -07002210 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
2211 }
2212
2213 @Override
2214 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
2215 return super.checkLayoutParams(p) && p instanceof LayoutParams;
2216 }
2217
2218 private static boolean isCustomView(View child) {
2219 return ((LayoutParams) child.getLayoutParams()).mViewType == LayoutParams.CUSTOM;
2220 }
2221
Adam Powelle021e6e2014-05-23 17:27:24 -07002222 /** @hide */
2223 public DecorToolbar getWrapper() {
2224 if (mWrapper == null) {
Adam Powell2aa09a92014-07-19 16:01:12 -07002225 mWrapper = new ToolbarWidgetWrapper(this, true);
Adam Powelle021e6e2014-05-23 17:27:24 -07002226 }
2227 return mWrapper;
2228 }
2229
Adam Powell5f622842015-06-15 14:14:42 -07002230 void removeChildrenForExpandedActionView() {
Adam Powelle021e6e2014-05-23 17:27:24 -07002231 final int childCount = getChildCount();
Adam Powell5f622842015-06-15 14:14:42 -07002232 // Go backwards since we're removing from the list
2233 for (int i = childCount - 1; i >= 0; i--) {
Adam Powelle021e6e2014-05-23 17:27:24 -07002234 final View child = getChildAt(i);
2235 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
2236 if (lp.mViewType != LayoutParams.EXPANDED && child != mMenuView) {
Adam Powell5f622842015-06-15 14:14:42 -07002237 removeViewAt(i);
2238 mHiddenViews.add(child);
Adam Powelle021e6e2014-05-23 17:27:24 -07002239 }
2240 }
2241 }
2242
Adam Powell5f622842015-06-15 14:14:42 -07002243 void addChildrenForExpandedActionView() {
2244 final int count = mHiddenViews.size();
2245 // Re-add in reverse order since we removed in reverse order
2246 for (int i = count - 1; i >= 0; i--) {
2247 addView(mHiddenViews.get(i));
Adam Powell31c91c82014-08-22 17:20:00 -07002248 }
Adam Powell5f622842015-06-15 14:14:42 -07002249 mHiddenViews.clear();
2250 }
2251
2252 private boolean isChildOrHidden(View child) {
2253 return child.getParent() == this || mHiddenViews.contains(child);
Adam Powell31c91c82014-08-22 17:20:00 -07002254 }
2255
Adam Powelle43340c2014-03-17 19:10:43 -07002256 /**
Adam Powell360c1d82014-06-11 19:31:41 -07002257 * Force the toolbar to collapse to zero-height during measurement if
2258 * it could be considered "empty" (no visible elements with nonzero measured size)
2259 * @hide
2260 */
2261 public void setCollapsible(boolean collapsible) {
2262 mCollapsible = collapsible;
2263 requestLayout();
2264 }
2265
2266 /**
Adam Powell04c0d462014-09-04 16:10:24 -07002267 * Must be called before the menu is accessed
2268 * @hide
2269 */
Adam Powellc4612502014-09-04 19:16:39 -07002270 public void setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb) {
2271 mActionMenuPresenterCallback = pcb;
2272 mMenuBuilderCallback = mcb;
Alan Viverette5190f172016-01-13 11:20:26 -05002273 if (mMenuView != null) {
2274 mMenuView.setMenuCallbacks(pcb, mcb);
2275 }
Adam Powell04c0d462014-09-04 16:10:24 -07002276 }
2277
Chris Banes3b925c12016-06-20 10:38:02 +01002278 private void ensureContentInsets() {
2279 if (mContentInsets == null) {
2280 mContentInsets = new RtlSpacingHelper();
2281 }
2282 }
2283
Adam Powell04c0d462014-09-04 16:10:24 -07002284 /**
Deepanshu Gupta0bffc732014-10-27 10:34:23 -07002285 * Accessor to enable LayoutLib to get ActionMenuPresenter directly.
2286 */
2287 ActionMenuPresenter getOuterActionMenuPresenter() {
2288 return mOuterActionMenuPresenter;
2289 }
2290
2291 Context getPopupContext() {
2292 return mPopupContext;
2293 }
2294
2295 /**
Adam Powelle43340c2014-03-17 19:10:43 -07002296 * Interface responsible for receiving menu item click events if the items themselves
2297 * do not have individual item click listeners.
2298 */
2299 public interface OnMenuItemClickListener {
2300 /**
2301 * This method will be invoked when a menu item is clicked if the item itself did
2302 * not already handle the event.
2303 *
2304 * @param item {@link MenuItem} that was clicked
2305 * @return <code>true</code> if the event was handled, <code>false</code> otherwise.
2306 */
2307 public boolean onMenuItemClick(MenuItem item);
2308 }
2309
2310 /**
2311 * Layout information for child views of Toolbars.
2312 *
Adam Powelld7600832014-07-01 15:22:50 -07002313 * <p>Toolbar.LayoutParams extends ActionBar.LayoutParams for compatibility with existing
2314 * ActionBar API. See {@link android.app.Activity#setActionBar(Toolbar) Activity.setActionBar}
2315 * for more info on how to use a Toolbar as your Activity's ActionBar.</p>
2316 *
Adam Powelle43340c2014-03-17 19:10:43 -07002317 * @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity
2318 */
Adam Powelle021e6e2014-05-23 17:27:24 -07002319 public static class LayoutParams extends ActionBar.LayoutParams {
Adam Powelle43340c2014-03-17 19:10:43 -07002320 static final int CUSTOM = 0;
2321 static final int SYSTEM = 1;
Adam Powelle021e6e2014-05-23 17:27:24 -07002322 static final int EXPANDED = 2;
Adam Powelle43340c2014-03-17 19:10:43 -07002323
2324 int mViewType = CUSTOM;
2325
2326 public LayoutParams(@NonNull Context c, AttributeSet attrs) {
2327 super(c, attrs);
Adam Powelle43340c2014-03-17 19:10:43 -07002328 }
2329
2330 public LayoutParams(int width, int height) {
2331 super(width, height);
2332 this.gravity = Gravity.CENTER_VERTICAL | Gravity.START;
2333 }
2334
2335 public LayoutParams(int width, int height, int gravity) {
2336 super(width, height);
2337 this.gravity = gravity;
2338 }
2339
2340 public LayoutParams(int gravity) {
2341 this(WRAP_CONTENT, MATCH_PARENT, gravity);
2342 }
2343
2344 public LayoutParams(LayoutParams source) {
2345 super(source);
2346
Adam Powelle021e6e2014-05-23 17:27:24 -07002347 mViewType = source.mViewType;
2348 }
2349
2350 public LayoutParams(ActionBar.LayoutParams source) {
2351 super(source);
Adam Powelle43340c2014-03-17 19:10:43 -07002352 }
2353
2354 public LayoutParams(MarginLayoutParams source) {
2355 super(source);
Adam Powelld7600832014-07-01 15:22:50 -07002356 // ActionBar.LayoutParams doesn't have a MarginLayoutParams constructor.
2357 // Fake it here and copy over the relevant data.
2358 copyMarginsFrom(source);
Adam Powelle43340c2014-03-17 19:10:43 -07002359 }
2360
2361 public LayoutParams(ViewGroup.LayoutParams source) {
2362 super(source);
2363 }
2364 }
2365
2366 static class SavedState extends BaseSavedState {
Adam Powell31c91c82014-08-22 17:20:00 -07002367 public int expandedMenuItemId;
2368 public boolean isOverflowOpen;
2369
Adam Powelle43340c2014-03-17 19:10:43 -07002370 public SavedState(Parcel source) {
2371 super(source);
Adam Powell31c91c82014-08-22 17:20:00 -07002372 expandedMenuItemId = source.readInt();
2373 isOverflowOpen = source.readInt() != 0;
Adam Powelle43340c2014-03-17 19:10:43 -07002374 }
2375
2376 public SavedState(Parcelable superState) {
2377 super(superState);
2378 }
2379
2380 @Override
2381 public void writeToParcel(Parcel out, int flags) {
2382 super.writeToParcel(out, flags);
Adam Powell31c91c82014-08-22 17:20:00 -07002383 out.writeInt(expandedMenuItemId);
2384 out.writeInt(isOverflowOpen ? 1 : 0);
Adam Powelle43340c2014-03-17 19:10:43 -07002385 }
2386
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07002387 public static final @android.annotation.NonNull Creator<SavedState> CREATOR = new Creator<SavedState>() {
Adam Powelle43340c2014-03-17 19:10:43 -07002388
2389 @Override
2390 public SavedState createFromParcel(Parcel source) {
2391 return new SavedState(source);
2392 }
2393
2394 @Override
2395 public SavedState[] newArray(int size) {
2396 return new SavedState[size];
2397 }
2398 };
2399 }
Adam Powelle021e6e2014-05-23 17:27:24 -07002400
2401 private class ExpandedActionViewMenuPresenter implements MenuPresenter {
2402 MenuBuilder mMenu;
2403 MenuItemImpl mCurrentExpandedItem;
2404
2405 @Override
Alan Viverette28a84682016-01-04 13:43:23 -05002406 public void initForMenu(@NonNull Context context, @Nullable MenuBuilder menu) {
Adam Powelle021e6e2014-05-23 17:27:24 -07002407 // Clear the expanded action view when menus change.
2408 if (mMenu != null && mCurrentExpandedItem != null) {
2409 mMenu.collapseItemActionView(mCurrentExpandedItem);
2410 }
2411 mMenu = menu;
2412 }
2413
2414 @Override
2415 public MenuView getMenuView(ViewGroup root) {
2416 return null;
2417 }
2418
2419 @Override
2420 public void updateMenuView(boolean cleared) {
2421 // Make sure the expanded item we have is still there.
2422 if (mCurrentExpandedItem != null) {
2423 boolean found = false;
2424
2425 if (mMenu != null) {
2426 final int count = mMenu.size();
2427 for (int i = 0; i < count; i++) {
2428 final MenuItem item = mMenu.getItem(i);
2429 if (item == mCurrentExpandedItem) {
2430 found = true;
2431 break;
2432 }
2433 }
2434 }
2435
2436 if (!found) {
2437 // The item we had expanded disappeared. Collapse.
2438 collapseItemActionView(mMenu, mCurrentExpandedItem);
2439 }
2440 }
2441 }
2442
2443 @Override
2444 public void setCallback(Callback cb) {
2445 }
2446
2447 @Override
2448 public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
2449 return false;
2450 }
2451
2452 @Override
2453 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
2454 }
2455
2456 @Override
2457 public boolean flagActionItems() {
2458 return false;
2459 }
2460
2461 @Override
2462 public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
2463 ensureCollapseButtonView();
2464 if (mCollapseButtonView.getParent() != Toolbar.this) {
2465 addView(mCollapseButtonView);
2466 }
2467 mExpandedActionView = item.getActionView();
2468 mCurrentExpandedItem = item;
2469 if (mExpandedActionView.getParent() != Toolbar.this) {
2470 final LayoutParams lp = generateDefaultLayoutParams();
2471 lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
2472 lp.mViewType = LayoutParams.EXPANDED;
2473 mExpandedActionView.setLayoutParams(lp);
2474 addView(mExpandedActionView);
2475 }
2476
Adam Powell5f622842015-06-15 14:14:42 -07002477 removeChildrenForExpandedActionView();
Adam Powelle021e6e2014-05-23 17:27:24 -07002478 requestLayout();
2479 item.setActionViewExpanded(true);
2480
2481 if (mExpandedActionView instanceof CollapsibleActionView) {
2482 ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded();
2483 }
2484
2485 return true;
2486 }
2487
2488 @Override
2489 public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
2490 // Do this before detaching the actionview from the hierarchy, in case
2491 // it needs to dismiss the soft keyboard, etc.
2492 if (mExpandedActionView instanceof CollapsibleActionView) {
2493 ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed();
2494 }
2495
2496 removeView(mExpandedActionView);
2497 removeView(mCollapseButtonView);
2498 mExpandedActionView = null;
2499
Adam Powell5f622842015-06-15 14:14:42 -07002500 addChildrenForExpandedActionView();
Adam Powelle021e6e2014-05-23 17:27:24 -07002501 mCurrentExpandedItem = null;
2502 requestLayout();
2503 item.setActionViewExpanded(false);
2504
2505 return true;
2506 }
2507
2508 @Override
2509 public int getId() {
2510 return 0;
2511 }
2512
2513 @Override
2514 public Parcelable onSaveInstanceState() {
2515 return null;
2516 }
2517
2518 @Override
2519 public void onRestoreInstanceState(Parcelable state) {
2520 }
2521 }
Adam Powelle43340c2014-03-17 19:10:43 -07002522}