blob: cbd9a6af425e88e33e2326111184018722ef4f25 [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
17
18package android.widget;
19
20import android.annotation.NonNull;
Adam Powelle021e6e2014-05-23 17:27:24 -070021import android.app.ActionBar;
Adam Powelle43340c2014-03-17 19:10:43 -070022import android.content.Context;
23import android.content.res.TypedArray;
24import android.graphics.drawable.Drawable;
25import android.os.Parcel;
26import android.os.Parcelable;
Adam Powelle021e6e2014-05-23 17:27:24 -070027import android.text.Layout;
Adam Powelle43340c2014-03-17 19:10:43 -070028import android.text.TextUtils;
29import android.util.AttributeSet;
Adam Powelle021e6e2014-05-23 17:27:24 -070030import android.util.Log;
31import android.view.CollapsibleActionView;
Adam Powelle43340c2014-03-17 19:10:43 -070032import android.view.Gravity;
33import android.view.Menu;
34import android.view.MenuInflater;
35import android.view.MenuItem;
36import android.view.View;
37import android.view.ViewDebug;
38import android.view.ViewGroup;
Adam Powelle021e6e2014-05-23 17:27:24 -070039import android.view.Window;
Adam Powelle43340c2014-03-17 19:10:43 -070040import com.android.internal.R;
Adam Powelle021e6e2014-05-23 17:27:24 -070041import com.android.internal.view.menu.MenuBuilder;
42import com.android.internal.view.menu.MenuItemImpl;
43import com.android.internal.view.menu.MenuPresenter;
44import com.android.internal.view.menu.MenuView;
45import com.android.internal.view.menu.SubMenuBuilder;
46import com.android.internal.widget.DecorToolbar;
47import com.android.internal.widget.ToolbarWidgetWrapper;
Adam Powelle43340c2014-03-17 19:10:43 -070048
49import java.util.ArrayList;
50import java.util.List;
51
52/**
53 * A standard toolbar for use within application content.
54 *
55 * <p>A Toolbar is a generalization of {@link android.app.ActionBar action bars} for use
56 * within application layouts. While an action bar is traditionally part of an
57 * {@link android.app.Activity Activity's} opaque window decor controlled by the framework,
58 * a Toolbar may be placed at any arbitrary level of nesting within a view hierarchy.
59 * An application may choose to designate a Toolbar as the action bar for an Activity
60 * using the {@link android.app.Activity#setActionBar(Toolbar) setActionBar()} method.</p>
61 *
62 * <p>Toolbar supports a more focused feature set than ActionBar. From start to end, a toolbar
63 * may contain a combination of the following optional elements:
64 *
65 * <ul>
66 * <li><em>A navigation button.</em> This may be an Up arrow, navigation menu toggle, close,
67 * collapse, done or another glyph of the app's choosing. This button should always be used
68 * to access other navigational destinations within the container of the Toolbar and
69 * its signified content or otherwise leave the current context signified by the Toolbar.</li>
70 * <li><em>A branded logo image.</em> This may extend to the height of the bar and can be
71 * arbitrarily wide.</li>
72 * <li><em>A title and subtitle.</em> The title should be a signpost for the Toolbar's current
73 * position in the navigation hierarchy and the content contained there. The subtitle,
74 * if present should indicate any extended information about the current content.
75 * If an app uses a logo image it should strongly consider omitting a title and subtitle.</li>
76 * <li><em>One or more custom views.</em> The application may add arbitrary child views
77 * to the Toolbar. They will appear at this position within the layout. If a child view's
78 * {@link LayoutParams} indicates a {@link Gravity} value of
79 * {@link Gravity#CENTER_HORIZONTAL CENTER_HORIZONTAL} the view will attempt to center
80 * within the available space remaining in the Toolbar after all other elements have been
81 * measured.</li>
82 * <li><em>An {@link ActionMenuView action menu}.</em> The menu of actions will pin to the
83 * end of the Toolbar offering a few
84 * <a href="http://developer.android.com/design/patterns/actionbar.html#ActionButtons">
85 * frequent, important or typical</a> actions along with an optional overflow menu for
86 * additional actions.</li>
87 * </ul>
88 * </p>
89 *
90 * <p>In modern Android UIs developers should lean more on a visually distinct color scheme for
91 * toolbars than on their application icon. The use of application icon plus title as a standard
92 * layout is discouraged on API 21 devices and newer.</p>
93 */
94public class Toolbar extends ViewGroup {
Adam Powelle021e6e2014-05-23 17:27:24 -070095 private static final String TAG = "Toolbar";
96
Adam Powelle43340c2014-03-17 19:10:43 -070097 private ActionMenuView mMenuView;
98 private TextView mTitleTextView;
99 private TextView mSubtitleTextView;
100 private ImageButton mNavButtonView;
101 private ImageView mLogoView;
102
Adam Powelle021e6e2014-05-23 17:27:24 -0700103 private Drawable mCollapseIcon;
104 private ImageButton mCollapseButtonView;
105 View mExpandedActionView;
106
Adam Powelle43340c2014-03-17 19:10:43 -0700107 private int mTitleTextAppearance;
108 private int mSubtitleTextAppearance;
Adam Powelle021e6e2014-05-23 17:27:24 -0700109 private int mNavButtonStyle;
110
111 private int mButtonGravity;
112
113 private int mMaxButtonHeight;
Adam Powell76d8f962014-05-14 20:13:47 -0700114
Adam Powelle43340c2014-03-17 19:10:43 -0700115 private int mTitleMarginStart;
116 private int mTitleMarginEnd;
117 private int mTitleMarginTop;
118 private int mTitleMarginBottom;
119
Adam Powell76d8f962014-05-14 20:13:47 -0700120 private final RtlSpacingHelper mContentInsets = new RtlSpacingHelper();
121
Adam Powelle43340c2014-03-17 19:10:43 -0700122 private int mGravity = Gravity.START | Gravity.CENTER_VERTICAL;
123
124 private CharSequence mTitleText;
125 private CharSequence mSubtitleText;
126
127 // Clear me after use.
128 private final ArrayList<View> mTempViews = new ArrayList<View>();
129
Adam Powell7a324012014-05-30 18:52:37 -0700130 private final int[] mTempMargins = new int[2];
131
Adam Powelle43340c2014-03-17 19:10:43 -0700132 private OnMenuItemClickListener mOnMenuItemClickListener;
133
134 private final ActionMenuView.OnMenuItemClickListener mMenuViewItemClickListener =
135 new ActionMenuView.OnMenuItemClickListener() {
136 @Override
137 public boolean onMenuItemClick(MenuItem item) {
138 if (mOnMenuItemClickListener != null) {
139 return mOnMenuItemClickListener.onMenuItemClick(item);
140 }
141 return false;
142 }
143 };
144
Adam Powelle021e6e2014-05-23 17:27:24 -0700145 private ToolbarWidgetWrapper mWrapper;
146 private ActionMenuPresenter mOuterActionMenuPresenter;
147 private ExpandedActionViewMenuPresenter mExpandedMenuPresenter;
148
Adam Powelle43340c2014-03-17 19:10:43 -0700149 public Toolbar(Context context) {
150 this(context, null);
151 }
152
153 public Toolbar(Context context, AttributeSet attrs) {
154 this(context, attrs, com.android.internal.R.attr.toolbarStyle);
155 }
156
157 public Toolbar(Context context, AttributeSet attrs, int defStyleAttr) {
158 this(context, attrs, defStyleAttr, 0);
159 }
160
161 public Toolbar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
162 super(context, attrs, defStyleAttr, defStyleRes);
163
164 final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Toolbar,
165 defStyleAttr, defStyleRes);
166
167 mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0);
168 mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0);
Adam Powelle021e6e2014-05-23 17:27:24 -0700169 mNavButtonStyle = a.getResourceId(R.styleable.Toolbar_navigationButtonStyle, 0);
Adam Powelle43340c2014-03-17 19:10:43 -0700170 mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity);
Adam Powelle021e6e2014-05-23 17:27:24 -0700171 mButtonGravity = a.getInteger(R.styleable.Toolbar_buttonGravity, Gravity.TOP);
Adam Powell76d8f962014-05-14 20:13:47 -0700172 mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom =
173 a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, 0);
Adam Powelle43340c2014-03-17 19:10:43 -0700174
175 final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1);
176 if (marginStart >= 0) {
177 mTitleMarginStart = marginStart;
178 }
179
180 final int marginEnd = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginEnd, -1);
181 if (marginEnd >= 0) {
182 mTitleMarginEnd = marginEnd;
183 }
184
185 final int marginTop = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginTop, -1);
186 if (marginTop >= 0) {
187 mTitleMarginTop = marginTop;
188 }
189
190 final int marginBottom = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginBottom,
191 -1);
192 if (marginBottom >= 0) {
193 mTitleMarginBottom = marginBottom;
194 }
195
Adam Powelle021e6e2014-05-23 17:27:24 -0700196 mMaxButtonHeight = a.getDimensionPixelSize(R.styleable.Toolbar_maxButtonHeight, -1);
197
Adam Powell76d8f962014-05-14 20:13:47 -0700198 final int contentInsetStart =
199 a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetStart,
200 RtlSpacingHelper.UNDEFINED);
201 final int contentInsetEnd =
202 a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetEnd,
203 RtlSpacingHelper.UNDEFINED);
204 final int contentInsetLeft =
205 a.getDimensionPixelSize(R.styleable.Toolbar_contentInsetLeft, 0);
206 final int contentInsetRight =
207 a.getDimensionPixelSize(R.styleable.Toolbar_contentInsetRight, 0);
208
209 mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight);
210
211 if (contentInsetStart != RtlSpacingHelper.UNDEFINED ||
212 contentInsetEnd != RtlSpacingHelper.UNDEFINED) {
213 mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
214 }
215
Adam Powelle021e6e2014-05-23 17:27:24 -0700216 mCollapseIcon = a.getDrawable(R.styleable.Toolbar_collapseIcon);
217
Adam Powelle43340c2014-03-17 19:10:43 -0700218 final CharSequence title = a.getText(R.styleable.Toolbar_title);
219 if (!TextUtils.isEmpty(title)) {
220 setTitle(title);
221 }
222
223 final CharSequence subtitle = a.getText(R.styleable.Toolbar_subtitle);
224 if (!TextUtils.isEmpty(subtitle)) {
Adam Powell07a74542014-05-30 15:52:44 -0700225 setSubtitle(subtitle);
Adam Powelle43340c2014-03-17 19:10:43 -0700226 }
227 a.recycle();
228 }
229
Adam Powell76d8f962014-05-14 20:13:47 -0700230 @Override
231 public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
232 super.onRtlPropertiesChanged(layoutDirection);
233 mContentInsets.setDirection(layoutDirection == LAYOUT_DIRECTION_RTL);
234 }
235
Adam Powelle43340c2014-03-17 19:10:43 -0700236 /**
237 * Set a logo drawable from a resource id.
238 *
239 * <p>This drawable should generally take the place of title text. The logo cannot be
240 * clicked. Apps using a logo should also supply a description using
241 * {@link #setLogoDescription(int)}.</p>
242 *
243 * @param resId ID of a drawable resource
244 */
245 public void setLogo(int resId) {
246 setLogo(getContext().getDrawable(resId));
247 }
248
Adam Powelle021e6e2014-05-23 17:27:24 -0700249 /** @hide */
250 public boolean canShowOverflowMenu() {
251 return getVisibility() == VISIBLE && mMenuView != null && mMenuView.isOverflowReserved();
252 }
253
254 /**
255 * Check whether the overflow menu is currently showing. This may not reflect
256 * a pending show operation in progress.
257 *
258 * @return true if the overflow menu is currently showing
259 */
260 public boolean isOverflowMenuShowing() {
261 return mMenuView != null && mMenuView.isOverflowMenuShowing();
262 }
263
264 /** @hide */
265 public boolean isOverflowMenuShowPending() {
266 return mMenuView != null && mMenuView.isOverflowMenuShowPending();
267 }
268
269 /**
270 * Show the overflow items from the associated menu.
271 *
272 * @return true if the menu was able to be shown, false otherwise
273 */
274 public boolean showOverflowMenu() {
275 return mMenuView != null && mMenuView.showOverflowMenu();
276 }
277
278 /**
279 * Hide the overflow items from the associated menu.
280 *
281 * @return true if the menu was able to be hidden, false otherwise
282 */
283 public boolean hideOverflowMenu() {
284 return mMenuView != null && mMenuView.hideOverflowMenu();
285 }
286
287 /** @hide */
288 public void setMenu(MenuBuilder menu, ActionMenuPresenter outerPresenter) {
289 if (menu == null && mMenuView == null) {
290 return;
291 }
292
293 ensureMenuView();
294 final MenuBuilder oldMenu = mMenuView.peekMenu();
295 if (oldMenu == menu) {
296 return;
297 }
298
299 if (oldMenu != null) {
300 oldMenu.removeMenuPresenter(mOuterActionMenuPresenter);
301 oldMenu.removeMenuPresenter(mExpandedMenuPresenter);
302 }
303
304 final Context context = getContext();
305
306 if (mExpandedMenuPresenter == null) {
307 mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
308 }
309
310 outerPresenter.setExpandedActionViewsExclusive(true);
311 if (menu != null) {
312 menu.addMenuPresenter(outerPresenter);
313 menu.addMenuPresenter(mExpandedMenuPresenter);
314 } else {
315 outerPresenter.initForMenu(context, null);
316 mExpandedMenuPresenter.initForMenu(context, null);
317 outerPresenter.updateMenuView(true);
318 mExpandedMenuPresenter.updateMenuView(true);
319 }
320 mMenuView.setPresenter(outerPresenter);
321 mOuterActionMenuPresenter = outerPresenter;
322 }
323
324 /**
325 * Dismiss all currently showing popup menus, including overflow or submenus.
326 */
327 public void dismissPopupMenus() {
328 if (mMenuView != null) {
329 mMenuView.dismissPopupMenus();
330 }
331 }
332
333 /** @hide */
334 public boolean isTitleTruncated() {
335 if (mTitleTextView == null) {
336 return false;
337 }
338
339 final Layout titleLayout = mTitleTextView.getLayout();
340 if (titleLayout == null) {
341 return false;
342 }
343
344 final int lineCount = titleLayout.getLineCount();
345 for (int i = 0; i < lineCount; i++) {
346 if (titleLayout.getEllipsisCount(i) > 0) {
347 return true;
348 }
349 }
350 return false;
351 }
352
Adam Powelle43340c2014-03-17 19:10:43 -0700353 /**
354 * Set a logo drawable.
355 *
356 * <p>This drawable should generally take the place of title text. The logo cannot be
357 * clicked. Apps using a logo should also supply a description using
358 * {@link #setLogoDescription(int)}.</p>
359 *
360 * @param drawable Drawable to use as a logo
361 */
362 public void setLogo(Drawable drawable) {
363 if (drawable != null) {
Adam Powelle021e6e2014-05-23 17:27:24 -0700364 ensureLogoView();
Adam Powelle43340c2014-03-17 19:10:43 -0700365 if (mLogoView.getParent() == null) {
366 addSystemView(mLogoView);
367 }
368 } else if (mLogoView != null && mLogoView.getParent() != null) {
369 removeView(mLogoView);
370 }
371 if (mLogoView != null) {
372 mLogoView.setImageDrawable(drawable);
373 }
374 }
375
376 /**
377 * Return the current logo drawable.
378 *
379 * @return The current logo drawable
380 * @see #setLogo(int)
381 * @see #setLogo(android.graphics.drawable.Drawable)
382 */
383 public Drawable getLogo() {
384 return mLogoView != null ? mLogoView.getDrawable() : null;
385 }
386
387 /**
388 * Set a description of the toolbar's logo.
389 *
390 * <p>This description will be used for accessibility or other similar descriptions
391 * of the UI.</p>
392 *
393 * @param resId String resource id
394 */
395 public void setLogoDescription(int resId) {
396 setLogoDescription(getContext().getText(resId));
397 }
398
399 /**
400 * Set a description of the toolbar's logo.
401 *
402 * <p>This description will be used for accessibility or other similar descriptions
403 * of the UI.</p>
404 *
405 * @param description Description to set
406 */
407 public void setLogoDescription(CharSequence description) {
Adam Powelle021e6e2014-05-23 17:27:24 -0700408 if (!TextUtils.isEmpty(description)) {
409 ensureLogoView();
Adam Powelle43340c2014-03-17 19:10:43 -0700410 }
411 if (mLogoView != null) {
412 mLogoView.setContentDescription(description);
413 }
414 }
415
416 /**
417 * Return the description of the toolbar's logo.
418 *
419 * @return A description of the logo
420 */
421 public CharSequence getLogoDescription() {
422 return mLogoView != null ? mLogoView.getContentDescription() : null;
423 }
424
Adam Powelle021e6e2014-05-23 17:27:24 -0700425 private void ensureLogoView() {
426 if (mLogoView == null) {
427 mLogoView = new ImageView(getContext());
428 }
429 }
430
Adam Powelle43340c2014-03-17 19:10:43 -0700431 /**
Adam Powelle021e6e2014-05-23 17:27:24 -0700432 * Check whether this Toolbar is currently hosting an expanded action view.
Adam Powelle43340c2014-03-17 19:10:43 -0700433 *
Adam Powelle021e6e2014-05-23 17:27:24 -0700434 * <p>An action view may be expanded either directly from the
435 * {@link android.view.MenuItem MenuItem} it belongs to or by user action. If the Toolbar
436 * has an expanded action view it can be collapsed using the {@link #collapseActionView()}
437 * method.</p>
438 *
439 * @return true if the Toolbar has an expanded action view
440 */
441 public boolean hasExpandedActionView() {
442 return mExpandedMenuPresenter != null &&
443 mExpandedMenuPresenter.mCurrentExpandedItem != null;
444 }
445
446 /**
447 * Collapse a currently expanded action view. If this Toolbar does not have an
448 * expanded action view this method has no effect.
449 *
450 * <p>An action view may be expanded either directly from the
451 * {@link android.view.MenuItem MenuItem} it belongs to or by user action.</p>
452 *
453 * @see #hasExpandedActionView()
454 */
455 public void collapseActionView() {
456 final MenuItemImpl item = mExpandedMenuPresenter == null ? null :
457 mExpandedMenuPresenter.mCurrentExpandedItem;
458 if (item != null) {
459 item.collapseActionView();
460 }
461 }
462
463 /**
464 * Returns the title of this toolbar.
465 *
466 * @return The current title.
Adam Powelle43340c2014-03-17 19:10:43 -0700467 */
468 public CharSequence getTitle() {
469 return mTitleText;
470 }
471
472 /**
473 * Set the title of this toolbar.
474 *
475 * <p>A title should be used as the anchor for a section of content. It should
476 * describe or name the content being viewed.</p>
477 *
478 * @param resId Resource ID of a string to set as the title
479 */
480 public void setTitle(int resId) {
481 setTitle(getContext().getText(resId));
482 }
483
484 /**
485 * Set the title of this toolbar.
486 *
487 * <p>A title should be used as the anchor for a section of content. It should
488 * describe or name the content being viewed.</p>
489 *
490 * @param title Title to set
491 */
492 public void setTitle(CharSequence title) {
493 if (!TextUtils.isEmpty(title)) {
494 if (mTitleTextView == null) {
495 final Context context = getContext();
496 mTitleTextView = new TextView(context);
Adam Powelle021e6e2014-05-23 17:27:24 -0700497 mTitleTextView.setSingleLine();
498 mTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
Adam Powelle43340c2014-03-17 19:10:43 -0700499 mTitleTextView.setTextAppearance(context, mTitleTextAppearance);
500 }
501 if (mTitleTextView.getParent() == null) {
502 addSystemView(mTitleTextView);
503 }
504 } else if (mTitleTextView != null && mTitleTextView.getParent() != null) {
505 removeView(mTitleTextView);
506 }
507 if (mTitleTextView != null) {
508 mTitleTextView.setText(title);
509 }
510 mTitleText = title;
511 }
512
513 /**
514 * Return the subtitle of this toolbar.
515 *
516 * @return The current subtitle
517 */
518 public CharSequence getSubtitle() {
519 return mSubtitleText;
520 }
521
522 /**
523 * Set the subtitle of this toolbar.
524 *
525 * <p>Subtitles should express extended information about the current content.</p>
526 *
527 * @param resId String resource ID
528 */
529 public void setSubtitle(int resId) {
530 setSubtitle(getContext().getText(resId));
531 }
532
533 /**
534 * Set the subtitle of this toolbar.
535 *
536 * <p>Subtitles should express extended information about the current content.</p>
537 *
538 * @param subtitle Subtitle to set
539 */
540 public void setSubtitle(CharSequence subtitle) {
541 if (!TextUtils.isEmpty(subtitle)) {
542 if (mSubtitleTextView == null) {
543 final Context context = getContext();
544 mSubtitleTextView = new TextView(context);
Adam Powelle021e6e2014-05-23 17:27:24 -0700545 mSubtitleTextView.setSingleLine();
546 mSubtitleTextView.setEllipsize(TextUtils.TruncateAt.END);
Adam Powelle43340c2014-03-17 19:10:43 -0700547 mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance);
548 }
549 if (mSubtitleTextView.getParent() == null) {
550 addSystemView(mSubtitleTextView);
551 }
552 } else if (mSubtitleTextView != null && mSubtitleTextView.getParent() != null) {
553 removeView(mSubtitleTextView);
554 }
555 if (mSubtitleTextView != null) {
556 mSubtitleTextView.setText(subtitle);
557 }
558 mSubtitleText = subtitle;
559 }
560
561 /**
Adam Powellebba5d42014-05-30 16:19:16 -0700562 * Sets the text color, size, style, hint color, and highlight color
563 * from the specified TextAppearance resource.
564 */
565 public void setTitleTextAppearance(Context context, int resId) {
566 mTitleTextAppearance = resId;
567 if (mTitleTextView != null) {
568 mTitleTextView.setTextAppearance(context, resId);
569 }
570 }
571
572 /**
573 * Sets the text color, size, style, hint color, and highlight color
574 * from the specified TextAppearance resource.
575 */
576 public void setSubtitleTextAppearance(Context context, int resId) {
577 mSubtitleTextAppearance = resId;
578 if (mSubtitleTextView != null) {
579 mSubtitleTextView.setTextAppearance(context, resId);
580 }
581 }
582
583 /**
Adam Powelle43340c2014-03-17 19:10:43 -0700584 * Set the icon to use for the toolbar's navigation button.
585 *
586 * <p>The navigation button appears at the start of the toolbar if present. Setting an icon
587 * will make the navigation button visible.</p>
588 *
589 * <p>If you use a navigation icon you should also set a description for its action using
590 * {@link #setNavigationDescription(int)}. This is used for accessibility and tooltips.</p>
591 *
592 * @param resId Resource ID of a drawable to set
593 */
594 public void setNavigationIcon(int resId) {
595 setNavigationIcon(getContext().getDrawable(resId));
596 }
597
598 /**
Adam Powelle021e6e2014-05-23 17:27:24 -0700599 * Set a content description for the navigation button if one is present. The content
600 * description will be read via screen readers or other accessibility systems to explain
601 * the action of the navigation button.
602 *
603 * @param description Content description to set
604 */
605 public void setNavigationContentDescription(CharSequence description) {
606 ensureNavButtonView();
607 mNavButtonView.setContentDescription(description);
608 }
609
610 /**
611 * Set a content description for the navigation button if one is present. The content
612 * description will be read via screen readers or other accessibility systems to explain
613 * the action of the navigation button.
614 *
615 * @param resId Resource ID of a content description string to set
616 */
617 public void setNavigationContentDescription(int resId) {
618 ensureNavButtonView();
Adam Powell8e5372f2014-05-29 12:16:09 -0700619 mNavButtonView.setContentDescription(resId != 0 ? getContext().getText(resId) : null);
Adam Powelle021e6e2014-05-23 17:27:24 -0700620 }
621
622 /**
Adam Powelle43340c2014-03-17 19:10:43 -0700623 * Set the icon to use for the toolbar's navigation button.
624 *
625 * <p>The navigation button appears at the start of the toolbar if present. Setting an icon
626 * will make the navigation button visible.</p>
627 *
628 * <p>If you use a navigation icon you should also set a description for its action using
629 * {@link #setNavigationDescription(int)}. This is used for accessibility and tooltips.</p>
630 *
631 * @param icon Drawable to set
632 */
633 public void setNavigationIcon(Drawable icon) {
634 if (icon != null) {
635 ensureNavButtonView();
636 if (mNavButtonView.getParent() == null) {
637 addSystemView(mNavButtonView);
638 }
639 } else if (mNavButtonView != null && mNavButtonView.getParent() != null) {
640 removeView(mNavButtonView);
641 }
642 if (mNavButtonView != null) {
643 mNavButtonView.setImageDrawable(icon);
644 }
645 }
646
647 /**
648 * Return the current drawable used as the navigation icon.
649 *
650 * @return The navigation icon drawable
651 */
652 public Drawable getNavigationIcon() {
653 return mNavButtonView != null ? mNavButtonView.getDrawable() : null;
654 }
655
656 /**
657 * Set a description for the navigation button.
658 *
659 * <p>This description string is used for accessibility, tooltips and other facilities
660 * to improve discoverability.</p>
661 *
662 * @param resId Resource ID of a string to set
663 */
664 public void setNavigationDescription(int resId) {
665 setNavigationDescription(getContext().getText(resId));
666 }
667
668 /**
669 * Set a description for the navigation button.
670 *
671 * <p>This description string is used for accessibility, tooltips and other facilities
672 * to improve discoverability.</p>
673 *
674 * @param description String to set as the description
675 */
676 public void setNavigationDescription(CharSequence description) {
677 if (!TextUtils.isEmpty(description)) {
678 ensureNavButtonView();
679 }
680 if (mNavButtonView != null) {
681 mNavButtonView.setContentDescription(description);
682 }
683 }
684
685 /**
686 * Set a listener to respond to navigation events.
687 *
688 * <p>This listener will be called whenever the user clicks the navigation button
689 * at the start of the toolbar. An icon must be set for the navigation button to appear.</p>
690 *
691 * @param listener Listener to set
692 * @see #setNavigationIcon(android.graphics.drawable.Drawable)
693 */
694 public void setNavigationOnClickListener(OnClickListener listener) {
695 ensureNavButtonView();
696 mNavButtonView.setOnClickListener(listener);
697 }
698
699 /**
700 * Return the Menu shown in the toolbar.
701 *
702 * <p>Applications that wish to populate the toolbar's menu can do so from here. To use
703 * an XML menu resource, use {@link #inflateMenu(int)}.</p>
704 *
705 * @return The toolbar's Menu
706 */
707 public Menu getMenu() {
Adam Powell07a74542014-05-30 15:52:44 -0700708 ensureMenu();
Adam Powelle021e6e2014-05-23 17:27:24 -0700709 return mMenuView.getMenu();
710 }
711
Adam Powell07a74542014-05-30 15:52:44 -0700712 private void ensureMenu() {
713 ensureMenuView();
714 if (mMenuView.peekMenu() == null) {
715 // Initialize a new menu for the first time.
716 final MenuBuilder menu = (MenuBuilder) mMenuView.getMenu();
717 if (mExpandedMenuPresenter == null) {
718 mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
719 }
720 mMenuView.setExpandedActionViewsExclusive(true);
721 menu.addMenuPresenter(mExpandedMenuPresenter);
722 }
723 }
724
Adam Powelle021e6e2014-05-23 17:27:24 -0700725 private void ensureMenuView() {
Adam Powelle43340c2014-03-17 19:10:43 -0700726 if (mMenuView == null) {
727 mMenuView = new ActionMenuView(getContext());
728 mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener);
Adam Powelle021e6e2014-05-23 17:27:24 -0700729 final LayoutParams lp = generateDefaultLayoutParams();
730 lp.gravity = Gravity.END | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
731 mMenuView.setLayoutParams(lp);
Adam Powelle43340c2014-03-17 19:10:43 -0700732 addSystemView(mMenuView);
733 }
Adam Powelle43340c2014-03-17 19:10:43 -0700734 }
735
736 private MenuInflater getMenuInflater() {
737 return new MenuInflater(getContext());
738 }
739
740 /**
741 * Inflate a menu resource into this toolbar.
742 *
743 * <p>Inflate an XML menu resource into this toolbar. Existing items in the menu will not
744 * be modified or removed.</p>
745 *
746 * @param resId ID of a menu resource to inflate
747 */
748 public void inflateMenu(int resId) {
749 getMenuInflater().inflate(resId, getMenu());
750 }
751
752 /**
753 * Set a listener to respond to menu item click events.
754 *
755 * <p>This listener will be invoked whenever a user selects a menu item from
756 * the action buttons presented at the end of the toolbar or the associated overflow.</p>
757 *
758 * @param listener Listener to set
759 */
760 public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
761 mOnMenuItemClickListener = listener;
762 }
763
Adam Powell76d8f962014-05-14 20:13:47 -0700764 /**
765 * Set the content insets for this toolbar relative to layout direction.
766 *
767 * <p>The content inset affects the valid area for Toolbar content other than
768 * the navigation button and menu. Insets define the minimum margin for these components
769 * and can be used to effectively align Toolbar content along well-known gridlines.</p>
770 *
771 * @param contentInsetStart Content inset for the toolbar starting edge
772 * @param contentInsetEnd Content inset for the toolbar ending edge
773 *
774 * @see #setContentInsetsAbsolute(int, int)
775 * @see #getContentInsetStart()
776 * @see #getContentInsetEnd()
777 * @see #getContentInsetLeft()
778 * @see #getContentInsetRight()
779 */
780 public void setContentInsetsRelative(int contentInsetStart, int contentInsetEnd) {
781 mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
782 }
783
784 /**
785 * Get the starting content inset for this toolbar.
786 *
787 * <p>The content inset affects the valid area for Toolbar content other than
788 * the navigation button and menu. Insets define the minimum margin for these components
789 * and can be used to effectively align Toolbar content along well-known gridlines.</p>
790 *
791 * @return The starting content inset for this toolbar
792 *
793 * @see #setContentInsetsRelative(int, int)
794 * @see #setContentInsetsAbsolute(int, int)
795 * @see #getContentInsetEnd()
796 * @see #getContentInsetLeft()
797 * @see #getContentInsetRight()
798 */
799 public int getContentInsetStart() {
800 return mContentInsets.getStart();
801 }
802
803 /**
804 * Get the ending content inset for this toolbar.
805 *
806 * <p>The content inset affects the valid area for Toolbar content other than
807 * the navigation button and menu. Insets define the minimum margin for these components
808 * and can be used to effectively align Toolbar content along well-known gridlines.</p>
809 *
810 * @return The ending content inset for this toolbar
811 *
812 * @see #setContentInsetsRelative(int, int)
813 * @see #setContentInsetsAbsolute(int, int)
814 * @see #getContentInsetStart()
815 * @see #getContentInsetLeft()
816 * @see #getContentInsetRight()
817 */
818 public int getContentInsetEnd() {
819 return mContentInsets.getEnd();
820 }
821
822 /**
823 * Set the content insets for this toolbar.
824 *
825 * <p>The content inset affects the valid area for Toolbar content other than
826 * the navigation button and menu. Insets define the minimum margin for these components
827 * and can be used to effectively align Toolbar content along well-known gridlines.</p>
828 *
829 * @param contentInsetLeft Content inset for the toolbar's left edge
830 * @param contentInsetRight Content inset for the toolbar's right edge
831 *
832 * @see #setContentInsetsAbsolute(int, int)
833 * @see #getContentInsetStart()
834 * @see #getContentInsetEnd()
835 * @see #getContentInsetLeft()
836 * @see #getContentInsetRight()
837 */
838 public void setContentInsetsAbsolute(int contentInsetLeft, int contentInsetRight) {
839 mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight);
840 }
841
842 /**
843 * Get the left content inset for this toolbar.
844 *
845 * <p>The content inset affects the valid area for Toolbar content other than
846 * the navigation button and menu. Insets define the minimum margin for these components
847 * and can be used to effectively align Toolbar content along well-known gridlines.</p>
848 *
849 * @return The left content inset for this toolbar
850 *
851 * @see #setContentInsetsRelative(int, int)
852 * @see #setContentInsetsAbsolute(int, int)
853 * @see #getContentInsetStart()
854 * @see #getContentInsetEnd()
855 * @see #getContentInsetRight()
856 */
857 public int getContentInsetLeft() {
858 return mContentInsets.getLeft();
859 }
860
861 /**
862 * Get the right content inset for this toolbar.
863 *
864 * <p>The content inset affects the valid area for Toolbar content other than
865 * the navigation button and menu. Insets define the minimum margin for these components
866 * and can be used to effectively align Toolbar content along well-known gridlines.</p>
867 *
868 * @return The right content inset for this toolbar
869 *
870 * @see #setContentInsetsRelative(int, int)
871 * @see #setContentInsetsAbsolute(int, int)
872 * @see #getContentInsetStart()
873 * @see #getContentInsetEnd()
874 * @see #getContentInsetLeft()
875 */
876 public int getContentInsetRight() {
877 return mContentInsets.getRight();
878 }
879
Adam Powelle43340c2014-03-17 19:10:43 -0700880 private void ensureNavButtonView() {
881 if (mNavButtonView == null) {
Adam Powelle021e6e2014-05-23 17:27:24 -0700882 mNavButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle);
883 final LayoutParams lp = generateDefaultLayoutParams();
884 lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
885 mNavButtonView.setLayoutParams(lp);
886 }
887 }
888
889 private void ensureCollapseButtonView() {
890 if (mCollapseButtonView == null) {
891 mCollapseButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle);
892 mCollapseButtonView.setImageDrawable(mCollapseIcon);
893 final LayoutParams lp = generateDefaultLayoutParams();
894 lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
895 lp.mViewType = LayoutParams.EXPANDED;
896 mCollapseButtonView.setLayoutParams(lp);
897 mCollapseButtonView.setOnClickListener(new OnClickListener() {
898 @Override
899 public void onClick(View v) {
900 collapseActionView();
901 }
902 });
Adam Powelle43340c2014-03-17 19:10:43 -0700903 }
904 }
905
906 private void addSystemView(View v) {
907 final LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
908 LayoutParams.WRAP_CONTENT);
909 lp.mViewType = LayoutParams.SYSTEM;
910 addView(v, lp);
911 }
912
913 @Override
914 protected Parcelable onSaveInstanceState() {
915 SavedState state = new SavedState(super.onSaveInstanceState());
916 return state;
917 }
918
919 @Override
920 protected void onRestoreInstanceState(Parcelable state) {
921 final SavedState ss = (SavedState) state;
922 super.onRestoreInstanceState(ss.getSuperState());
923 }
924
Adam Powelle021e6e2014-05-23 17:27:24 -0700925 private void measureChildConstrained(View child, int parentWidthSpec, int widthUsed,
926 int parentHeightSpec, int heightUsed, int heightConstraint) {
927 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
928
929 int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
930 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
931 + widthUsed, lp.width);
932 int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
933 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
934 + heightUsed, lp.height);
935
936 final int childHeightMode = MeasureSpec.getMode(childHeightSpec);
937 if (childHeightMode != MeasureSpec.EXACTLY && heightConstraint >= 0) {
938 final int size = childHeightMode != MeasureSpec.UNSPECIFIED ?
939 Math.min(MeasureSpec.getSize(childHeightSpec), heightConstraint) :
940 heightConstraint;
941 childHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
942 }
943 child.measure(childWidthSpec, childHeightSpec);
944 }
945
Adam Powell7a324012014-05-30 18:52:37 -0700946 /**
947 * Returns the width + uncollapsed margins
948 */
949 private int measureChildCollapseMargins(View child,
950 int parentWidthMeasureSpec, int widthUsed,
951 int parentHeightMeasureSpec, int heightUsed, int[] collapsingMargins) {
952 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
953
954 final int leftDiff = lp.leftMargin - collapsingMargins[0];
955 final int rightDiff = lp.rightMargin - collapsingMargins[1];
956 final int leftMargin = Math.max(0, leftDiff);
957 final int rightMargin = Math.max(0, rightDiff);
958 final int hMargins = leftMargin + rightMargin;
959 collapsingMargins[0] = Math.max(0, -leftDiff);
960 collapsingMargins[1] = Math.max(0, -rightDiff);
961
962 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
963 mPaddingLeft + mPaddingRight + hMargins + widthUsed, lp.width);
964 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
965 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
966 + heightUsed, lp.height);
967
968 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
969 return child.getMeasuredWidth() + hMargins;
970 }
971
Adam Powelle43340c2014-03-17 19:10:43 -0700972 @Override
973 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
974 int width = 0;
975 int height = 0;
976 int childState = 0;
977
Adam Powell7a324012014-05-30 18:52:37 -0700978 final int[] collapsingMargins = mTempMargins;
979 final int marginStartIndex;
980 final int marginEndIndex;
981 if (isLayoutRtl()) {
982 marginStartIndex = 1;
983 marginEndIndex = 0;
984 } else {
985 marginStartIndex = 0;
986 marginEndIndex = 1;
987 }
988
Adam Powelle43340c2014-03-17 19:10:43 -0700989 // System views measure first.
990
Adam Powell76d8f962014-05-14 20:13:47 -0700991 int navWidth = 0;
Adam Powelle43340c2014-03-17 19:10:43 -0700992 if (shouldLayout(mNavButtonView)) {
Adam Powelle021e6e2014-05-23 17:27:24 -0700993 measureChildConstrained(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0,
994 mMaxButtonHeight);
Adam Powell76d8f962014-05-14 20:13:47 -0700995 navWidth = mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView);
Adam Powelle43340c2014-03-17 19:10:43 -0700996 height = Math.max(height, mNavButtonView.getMeasuredHeight() +
997 getVerticalMargins(mNavButtonView));
998 childState = combineMeasuredStates(childState, mNavButtonView.getMeasuredState());
999 }
1000
Adam Powelle021e6e2014-05-23 17:27:24 -07001001 if (shouldLayout(mCollapseButtonView)) {
1002 measureChildConstrained(mCollapseButtonView, widthMeasureSpec, width,
1003 heightMeasureSpec, 0, mMaxButtonHeight);
1004 navWidth = mCollapseButtonView.getMeasuredWidth() +
1005 getHorizontalMargins(mCollapseButtonView);
1006 height = Math.max(height, mCollapseButtonView.getMeasuredHeight() +
1007 getVerticalMargins(mCollapseButtonView));
1008 childState = combineMeasuredStates(childState, mCollapseButtonView.getMeasuredState());
1009 }
1010
Adam Powell7a324012014-05-30 18:52:37 -07001011 final int contentInsetStart = getContentInsetStart();
1012 width += Math.max(contentInsetStart, navWidth);
1013 collapsingMargins[marginStartIndex] = Math.max(0, contentInsetStart - navWidth);
Adam Powell76d8f962014-05-14 20:13:47 -07001014
1015 int menuWidth = 0;
Adam Powelle43340c2014-03-17 19:10:43 -07001016 if (shouldLayout(mMenuView)) {
Adam Powelle021e6e2014-05-23 17:27:24 -07001017 measureChildConstrained(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0,
1018 mMaxButtonHeight);
Adam Powell76d8f962014-05-14 20:13:47 -07001019 menuWidth = mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView);
Adam Powelle43340c2014-03-17 19:10:43 -07001020 height = Math.max(height, mMenuView.getMeasuredHeight() +
1021 getVerticalMargins(mMenuView));
1022 childState = combineMeasuredStates(childState, mMenuView.getMeasuredState());
1023 }
1024
Adam Powell7a324012014-05-30 18:52:37 -07001025 final int contentInsetEnd = getContentInsetEnd();
1026 width += Math.max(contentInsetEnd, menuWidth);
1027 collapsingMargins[marginEndIndex] = Math.max(0, contentInsetEnd - menuWidth);
Adam Powell76d8f962014-05-14 20:13:47 -07001028
Adam Powelle021e6e2014-05-23 17:27:24 -07001029 if (shouldLayout(mExpandedActionView)) {
Adam Powell7a324012014-05-30 18:52:37 -07001030 width += measureChildCollapseMargins(mExpandedActionView, widthMeasureSpec, width,
1031 heightMeasureSpec, 0, collapsingMargins);
Adam Powelle021e6e2014-05-23 17:27:24 -07001032 height = Math.max(height, mExpandedActionView.getMeasuredHeight() +
1033 getVerticalMargins(mExpandedActionView));
1034 childState = combineMeasuredStates(childState, mExpandedActionView.getMeasuredState());
1035 }
1036
Adam Powelle43340c2014-03-17 19:10:43 -07001037 if (shouldLayout(mLogoView)) {
Adam Powell7a324012014-05-30 18:52:37 -07001038 width += measureChildCollapseMargins(mLogoView, widthMeasureSpec, width,
1039 heightMeasureSpec, 0, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001040 height = Math.max(height, mLogoView.getMeasuredHeight() +
1041 getVerticalMargins(mLogoView));
1042 childState = combineMeasuredStates(childState, mLogoView.getMeasuredState());
1043 }
1044
1045 int titleWidth = 0;
1046 int titleHeight = 0;
1047 final int titleVertMargins = mTitleMarginTop + mTitleMarginBottom;
1048 final int titleHorizMargins = mTitleMarginStart + mTitleMarginEnd;
1049 if (shouldLayout(mTitleTextView)) {
Adam Powell7a324012014-05-30 18:52:37 -07001050 titleWidth = measureChildCollapseMargins(mTitleTextView, widthMeasureSpec,
1051 width + titleHorizMargins, heightMeasureSpec, titleVertMargins,
1052 collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001053 titleWidth = mTitleTextView.getMeasuredWidth() + getHorizontalMargins(mTitleTextView);
1054 titleHeight = mTitleTextView.getMeasuredHeight() + getVerticalMargins(mTitleTextView);
1055 childState = combineMeasuredStates(childState, mTitleTextView.getMeasuredState());
1056 }
1057 if (shouldLayout(mSubtitleTextView)) {
Adam Powell7a324012014-05-30 18:52:37 -07001058 titleWidth = Math.max(titleWidth, measureChildCollapseMargins(mSubtitleTextView,
1059 widthMeasureSpec, width + titleHorizMargins,
1060 heightMeasureSpec, titleHeight + titleVertMargins,
1061 collapsingMargins));
Adam Powelle43340c2014-03-17 19:10:43 -07001062 titleHeight += mSubtitleTextView.getMeasuredHeight() +
1063 getVerticalMargins(mSubtitleTextView);
1064 childState = combineMeasuredStates(childState, mSubtitleTextView.getMeasuredState());
1065 }
1066
1067 width += titleWidth;
1068 height = Math.max(height, titleHeight);
1069
1070 final int childCount = getChildCount();
1071 for (int i = 0; i < childCount; i++) {
1072 final View child = getChildAt(i);
1073 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Adam Powelle021e6e2014-05-23 17:27:24 -07001074 if (lp.mViewType != LayoutParams.CUSTOM || !shouldLayout(child)) {
Adam Powelle43340c2014-03-17 19:10:43 -07001075 // We already got all system views above. Skip them and GONE views.
1076 continue;
1077 }
1078
Adam Powell7a324012014-05-30 18:52:37 -07001079 width += measureChildCollapseMargins(child, widthMeasureSpec, width,
1080 heightMeasureSpec, 0, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001081 height = Math.max(height, child.getMeasuredHeight() + getVerticalMargins(child));
1082 childState = combineMeasuredStates(childState, child.getMeasuredState());
1083 }
1084
1085 // Measurement already took padding into account for available space for the children,
1086 // add it in for the final size.
1087 width += getPaddingLeft() + getPaddingRight();
1088 height += getPaddingTop() + getPaddingBottom();
1089
1090 final int measuredWidth = resolveSizeAndState(
1091 Math.max(width, getSuggestedMinimumWidth()),
1092 widthMeasureSpec, childState & MEASURED_STATE_MASK);
1093 final int measuredHeight = resolveSizeAndState(
1094 Math.max(height, getSuggestedMinimumHeight()),
1095 heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT);
1096 setMeasuredDimension(measuredWidth, measuredHeight);
1097 }
1098
1099 @Override
1100 protected void onLayout(boolean changed, int l, int t, int r, int b) {
1101 final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
1102 final int width = getWidth();
1103 final int height = getHeight();
1104 final int paddingLeft = getPaddingLeft();
1105 final int paddingRight = getPaddingRight();
1106 final int paddingTop = getPaddingTop();
1107 final int paddingBottom = getPaddingBottom();
1108 int left = paddingLeft;
1109 int right = width - paddingRight;
1110
Adam Powell7a324012014-05-30 18:52:37 -07001111 final int[] collapsingMargins = mTempMargins;
1112 collapsingMargins[0] = collapsingMargins[1] = 0;
1113
Adam Powelle43340c2014-03-17 19:10:43 -07001114 if (shouldLayout(mNavButtonView)) {
1115 if (isRtl) {
Adam Powell7a324012014-05-30 18:52:37 -07001116 right = layoutChildRight(mNavButtonView, right, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001117 } else {
Adam Powell7a324012014-05-30 18:52:37 -07001118 left = layoutChildLeft(mNavButtonView, left, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001119 }
1120 }
1121
Adam Powelle021e6e2014-05-23 17:27:24 -07001122 if (shouldLayout(mCollapseButtonView)) {
1123 if (isRtl) {
Adam Powell7a324012014-05-30 18:52:37 -07001124 right = layoutChildRight(mCollapseButtonView, right, collapsingMargins);
Adam Powelle021e6e2014-05-23 17:27:24 -07001125 } else {
Adam Powell7a324012014-05-30 18:52:37 -07001126 left = layoutChildLeft(mCollapseButtonView, left, collapsingMargins);
Adam Powelle021e6e2014-05-23 17:27:24 -07001127 }
1128 }
1129
Adam Powelle43340c2014-03-17 19:10:43 -07001130 if (shouldLayout(mMenuView)) {
1131 if (isRtl) {
Adam Powell7a324012014-05-30 18:52:37 -07001132 left = layoutChildLeft(mMenuView, left, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001133 } else {
Adam Powell7a324012014-05-30 18:52:37 -07001134 right = layoutChildRight(mMenuView, right, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001135 }
1136 }
1137
Adam Powell7a324012014-05-30 18:52:37 -07001138 collapsingMargins[0] = Math.max(0, getContentInsetLeft() - left);
1139 collapsingMargins[1] = Math.max(0, getContentInsetRight() - (width - paddingRight - right));
Adam Powell76d8f962014-05-14 20:13:47 -07001140 left = Math.max(left, getContentInsetLeft());
1141 right = Math.min(right, width - paddingRight - getContentInsetRight());
1142
Adam Powelle021e6e2014-05-23 17:27:24 -07001143 if (shouldLayout(mExpandedActionView)) {
1144 if (isRtl) {
Adam Powell7a324012014-05-30 18:52:37 -07001145 right = layoutChildRight(mExpandedActionView, right, collapsingMargins);
Adam Powelle021e6e2014-05-23 17:27:24 -07001146 } else {
Adam Powell7a324012014-05-30 18:52:37 -07001147 left = layoutChildLeft(mExpandedActionView, left, collapsingMargins);
Adam Powelle021e6e2014-05-23 17:27:24 -07001148 }
1149 }
1150
Adam Powelle43340c2014-03-17 19:10:43 -07001151 if (shouldLayout(mLogoView)) {
1152 if (isRtl) {
Adam Powell7a324012014-05-30 18:52:37 -07001153 right = layoutChildRight(mLogoView, right, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001154 } else {
Adam Powell7a324012014-05-30 18:52:37 -07001155 left = layoutChildLeft(mLogoView, left, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001156 }
1157 }
1158
1159 final boolean layoutTitle = shouldLayout(mTitleTextView);
1160 final boolean layoutSubtitle = shouldLayout(mSubtitleTextView);
1161 int titleHeight = 0;
1162 if (layoutTitle) {
1163 final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
1164 titleHeight += lp.topMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin;
1165 }
1166 if (layoutSubtitle) {
1167 final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
1168 titleHeight += lp.bottomMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin;
1169 }
1170
1171 if (layoutTitle || layoutSubtitle) {
1172 int titleTop;
Adam Powelle021e6e2014-05-23 17:27:24 -07001173 final View topChild = layoutTitle ? mTitleTextView : mSubtitleTextView;
1174 final View bottomChild = layoutSubtitle ? mSubtitleTextView : mTitleTextView;
1175 final LayoutParams toplp = (LayoutParams) topChild.getLayoutParams();
1176 final LayoutParams bottomlp = (LayoutParams) bottomChild.getLayoutParams();
1177
Adam Powelle43340c2014-03-17 19:10:43 -07001178 switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
1179 case Gravity.TOP:
Adam Powelle021e6e2014-05-23 17:27:24 -07001180 titleTop = getPaddingTop() + toplp.topMargin + mTitleMarginTop;
Adam Powelle43340c2014-03-17 19:10:43 -07001181 break;
1182 default:
1183 case Gravity.CENTER_VERTICAL:
Adam Powelle43340c2014-03-17 19:10:43 -07001184 final int space = height - paddingTop - paddingBottom;
1185 int spaceAbove = (space - titleHeight) / 2;
Adam Powelle021e6e2014-05-23 17:27:24 -07001186 if (spaceAbove < toplp.topMargin + mTitleMarginTop) {
1187 spaceAbove = toplp.topMargin + mTitleMarginTop;
Adam Powelle43340c2014-03-17 19:10:43 -07001188 } else {
1189 final int spaceBelow = height - paddingBottom - titleHeight -
1190 spaceAbove - paddingTop;
Adam Powelle021e6e2014-05-23 17:27:24 -07001191 if (spaceBelow < toplp.bottomMargin + mTitleMarginBottom) {
Adam Powelle43340c2014-03-17 19:10:43 -07001192 spaceAbove = Math.max(0, spaceAbove -
Adam Powelle021e6e2014-05-23 17:27:24 -07001193 (bottomlp.bottomMargin + mTitleMarginBottom - spaceBelow));
Adam Powelle43340c2014-03-17 19:10:43 -07001194 }
1195 }
1196 titleTop = paddingTop + spaceAbove;
1197 break;
1198 case Gravity.BOTTOM:
Adam Powelle021e6e2014-05-23 17:27:24 -07001199 titleTop = height - paddingBottom - bottomlp.bottomMargin - mTitleMarginBottom -
1200 titleHeight;
Adam Powelle43340c2014-03-17 19:10:43 -07001201 break;
1202 }
1203 if (isRtl) {
Adam Powell7a324012014-05-30 18:52:37 -07001204 final int rd = mTitleMarginStart - collapsingMargins[1];
1205 right -= Math.max(0, rd);
1206 collapsingMargins[1] = Math.max(0, -rd);
Adam Powelle43340c2014-03-17 19:10:43 -07001207 int titleRight = right;
1208 int subtitleRight = right;
Adam Powell7a324012014-05-30 18:52:37 -07001209
Adam Powelle43340c2014-03-17 19:10:43 -07001210 if (layoutTitle) {
1211 final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
Adam Powelle43340c2014-03-17 19:10:43 -07001212 final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth();
1213 final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
1214 mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
Adam Powell7a324012014-05-30 18:52:37 -07001215 titleRight = titleLeft - mTitleMarginEnd;
Adam Powelle43340c2014-03-17 19:10:43 -07001216 titleTop = titleBottom + lp.bottomMargin;
1217 }
1218 if (layoutSubtitle) {
1219 final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
Adam Powelle43340c2014-03-17 19:10:43 -07001220 titleTop += lp.topMargin;
1221 final int subtitleLeft = subtitleRight - mSubtitleTextView.getMeasuredWidth();
1222 final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
1223 mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
Adam Powell7a324012014-05-30 18:52:37 -07001224 subtitleRight = subtitleRight - mTitleMarginEnd;
Adam Powelle43340c2014-03-17 19:10:43 -07001225 titleTop = subtitleBottom + lp.bottomMargin;
1226 }
1227 right = Math.max(titleRight, subtitleRight);
1228 } else {
Adam Powell7a324012014-05-30 18:52:37 -07001229 final int ld = mTitleMarginStart - collapsingMargins[0];
1230 left += Math.max(0, ld);
1231 collapsingMargins[0] = Math.max(0, -ld);
Adam Powelle43340c2014-03-17 19:10:43 -07001232 int titleLeft = left;
1233 int subtitleLeft = left;
Adam Powell7a324012014-05-30 18:52:37 -07001234
Adam Powelle43340c2014-03-17 19:10:43 -07001235 if (layoutTitle) {
1236 final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
Adam Powelle43340c2014-03-17 19:10:43 -07001237 final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth();
1238 final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
1239 mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
Adam Powell7a324012014-05-30 18:52:37 -07001240 titleLeft = titleRight + mTitleMarginEnd;
Adam Powelle43340c2014-03-17 19:10:43 -07001241 titleTop = titleBottom + lp.bottomMargin;
1242 }
1243 if (layoutSubtitle) {
1244 final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
Adam Powelle43340c2014-03-17 19:10:43 -07001245 titleTop += lp.topMargin;
1246 final int subtitleRight = subtitleLeft + mSubtitleTextView.getMeasuredWidth();
1247 final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
1248 mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
Adam Powell7a324012014-05-30 18:52:37 -07001249 subtitleLeft = subtitleRight + mTitleMarginEnd;
Adam Powelle43340c2014-03-17 19:10:43 -07001250 titleTop = subtitleBottom + lp.bottomMargin;
1251 }
1252 left = Math.max(titleLeft, subtitleLeft);
1253 }
1254 }
1255
1256 // Get all remaining children sorted for layout. This is all prepared
1257 // such that absolute layout direction can be used below.
1258
1259 addCustomViewsWithGravity(mTempViews, Gravity.LEFT);
1260 final int leftViewsCount = mTempViews.size();
1261 for (int i = 0; i < leftViewsCount; i++) {
Adam Powell7a324012014-05-30 18:52:37 -07001262 left = layoutChildLeft(mTempViews.get(i), left, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001263 }
1264
1265 addCustomViewsWithGravity(mTempViews, Gravity.RIGHT);
1266 final int rightViewsCount = mTempViews.size();
1267 for (int i = 0; i < rightViewsCount; i++) {
Adam Powell7a324012014-05-30 18:52:37 -07001268 right = layoutChildRight(mTempViews.get(i), right, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001269 }
1270
1271 // Centered views try to center with respect to the whole bar, but views pinned
1272 // 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 -07001273 addCustomViewsWithGravity(mTempViews, Gravity.CENTER_HORIZONTAL);
Adam Powell7a324012014-05-30 18:52:37 -07001274 final int centerViewsWidth = getViewListMeasuredWidth(mTempViews, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001275 final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2;
1276 final int halfCenterViewsWidth = centerViewsWidth / 2;
1277 int centerLeft = parentCenter - halfCenterViewsWidth;
1278 final int centerRight = centerLeft + centerViewsWidth;
1279 if (centerLeft < left) {
1280 centerLeft = left;
1281 } else if (centerRight > right) {
1282 centerLeft -= centerRight - right;
1283 }
1284
1285 final int centerViewsCount = mTempViews.size();
1286 for (int i = 0; i < centerViewsCount; i++) {
Adam Powell7a324012014-05-30 18:52:37 -07001287 centerLeft = layoutChildLeft(mTempViews.get(i), centerLeft, collapsingMargins);
Adam Powelle43340c2014-03-17 19:10:43 -07001288 }
1289 mTempViews.clear();
1290 }
1291
Adam Powell7a324012014-05-30 18:52:37 -07001292 private int getViewListMeasuredWidth(List<View> views, int[] collapsingMargins) {
1293 int collapseLeft = collapsingMargins[0];
1294 int collapseRight = collapsingMargins[1];
Adam Powelle43340c2014-03-17 19:10:43 -07001295 int width = 0;
1296 final int count = views.size();
1297 for (int i = 0; i < count; i++) {
1298 final View v = views.get(i);
1299 final LayoutParams lp = (LayoutParams) v.getLayoutParams();
Adam Powell7a324012014-05-30 18:52:37 -07001300 final int l = lp.leftMargin - collapseLeft;
1301 final int r = lp.rightMargin - collapseRight;
1302 final int leftMargin = Math.max(0, l);
1303 final int rightMargin = Math.max(0, r);
1304 collapseLeft = Math.max(0, -l);
1305 collapseRight = Math.max(0, -r);
1306 width += leftMargin + v.getMeasuredWidth() + rightMargin;
Adam Powelle43340c2014-03-17 19:10:43 -07001307 }
1308 return width;
1309 }
1310
Adam Powell7a324012014-05-30 18:52:37 -07001311 private int layoutChildLeft(View child, int left, int[] collapsingMargins) {
Adam Powelle43340c2014-03-17 19:10:43 -07001312 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Adam Powell7a324012014-05-30 18:52:37 -07001313 final int l = lp.leftMargin - collapsingMargins[0];
1314 left += Math.max(0, l);
1315 collapsingMargins[0] = Math.max(0, -l);
Adam Powell7a8a2842014-05-14 16:04:03 -07001316 final int top = getChildTop(child);
1317 final int childWidth = child.getMeasuredWidth();
1318 child.layout(left, top, left + childWidth, top + child.getMeasuredHeight());
1319 left += childWidth + lp.rightMargin;
Adam Powelle43340c2014-03-17 19:10:43 -07001320 return left;
1321 }
1322
Adam Powell7a324012014-05-30 18:52:37 -07001323 private int layoutChildRight(View child, int right, int[] collapsingMargins) {
Adam Powelle43340c2014-03-17 19:10:43 -07001324 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Adam Powell7a324012014-05-30 18:52:37 -07001325 final int r = lp.rightMargin - collapsingMargins[1];
1326 right -= Math.max(0, r);
1327 collapsingMargins[1] = Math.max(0, -r);
Adam Powell7a8a2842014-05-14 16:04:03 -07001328 final int top = getChildTop(child);
1329 final int childWidth = child.getMeasuredWidth();
1330 child.layout(right - childWidth, top, right, top + child.getMeasuredHeight());
1331 right -= childWidth + lp.leftMargin;
Adam Powelle43340c2014-03-17 19:10:43 -07001332 return right;
1333 }
1334
1335 private int getChildTop(View child) {
1336 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1337 switch (getChildVerticalGravity(lp.gravity)) {
1338 case Gravity.TOP:
1339 return getPaddingTop();
1340
1341 case Gravity.BOTTOM:
Adam Powelle002c2f2014-06-03 17:54:34 -07001342 return getHeight() - getPaddingBottom() -
1343 child.getMeasuredHeight() - lp.bottomMargin;
Adam Powelle43340c2014-03-17 19:10:43 -07001344
1345 default:
1346 case Gravity.CENTER_VERTICAL:
1347 final int paddingTop = getPaddingTop();
1348 final int paddingBottom = getPaddingBottom();
1349 final int height = getHeight();
1350 final int childHeight = child.getMeasuredHeight();
1351 final int space = height - paddingTop - paddingBottom;
1352 int spaceAbove = (space - childHeight) / 2;
1353 if (spaceAbove < lp.topMargin) {
1354 spaceAbove = lp.topMargin;
1355 } else {
1356 final int spaceBelow = height - paddingBottom - childHeight -
1357 spaceAbove - paddingTop;
1358 if (spaceBelow < lp.bottomMargin) {
1359 spaceAbove = Math.max(0, spaceAbove - (lp.bottomMargin - spaceBelow));
1360 }
1361 }
1362 return paddingTop + spaceAbove;
1363 }
1364 }
1365
1366 private int getChildVerticalGravity(int gravity) {
1367 final int vgrav = gravity & Gravity.VERTICAL_GRAVITY_MASK;
1368 switch (vgrav) {
1369 case Gravity.TOP:
1370 case Gravity.BOTTOM:
1371 case Gravity.CENTER_VERTICAL:
1372 return vgrav;
1373 default:
1374 return mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1375 }
1376 }
1377
1378 /**
1379 * Prepare a list of non-SYSTEM child views. If the layout direction is RTL
1380 * this will be in reverse child order.
1381 *
1382 * @param views List to populate. It will be cleared before use.
1383 * @param gravity Horizontal gravity to match against
1384 */
1385 private void addCustomViewsWithGravity(List<View> views, int gravity) {
1386 final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
1387 final int childCount = getChildCount();
1388 final int absGrav = Gravity.getAbsoluteGravity(gravity, getLayoutDirection());
1389
1390 views.clear();
1391
1392 if (isRtl) {
1393 for (int i = childCount - 1; i >= 0; i--) {
1394 final View child = getChildAt(i);
1395 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Adam Powelle021e6e2014-05-23 17:27:24 -07001396 if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) &&
Adam Powelle43340c2014-03-17 19:10:43 -07001397 getChildHorizontalGravity(lp.gravity) == absGrav) {
1398 views.add(child);
1399 }
Adam Powelle43340c2014-03-17 19:10:43 -07001400 }
1401 } else {
1402 for (int i = 0; i < childCount; i++) {
1403 final View child = getChildAt(i);
1404 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
Adam Powelle021e6e2014-05-23 17:27:24 -07001405 if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) &&
Adam Powelle43340c2014-03-17 19:10:43 -07001406 getChildHorizontalGravity(lp.gravity) == absGrav) {
1407 views.add(child);
1408 }
1409 }
1410 }
1411 }
1412
1413 private int getChildHorizontalGravity(int gravity) {
1414 final int ld = getLayoutDirection();
1415 final int absGrav = Gravity.getAbsoluteGravity(gravity, ld);
1416 final int hGrav = absGrav & Gravity.HORIZONTAL_GRAVITY_MASK;
1417 switch (hGrav) {
1418 case Gravity.LEFT:
1419 case Gravity.RIGHT:
1420 case Gravity.CENTER_HORIZONTAL:
1421 return hGrav;
1422 default:
1423 return ld == LAYOUT_DIRECTION_RTL ? Gravity.RIGHT : Gravity.LEFT;
1424 }
1425 }
1426
1427 private boolean shouldLayout(View view) {
1428 return view != null && view.getParent() == this && view.getVisibility() != GONE;
1429 }
1430
1431 private int getHorizontalMargins(View v) {
1432 final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
1433 return mlp.getMarginStart() + mlp.getMarginEnd();
1434 }
1435
1436 private int getVerticalMargins(View v) {
1437 final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
1438 return mlp.topMargin + mlp.bottomMargin;
1439 }
1440
1441 @Override
Adam Powelle021e6e2014-05-23 17:27:24 -07001442 public LayoutParams generateLayoutParams(AttributeSet attrs) {
1443 return new LayoutParams(getContext(), attrs);
Adam Powelle43340c2014-03-17 19:10:43 -07001444 }
1445
1446 @Override
Adam Powelle021e6e2014-05-23 17:27:24 -07001447 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
Adam Powelle43340c2014-03-17 19:10:43 -07001448 if (p instanceof LayoutParams) {
1449 return new LayoutParams((LayoutParams) p);
Adam Powelle021e6e2014-05-23 17:27:24 -07001450 } else if (p instanceof ActionBar.LayoutParams) {
1451 return new LayoutParams((ActionBar.LayoutParams) p);
Adam Powelle43340c2014-03-17 19:10:43 -07001452 } else if (p instanceof MarginLayoutParams) {
1453 return new LayoutParams((MarginLayoutParams) p);
1454 } else {
1455 return new LayoutParams(p);
1456 }
1457 }
1458
1459 @Override
Adam Powelle021e6e2014-05-23 17:27:24 -07001460 protected LayoutParams generateDefaultLayoutParams() {
Adam Powelle43340c2014-03-17 19:10:43 -07001461 return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1462 }
1463
1464 @Override
1465 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1466 return super.checkLayoutParams(p) && p instanceof LayoutParams;
1467 }
1468
1469 private static boolean isCustomView(View child) {
1470 return ((LayoutParams) child.getLayoutParams()).mViewType == LayoutParams.CUSTOM;
1471 }
1472
Adam Powelle021e6e2014-05-23 17:27:24 -07001473 /** @hide */
1474 public DecorToolbar getWrapper() {
1475 if (mWrapper == null) {
1476 mWrapper = new ToolbarWidgetWrapper(this);
1477 }
1478 return mWrapper;
1479 }
1480
1481 private void setChildVisibilityForExpandedActionView(boolean expand) {
1482 final int childCount = getChildCount();
1483 for (int i = 0; i < childCount; i++) {
1484 final View child = getChildAt(i);
1485 final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1486 if (lp.mViewType != LayoutParams.EXPANDED && child != mMenuView) {
1487 child.setVisibility(expand ? GONE : VISIBLE);
1488 }
1489 }
1490 }
1491
Adam Powelle43340c2014-03-17 19:10:43 -07001492 /**
1493 * Interface responsible for receiving menu item click events if the items themselves
1494 * do not have individual item click listeners.
1495 */
1496 public interface OnMenuItemClickListener {
1497 /**
1498 * This method will be invoked when a menu item is clicked if the item itself did
1499 * not already handle the event.
1500 *
1501 * @param item {@link MenuItem} that was clicked
1502 * @return <code>true</code> if the event was handled, <code>false</code> otherwise.
1503 */
1504 public boolean onMenuItemClick(MenuItem item);
1505 }
1506
1507 /**
1508 * Layout information for child views of Toolbars.
1509 *
1510 * @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity
1511 */
Adam Powelle021e6e2014-05-23 17:27:24 -07001512 public static class LayoutParams extends ActionBar.LayoutParams {
Adam Powelle43340c2014-03-17 19:10:43 -07001513 static final int CUSTOM = 0;
1514 static final int SYSTEM = 1;
Adam Powelle021e6e2014-05-23 17:27:24 -07001515 static final int EXPANDED = 2;
Adam Powelle43340c2014-03-17 19:10:43 -07001516
1517 int mViewType = CUSTOM;
1518
1519 public LayoutParams(@NonNull Context c, AttributeSet attrs) {
1520 super(c, attrs);
Adam Powelle43340c2014-03-17 19:10:43 -07001521 }
1522
1523 public LayoutParams(int width, int height) {
1524 super(width, height);
1525 this.gravity = Gravity.CENTER_VERTICAL | Gravity.START;
1526 }
1527
1528 public LayoutParams(int width, int height, int gravity) {
1529 super(width, height);
1530 this.gravity = gravity;
1531 }
1532
1533 public LayoutParams(int gravity) {
1534 this(WRAP_CONTENT, MATCH_PARENT, gravity);
1535 }
1536
1537 public LayoutParams(LayoutParams source) {
1538 super(source);
1539
Adam Powelle021e6e2014-05-23 17:27:24 -07001540 mViewType = source.mViewType;
1541 }
1542
1543 public LayoutParams(ActionBar.LayoutParams source) {
1544 super(source);
Adam Powelle43340c2014-03-17 19:10:43 -07001545 }
1546
1547 public LayoutParams(MarginLayoutParams source) {
1548 super(source);
1549 }
1550
1551 public LayoutParams(ViewGroup.LayoutParams source) {
1552 super(source);
1553 }
1554 }
1555
1556 static class SavedState extends BaseSavedState {
1557 public SavedState(Parcel source) {
1558 super(source);
1559 }
1560
1561 public SavedState(Parcelable superState) {
1562 super(superState);
1563 }
1564
1565 @Override
1566 public void writeToParcel(Parcel out, int flags) {
1567 super.writeToParcel(out, flags);
1568 }
1569
1570 public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
1571
1572 @Override
1573 public SavedState createFromParcel(Parcel source) {
1574 return new SavedState(source);
1575 }
1576
1577 @Override
1578 public SavedState[] newArray(int size) {
1579 return new SavedState[size];
1580 }
1581 };
1582 }
Adam Powelle021e6e2014-05-23 17:27:24 -07001583
1584 private class ExpandedActionViewMenuPresenter implements MenuPresenter {
1585 MenuBuilder mMenu;
1586 MenuItemImpl mCurrentExpandedItem;
1587
1588 @Override
1589 public void initForMenu(Context context, MenuBuilder menu) {
1590 // Clear the expanded action view when menus change.
1591 if (mMenu != null && mCurrentExpandedItem != null) {
1592 mMenu.collapseItemActionView(mCurrentExpandedItem);
1593 }
1594 mMenu = menu;
1595 }
1596
1597 @Override
1598 public MenuView getMenuView(ViewGroup root) {
1599 return null;
1600 }
1601
1602 @Override
1603 public void updateMenuView(boolean cleared) {
1604 // Make sure the expanded item we have is still there.
1605 if (mCurrentExpandedItem != null) {
1606 boolean found = false;
1607
1608 if (mMenu != null) {
1609 final int count = mMenu.size();
1610 for (int i = 0; i < count; i++) {
1611 final MenuItem item = mMenu.getItem(i);
1612 if (item == mCurrentExpandedItem) {
1613 found = true;
1614 break;
1615 }
1616 }
1617 }
1618
1619 if (!found) {
1620 // The item we had expanded disappeared. Collapse.
1621 collapseItemActionView(mMenu, mCurrentExpandedItem);
1622 }
1623 }
1624 }
1625
1626 @Override
1627 public void setCallback(Callback cb) {
1628 }
1629
1630 @Override
1631 public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
1632 return false;
1633 }
1634
1635 @Override
1636 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
1637 }
1638
1639 @Override
1640 public boolean flagActionItems() {
1641 return false;
1642 }
1643
1644 @Override
1645 public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
1646 ensureCollapseButtonView();
1647 if (mCollapseButtonView.getParent() != Toolbar.this) {
1648 addView(mCollapseButtonView);
1649 }
1650 mExpandedActionView = item.getActionView();
1651 mCurrentExpandedItem = item;
1652 if (mExpandedActionView.getParent() != Toolbar.this) {
1653 final LayoutParams lp = generateDefaultLayoutParams();
1654 lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK);
1655 lp.mViewType = LayoutParams.EXPANDED;
1656 mExpandedActionView.setLayoutParams(lp);
1657 addView(mExpandedActionView);
1658 }
1659
1660 setChildVisibilityForExpandedActionView(true);
1661 requestLayout();
1662 item.setActionViewExpanded(true);
1663
1664 if (mExpandedActionView instanceof CollapsibleActionView) {
1665 ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded();
1666 }
1667
1668 return true;
1669 }
1670
1671 @Override
1672 public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
1673 // Do this before detaching the actionview from the hierarchy, in case
1674 // it needs to dismiss the soft keyboard, etc.
1675 if (mExpandedActionView instanceof CollapsibleActionView) {
1676 ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed();
1677 }
1678
1679 removeView(mExpandedActionView);
1680 removeView(mCollapseButtonView);
1681 mExpandedActionView = null;
1682
1683 setChildVisibilityForExpandedActionView(false);
1684 mCurrentExpandedItem = null;
1685 requestLayout();
1686 item.setActionViewExpanded(false);
1687
1688 return true;
1689 }
1690
1691 @Override
1692 public int getId() {
1693 return 0;
1694 }
1695
1696 @Override
1697 public Parcelable onSaveInstanceState() {
1698 return null;
1699 }
1700
1701 @Override
1702 public void onRestoreInstanceState(Parcelable state) {
1703 }
1704 }
Adam Powelle43340c2014-03-17 19:10:43 -07001705}