summaryrefslogtreecommitdiffstats
path: root/core/java/android/widget/Toolbar.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/widget/Toolbar.java')
-rw-r--r--core/java/android/widget/Toolbar.java1048
1 files changed, 1048 insertions, 0 deletions
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
new file mode 100644
index 0000000..075feba
--- /dev/null
+++ b/core/java/android/widget/Toolbar.java
@@ -0,0 +1,1048 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.widget;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A standard toolbar for use within application content.
+ *
+ * <p>A Toolbar is a generalization of {@link android.app.ActionBar action bars} for use
+ * within application layouts. While an action bar is traditionally part of an
+ * {@link android.app.Activity Activity's} opaque window decor controlled by the framework,
+ * a Toolbar may be placed at any arbitrary level of nesting within a view hierarchy.
+ * An application may choose to designate a Toolbar as the action bar for an Activity
+ * using the {@link android.app.Activity#setActionBar(Toolbar) setActionBar()} method.</p>
+ *
+ * <p>Toolbar supports a more focused feature set than ActionBar. From start to end, a toolbar
+ * may contain a combination of the following optional elements:
+ *
+ * <ul>
+ * <li><em>A navigation button.</em> This may be an Up arrow, navigation menu toggle, close,
+ * collapse, done or another glyph of the app's choosing. This button should always be used
+ * to access other navigational destinations within the container of the Toolbar and
+ * its signified content or otherwise leave the current context signified by the Toolbar.</li>
+ * <li><em>A branded logo image.</em> This may extend to the height of the bar and can be
+ * arbitrarily wide.</li>
+ * <li><em>A title and subtitle.</em> The title should be a signpost for the Toolbar's current
+ * position in the navigation hierarchy and the content contained there. The subtitle,
+ * if present should indicate any extended information about the current content.
+ * If an app uses a logo image it should strongly consider omitting a title and subtitle.</li>
+ * <li><em>One or more custom views.</em> The application may add arbitrary child views
+ * to the Toolbar. They will appear at this position within the layout. If a child view's
+ * {@link LayoutParams} indicates a {@link Gravity} value of
+ * {@link Gravity#CENTER_HORIZONTAL CENTER_HORIZONTAL} the view will attempt to center
+ * within the available space remaining in the Toolbar after all other elements have been
+ * measured.</li>
+ * <li><em>An {@link ActionMenuView action menu}.</em> The menu of actions will pin to the
+ * end of the Toolbar offering a few
+ * <a href="http://developer.android.com/design/patterns/actionbar.html#ActionButtons">
+ * frequent, important or typical</a> actions along with an optional overflow menu for
+ * additional actions.</li>
+ * </ul>
+ * </p>
+ *
+ * <p>In modern Android UIs developers should lean more on a visually distinct color scheme for
+ * toolbars than on their application icon. The use of application icon plus title as a standard
+ * layout is discouraged on API 21 devices and newer.</p>
+ */
+public class Toolbar extends ViewGroup {
+ private ActionMenuView mMenuView;
+ private TextView mTitleTextView;
+ private TextView mSubtitleTextView;
+ private ImageButton mNavButtonView;
+ private ImageView mLogoView;
+
+ private int mTitleTextAppearance;
+ private int mSubtitleTextAppearance;
+ private int mTitleMarginStart;
+ private int mTitleMarginEnd;
+ private int mTitleMarginTop;
+ private int mTitleMarginBottom;
+
+ private int mGravity = Gravity.START | Gravity.CENTER_VERTICAL;
+
+ private CharSequence mTitleText;
+ private CharSequence mSubtitleText;
+
+ // Clear me after use.
+ private final ArrayList<View> mTempViews = new ArrayList<View>();
+
+ private OnMenuItemClickListener mOnMenuItemClickListener;
+
+ private final ActionMenuView.OnMenuItemClickListener mMenuViewItemClickListener =
+ new ActionMenuView.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ if (mOnMenuItemClickListener != null) {
+ return mOnMenuItemClickListener.onMenuItemClick(item);
+ }
+ return false;
+ }
+ };
+
+ public Toolbar(Context context) {
+ this(context, null);
+ }
+
+ public Toolbar(Context context, AttributeSet attrs) {
+ this(context, attrs, com.android.internal.R.attr.toolbarStyle);
+ }
+
+ public Toolbar(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public Toolbar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Toolbar,
+ defStyleAttr, defStyleRes);
+
+ mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0);
+ mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0);
+ mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity);
+ mTitleMarginStart = mTitleMarginEnd = Math.max(0, a.getDimensionPixelOffset(
+ R.styleable.Toolbar_titleMargins, -1));
+
+ final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1);
+ if (marginStart >= 0) {
+ mTitleMarginStart = marginStart;
+ }
+
+ final int marginEnd = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginEnd, -1);
+ if (marginEnd >= 0) {
+ mTitleMarginEnd = marginEnd;
+ }
+
+ final int marginTop = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginTop, -1);
+ if (marginTop >= 0) {
+ mTitleMarginTop = marginTop;
+ }
+
+ final int marginBottom = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginBottom,
+ -1);
+ if (marginBottom >= 0) {
+ mTitleMarginBottom = marginBottom;
+ }
+
+ final CharSequence title = a.getText(R.styleable.Toolbar_title);
+ if (!TextUtils.isEmpty(title)) {
+ setTitle(title);
+ }
+
+ final CharSequence subtitle = a.getText(R.styleable.Toolbar_subtitle);
+ if (!TextUtils.isEmpty(subtitle)) {
+ setSubtitle(title);
+ }
+ a.recycle();
+ }
+
+ /**
+ * Set a logo drawable from a resource id.
+ *
+ * <p>This drawable should generally take the place of title text. The logo cannot be
+ * clicked. Apps using a logo should also supply a description using
+ * {@link #setLogoDescription(int)}.</p>
+ *
+ * @param resId ID of a drawable resource
+ */
+ public void setLogo(int resId) {
+ setLogo(getContext().getDrawable(resId));
+ }
+
+ /**
+ * Set a logo drawable.
+ *
+ * <p>This drawable should generally take the place of title text. The logo cannot be
+ * clicked. Apps using a logo should also supply a description using
+ * {@link #setLogoDescription(int)}.</p>
+ *
+ * @param drawable Drawable to use as a logo
+ */
+ public void setLogo(Drawable drawable) {
+ if (drawable != null) {
+ if (mLogoView == null) {
+ mLogoView = new ImageView(getContext());
+ }
+ if (mLogoView.getParent() == null) {
+ addSystemView(mLogoView);
+ }
+ } else if (mLogoView != null && mLogoView.getParent() != null) {
+ removeView(mLogoView);
+ }
+ if (mLogoView != null) {
+ mLogoView.setImageDrawable(drawable);
+ }
+ }
+
+ /**
+ * Return the current logo drawable.
+ *
+ * @return The current logo drawable
+ * @see #setLogo(int)
+ * @see #setLogo(android.graphics.drawable.Drawable)
+ */
+ public Drawable getLogo() {
+ return mLogoView != null ? mLogoView.getDrawable() : null;
+ }
+
+ /**
+ * Set a description of the toolbar's logo.
+ *
+ * <p>This description will be used for accessibility or other similar descriptions
+ * of the UI.</p>
+ *
+ * @param resId String resource id
+ */
+ public void setLogoDescription(int resId) {
+ setLogoDescription(getContext().getText(resId));
+ }
+
+ /**
+ * Set a description of the toolbar's logo.
+ *
+ * <p>This description will be used for accessibility or other similar descriptions
+ * of the UI.</p>
+ *
+ * @param description Description to set
+ */
+ public void setLogoDescription(CharSequence description) {
+ if (!TextUtils.isEmpty(description) && mLogoView == null) {
+ mLogoView = new ImageView(getContext());
+ }
+ if (mLogoView != null) {
+ mLogoView.setContentDescription(description);
+ }
+ }
+
+ /**
+ * Return the description of the toolbar's logo.
+ *
+ * @return A description of the logo
+ */
+ public CharSequence getLogoDescription() {
+ return mLogoView != null ? mLogoView.getContentDescription() : null;
+ }
+
+ /**
+ * Return the current title displayed in the toolbar.
+ *
+ * @return The current title
+ */
+ public CharSequence getTitle() {
+ return mTitleText;
+ }
+
+ /**
+ * Set the title of this toolbar.
+ *
+ * <p>A title should be used as the anchor for a section of content. It should
+ * describe or name the content being viewed.</p>
+ *
+ * @param resId Resource ID of a string to set as the title
+ */
+ public void setTitle(int resId) {
+ setTitle(getContext().getText(resId));
+ }
+
+ /**
+ * Set the title of this toolbar.
+ *
+ * <p>A title should be used as the anchor for a section of content. It should
+ * describe or name the content being viewed.</p>
+ *
+ * @param title Title to set
+ */
+ public void setTitle(CharSequence title) {
+ if (!TextUtils.isEmpty(title)) {
+ if (mTitleTextView == null) {
+ final Context context = getContext();
+ mTitleTextView = new TextView(context);
+ mTitleTextView.setTextAppearance(context, mTitleTextAppearance);
+ }
+ if (mTitleTextView.getParent() == null) {
+ addSystemView(mTitleTextView);
+ }
+ } else if (mTitleTextView != null && mTitleTextView.getParent() != null) {
+ removeView(mTitleTextView);
+ }
+ if (mTitleTextView != null) {
+ mTitleTextView.setText(title);
+ }
+ mTitleText = title;
+ }
+
+ /**
+ * Return the subtitle of this toolbar.
+ *
+ * @return The current subtitle
+ */
+ public CharSequence getSubtitle() {
+ return mSubtitleText;
+ }
+
+ /**
+ * Set the subtitle of this toolbar.
+ *
+ * <p>Subtitles should express extended information about the current content.</p>
+ *
+ * @param resId String resource ID
+ */
+ public void setSubtitle(int resId) {
+ setSubtitle(getContext().getText(resId));
+ }
+
+ /**
+ * Set the subtitle of this toolbar.
+ *
+ * <p>Subtitles should express extended information about the current content.</p>
+ *
+ * @param subtitle Subtitle to set
+ */
+ public void setSubtitle(CharSequence subtitle) {
+ if (!TextUtils.isEmpty(subtitle)) {
+ if (mSubtitleTextView == null) {
+ final Context context = getContext();
+ mSubtitleTextView = new TextView(context);
+ mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance);
+ }
+ if (mSubtitleTextView.getParent() == null) {
+ addSystemView(mSubtitleTextView);
+ }
+ } else if (mSubtitleTextView != null && mSubtitleTextView.getParent() != null) {
+ removeView(mSubtitleTextView);
+ }
+ if (mSubtitleTextView != null) {
+ mSubtitleTextView.setText(subtitle);
+ }
+ mSubtitleText = subtitle;
+ }
+
+ /**
+ * Set the icon to use for the toolbar's navigation button.
+ *
+ * <p>The navigation button appears at the start of the toolbar if present. Setting an icon
+ * will make the navigation button visible.</p>
+ *
+ * <p>If you use a navigation icon you should also set a description for its action using
+ * {@link #setNavigationDescription(int)}. This is used for accessibility and tooltips.</p>
+ *
+ * @param resId Resource ID of a drawable to set
+ */
+ public void setNavigationIcon(int resId) {
+ setNavigationIcon(getContext().getDrawable(resId));
+ }
+
+ /**
+ * Set the icon to use for the toolbar's navigation button.
+ *
+ * <p>The navigation button appears at the start of the toolbar if present. Setting an icon
+ * will make the navigation button visible.</p>
+ *
+ * <p>If you use a navigation icon you should also set a description for its action using
+ * {@link #setNavigationDescription(int)}. This is used for accessibility and tooltips.</p>
+ *
+ * @param icon Drawable to set
+ */
+ public void setNavigationIcon(Drawable icon) {
+ if (icon != null) {
+ ensureNavButtonView();
+ if (mNavButtonView.getParent() == null) {
+ addSystemView(mNavButtonView);
+ }
+ } else if (mNavButtonView != null && mNavButtonView.getParent() != null) {
+ removeView(mNavButtonView);
+ }
+ if (mNavButtonView != null) {
+ mNavButtonView.setImageDrawable(icon);
+ }
+ }
+
+ /**
+ * Return the current drawable used as the navigation icon.
+ *
+ * @return The navigation icon drawable
+ */
+ public Drawable getNavigationIcon() {
+ return mNavButtonView != null ? mNavButtonView.getDrawable() : null;
+ }
+
+ /**
+ * Set a description for the navigation button.
+ *
+ * <p>This description string is used for accessibility, tooltips and other facilities
+ * to improve discoverability.</p>
+ *
+ * @param resId Resource ID of a string to set
+ */
+ public void setNavigationDescription(int resId) {
+ setNavigationDescription(getContext().getText(resId));
+ }
+
+ /**
+ * Set a description for the navigation button.
+ *
+ * <p>This description string is used for accessibility, tooltips and other facilities
+ * to improve discoverability.</p>
+ *
+ * @param description String to set as the description
+ */
+ public void setNavigationDescription(CharSequence description) {
+ if (!TextUtils.isEmpty(description)) {
+ ensureNavButtonView();
+ }
+ if (mNavButtonView != null) {
+ mNavButtonView.setContentDescription(description);
+ }
+ }
+
+ /**
+ * Set a listener to respond to navigation events.
+ *
+ * <p>This listener will be called whenever the user clicks the navigation button
+ * at the start of the toolbar. An icon must be set for the navigation button to appear.</p>
+ *
+ * @param listener Listener to set
+ * @see #setNavigationIcon(android.graphics.drawable.Drawable)
+ */
+ public void setNavigationOnClickListener(OnClickListener listener) {
+ ensureNavButtonView();
+ mNavButtonView.setOnClickListener(listener);
+ }
+
+ /**
+ * Return the Menu shown in the toolbar.
+ *
+ * <p>Applications that wish to populate the toolbar's menu can do so from here. To use
+ * an XML menu resource, use {@link #inflateMenu(int)}.</p>
+ *
+ * @return The toolbar's Menu
+ */
+ public Menu getMenu() {
+ if (mMenuView == null) {
+ mMenuView = new ActionMenuView(getContext());
+ mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener);
+ addSystemView(mMenuView);
+ }
+ return mMenuView.getMenu();
+ }
+
+ private MenuInflater getMenuInflater() {
+ return new MenuInflater(getContext());
+ }
+
+ /**
+ * Inflate a menu resource into this toolbar.
+ *
+ * <p>Inflate an XML menu resource into this toolbar. Existing items in the menu will not
+ * be modified or removed.</p>
+ *
+ * @param resId ID of a menu resource to inflate
+ */
+ public void inflateMenu(int resId) {
+ getMenuInflater().inflate(resId, getMenu());
+ }
+
+ /**
+ * Set a listener to respond to menu item click events.
+ *
+ * <p>This listener will be invoked whenever a user selects a menu item from
+ * the action buttons presented at the end of the toolbar or the associated overflow.</p>
+ *
+ * @param listener Listener to set
+ */
+ public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
+ mOnMenuItemClickListener = listener;
+ }
+
+ private void ensureNavButtonView() {
+ if (mNavButtonView == null) {
+ mNavButtonView = new ImageButton(getContext(), null, R.attr.borderlessButtonStyle);
+ }
+ }
+
+ private void addSystemView(View v) {
+ final LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ lp.mViewType = LayoutParams.SYSTEM;
+ addView(v, lp);
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ SavedState state = new SavedState(super.onSaveInstanceState());
+ return state;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ final SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = 0;
+ int height = 0;
+ int childState = 0;
+
+ // System views measure first.
+
+ if (shouldLayout(mNavButtonView)) {
+ measureChildWithMargins(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0);
+ width += mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView);
+ height = Math.max(height, mNavButtonView.getMeasuredHeight() +
+ getVerticalMargins(mNavButtonView));
+ childState = combineMeasuredStates(childState, mNavButtonView.getMeasuredState());
+ }
+
+ if (shouldLayout(mMenuView)) {
+ measureChildWithMargins(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0);
+ width += mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView);
+ height = Math.max(height, mMenuView.getMeasuredHeight() +
+ getVerticalMargins(mMenuView));
+ childState = combineMeasuredStates(childState, mMenuView.getMeasuredState());
+ }
+
+ if (shouldLayout(mLogoView)) {
+ measureChildWithMargins(mLogoView, widthMeasureSpec, width, heightMeasureSpec, 0);
+ width += mLogoView.getMeasuredWidth() + getHorizontalMargins(mLogoView);
+ height = Math.max(height, mLogoView.getMeasuredHeight() +
+ getVerticalMargins(mLogoView));
+ childState = combineMeasuredStates(childState, mLogoView.getMeasuredState());
+ }
+
+ int titleWidth = 0;
+ int titleHeight = 0;
+ final int titleVertMargins = mTitleMarginTop + mTitleMarginBottom;
+ final int titleHorizMargins = mTitleMarginStart + mTitleMarginEnd;
+ if (shouldLayout(mTitleTextView)) {
+ measureChildWithMargins(mTitleTextView, widthMeasureSpec, width + titleHorizMargins,
+ heightMeasureSpec, titleVertMargins);
+ titleWidth = mTitleTextView.getMeasuredWidth() + getHorizontalMargins(mTitleTextView);
+ titleHeight = mTitleTextView.getMeasuredHeight() + getVerticalMargins(mTitleTextView);
+ childState = combineMeasuredStates(childState, mTitleTextView.getMeasuredState());
+ }
+ if (shouldLayout(mSubtitleTextView)) {
+ measureChildWithMargins(mSubtitleTextView, widthMeasureSpec, width + titleHorizMargins,
+ heightMeasureSpec, titleHeight + titleVertMargins);
+ titleWidth = Math.max(titleWidth, mSubtitleTextView.getMeasuredWidth() +
+ getHorizontalMargins(mSubtitleTextView));
+ titleHeight += mSubtitleTextView.getMeasuredHeight() +
+ getVerticalMargins(mSubtitleTextView);
+ childState = combineMeasuredStates(childState, mSubtitleTextView.getMeasuredState());
+ }
+
+ width += titleWidth;
+ height = Math.max(height, titleHeight);
+
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.mViewType == LayoutParams.SYSTEM || !shouldLayout(child)) {
+ // We already got all system views above. Skip them and GONE views.
+ continue;
+ }
+
+ measureChildWithMargins(child, widthMeasureSpec, width, heightMeasureSpec, 0);
+ width += child.getMeasuredWidth() + getHorizontalMargins(child);
+ height = Math.max(height, child.getMeasuredHeight() + getVerticalMargins(child));
+ childState = combineMeasuredStates(childState, child.getMeasuredState());
+ }
+
+ // Measurement already took padding into account for available space for the children,
+ // add it in for the final size.
+ width += getPaddingLeft() + getPaddingRight();
+ height += getPaddingTop() + getPaddingBottom();
+
+ final int measuredWidth = resolveSizeAndState(
+ Math.max(width, getSuggestedMinimumWidth()),
+ widthMeasureSpec, childState & MEASURED_STATE_MASK);
+ final int measuredHeight = resolveSizeAndState(
+ Math.max(height, getSuggestedMinimumHeight()),
+ heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT);
+ setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ final int width = getWidth();
+ final int height = getHeight();
+ final int paddingLeft = getPaddingLeft();
+ final int paddingRight = getPaddingRight();
+ final int paddingTop = getPaddingTop();
+ final int paddingBottom = getPaddingBottom();
+ int left = paddingLeft;
+ int right = width - paddingRight;
+
+ if (shouldLayout(mNavButtonView)) {
+ if (isRtl) {
+ right = layoutChildRight(mNavButtonView, right);
+ } else {
+ left = layoutChildLeft(mNavButtonView, left);
+ }
+ }
+
+ if (shouldLayout(mMenuView)) {
+ if (isRtl) {
+ left = layoutChildLeft(mMenuView, left);
+ } else {
+ right = layoutChildRight(mMenuView, right);
+ }
+ }
+
+ if (shouldLayout(mLogoView)) {
+ if (isRtl) {
+ right = layoutChildRight(mLogoView, right);
+ } else {
+ left = layoutChildLeft(mLogoView, left);
+ }
+ }
+
+ final boolean layoutTitle = shouldLayout(mTitleTextView);
+ final boolean layoutSubtitle = shouldLayout(mSubtitleTextView);
+ int titleHeight = 0;
+ if (layoutTitle) {
+ final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
+ titleHeight += lp.topMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin;
+ }
+ if (layoutSubtitle) {
+ final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
+ titleHeight += lp.bottomMargin + mTitleTextView.getMeasuredHeight() + lp.bottomMargin;
+ }
+
+ if (layoutTitle || layoutSubtitle) {
+ int titleTop;
+ switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {
+ case Gravity.TOP:
+ titleTop = getPaddingTop();
+ break;
+ default:
+ case Gravity.CENTER_VERTICAL:
+ final View child = layoutTitle ? mTitleTextView : mSubtitleTextView;
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final int space = height - paddingTop - paddingBottom;
+ int spaceAbove = (space - titleHeight) / 2;
+ if (spaceAbove < lp.topMargin + mTitleMarginTop) {
+ spaceAbove = lp.topMargin + mTitleMarginTop;
+ } else {
+ final int spaceBelow = height - paddingBottom - titleHeight -
+ spaceAbove - paddingTop;
+ if (spaceBelow < lp.bottomMargin + mTitleMarginBottom) {
+ spaceAbove = Math.max(0, spaceAbove -
+ (lp.bottomMargin + mTitleMarginBottom - spaceBelow));
+ }
+ }
+ titleTop = paddingTop + spaceAbove;
+ break;
+ case Gravity.BOTTOM:
+ titleTop = height - paddingBottom - titleHeight;
+ break;
+ }
+ if (isRtl) {
+ int titleRight = right;
+ int subtitleRight = right;
+ titleTop += mTitleMarginTop;
+ if (layoutTitle) {
+ final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
+ titleRight -= lp.rightMargin + mTitleMarginStart;
+ titleTop += lp.topMargin;
+ final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth();
+ final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
+ mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
+ titleRight = titleLeft - lp.leftMargin - mTitleMarginEnd;
+ titleTop = titleBottom + lp.bottomMargin;
+ }
+ if (layoutSubtitle) {
+ final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
+ subtitleRight -= lp.rightMargin + mTitleMarginStart;
+ titleTop += lp.topMargin;
+ final int subtitleLeft = subtitleRight - mSubtitleTextView.getMeasuredWidth();
+ final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
+ mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
+ subtitleRight = subtitleRight - lp.leftMargin - mTitleMarginEnd;
+ titleTop = subtitleBottom + lp.bottomMargin;
+ }
+ right = Math.max(titleRight, subtitleRight);
+ } else {
+ int titleLeft = left;
+ int subtitleLeft = left;
+ titleTop += mTitleMarginTop;
+ if (layoutTitle) {
+ final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams();
+ titleLeft += lp.leftMargin + mTitleMarginStart;
+ titleTop += lp.topMargin;
+ final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth();
+ final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight();
+ mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom);
+ titleLeft = titleRight + lp.rightMargin + mTitleMarginEnd;
+ titleTop = titleBottom + lp.bottomMargin;
+ }
+ if (layoutSubtitle) {
+ final LayoutParams lp = (LayoutParams) mSubtitleTextView.getLayoutParams();
+ subtitleLeft += lp.leftMargin + mTitleMarginStart;
+ titleTop += lp.topMargin;
+ final int subtitleRight = subtitleLeft + mSubtitleTextView.getMeasuredWidth();
+ final int subtitleBottom = titleTop + mSubtitleTextView.getMeasuredHeight();
+ mSubtitleTextView.layout(subtitleLeft, titleTop, subtitleRight, subtitleBottom);
+ subtitleLeft = subtitleRight + lp.rightMargin + mTitleMarginEnd;
+ titleTop = subtitleBottom + lp.bottomMargin;
+ }
+ left = Math.max(titleLeft, subtitleLeft);
+ }
+ }
+
+ // Get all remaining children sorted for layout. This is all prepared
+ // such that absolute layout direction can be used below.
+
+ addCustomViewsWithGravity(mTempViews, Gravity.LEFT);
+ final int leftViewsCount = mTempViews.size();
+ for (int i = 0; i < leftViewsCount; i++) {
+ left = layoutChildLeft(getChildAt(i), left);
+ }
+
+ addCustomViewsWithGravity(mTempViews, Gravity.RIGHT);
+ final int rightViewsCount = mTempViews.size();
+ for (int i = 0; i < rightViewsCount; i++) {
+ right = layoutChildRight(getChildAt(i), right);
+ }
+
+ // Centered views try to center with respect to the whole bar, but views pinned
+ // to the left or right can push the mass of centered views to one side or the other.
+ addCustomViewsWithGravity(mTempViews, Gravity.CENTER);
+ final int centerViewsWidth = getViewListMeasuredWidth(mTempViews);
+ final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2;
+ final int halfCenterViewsWidth = centerViewsWidth / 2;
+ int centerLeft = parentCenter - halfCenterViewsWidth;
+ final int centerRight = centerLeft + centerViewsWidth;
+ if (centerLeft < left) {
+ centerLeft = left;
+ } else if (centerRight > right) {
+ centerLeft -= centerRight - right;
+ }
+
+ final int centerViewsCount = mTempViews.size();
+ for (int i = 0; i < centerViewsCount; i++) {
+ centerLeft = layoutChildLeft(getChildAt(i), centerLeft);
+ }
+ mTempViews.clear();
+ }
+
+ private int getViewListMeasuredWidth(List<View> views) {
+ int width = 0;
+ final int count = views.size();
+ for (int i = 0; i < count; i++) {
+ final View v = views.get(i);
+ final LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ width += lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin;
+ }
+ return width;
+ }
+
+ private int layoutChildLeft(View child, int left) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ left += lp.leftMargin;
+ int top = getChildTop(child);
+ child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
+ left += lp.rightMargin;
+ return left;
+ }
+
+ private int layoutChildRight(View child, int right) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ right -= lp.rightMargin;
+ int top = getChildTop(child);
+ child.layout(right - child.getMeasuredWidth(), top, right, top + child.getMeasuredHeight());
+ right -= lp.leftMargin;
+ return right;
+ }
+
+ private int getChildTop(View child) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ switch (getChildVerticalGravity(lp.gravity)) {
+ case Gravity.TOP:
+ return getPaddingTop();
+
+ case Gravity.BOTTOM:
+ return getPaddingBottom() - child.getMeasuredHeight() - lp.bottomMargin;
+
+ default:
+ case Gravity.CENTER_VERTICAL:
+ final int paddingTop = getPaddingTop();
+ final int paddingBottom = getPaddingBottom();
+ final int height = getHeight();
+ final int childHeight = child.getMeasuredHeight();
+ final int space = height - paddingTop - paddingBottom;
+ int spaceAbove = (space - childHeight) / 2;
+ if (spaceAbove < lp.topMargin) {
+ spaceAbove = lp.topMargin;
+ } else {
+ final int spaceBelow = height - paddingBottom - childHeight -
+ spaceAbove - paddingTop;
+ if (spaceBelow < lp.bottomMargin) {
+ spaceAbove = Math.max(0, spaceAbove - (lp.bottomMargin - spaceBelow));
+ }
+ }
+ return paddingTop + spaceAbove;
+ }
+ }
+
+ private int getChildVerticalGravity(int gravity) {
+ final int vgrav = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+ switch (vgrav) {
+ case Gravity.TOP:
+ case Gravity.BOTTOM:
+ case Gravity.CENTER_VERTICAL:
+ return vgrav;
+ default:
+ return mGravity & Gravity.VERTICAL_GRAVITY_MASK;
+ }
+ }
+
+ /**
+ * Prepare a list of non-SYSTEM child views. If the layout direction is RTL
+ * this will be in reverse child order.
+ *
+ * @param views List to populate. It will be cleared before use.
+ * @param gravity Horizontal gravity to match against
+ */
+ private void addCustomViewsWithGravity(List<View> views, int gravity) {
+ final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ final int childCount = getChildCount();
+ final int absGrav = Gravity.getAbsoluteGravity(gravity, getLayoutDirection());
+
+ views.clear();
+
+ if (isRtl) {
+ for (int i = childCount - 1; i >= 0; i--) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) &&
+ getChildHorizontalGravity(lp.gravity) == absGrav) {
+ views.add(child);
+ }
+
+ }
+ } else {
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) &&
+ getChildHorizontalGravity(lp.gravity) == absGrav) {
+ views.add(child);
+ }
+ }
+ }
+ }
+
+ private int getChildHorizontalGravity(int gravity) {
+ final int ld = getLayoutDirection();
+ final int absGrav = Gravity.getAbsoluteGravity(gravity, ld);
+ final int hGrav = absGrav & Gravity.HORIZONTAL_GRAVITY_MASK;
+ switch (hGrav) {
+ case Gravity.LEFT:
+ case Gravity.RIGHT:
+ case Gravity.CENTER_HORIZONTAL:
+ return hGrav;
+ default:
+ return ld == LAYOUT_DIRECTION_RTL ? Gravity.RIGHT : Gravity.LEFT;
+ }
+ }
+
+ private boolean shouldLayout(View view) {
+ return view != null && view.getParent() == this && view.getVisibility() != GONE;
+ }
+
+ private int getHorizontalMargins(View v) {
+ final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
+ return mlp.getMarginStart() + mlp.getMarginEnd();
+ }
+
+ private int getVerticalMargins(View v) {
+ final MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
+ return mlp.topMargin + mlp.bottomMargin;
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return super.generateLayoutParams(attrs);
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ if (p instanceof LayoutParams) {
+ return new LayoutParams((LayoutParams) p);
+ } else if (p instanceof MarginLayoutParams) {
+ return new LayoutParams((MarginLayoutParams) p);
+ } else {
+ return new LayoutParams(p);
+ }
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return super.checkLayoutParams(p) && p instanceof LayoutParams;
+ }
+
+ private static boolean isCustomView(View child) {
+ return ((LayoutParams) child.getLayoutParams()).mViewType == LayoutParams.CUSTOM;
+ }
+
+ /**
+ * Interface responsible for receiving menu item click events if the items themselves
+ * do not have individual item click listeners.
+ */
+ public interface OnMenuItemClickListener {
+ /**
+ * This method will be invoked when a menu item is clicked if the item itself did
+ * not already handle the event.
+ *
+ * @param item {@link MenuItem} that was clicked
+ * @return <code>true</code> if the event was handled, <code>false</code> otherwise.
+ */
+ public boolean onMenuItemClick(MenuItem item);
+ }
+
+ /**
+ * Layout information for child views of Toolbars.
+ *
+ * @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity
+ */
+ public static class LayoutParams extends MarginLayoutParams {
+ /**
+ * Gravity for the view associated with these LayoutParams.
+ *
+ * @see android.view.Gravity
+ */
+ @ViewDebug.ExportedProperty(category = "layout", mapping = {
+ @ViewDebug.IntToString(from = -1, to = "NONE"),
+ @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
+ @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
+ @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"),
+ @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"),
+ @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"),
+ @ViewDebug.IntToString(from = Gravity.START, to = "START"),
+ @ViewDebug.IntToString(from = Gravity.END, to = "END"),
+ @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"),
+ @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"),
+ @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
+ @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"),
+ @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"),
+ @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL")
+ })
+ public int gravity = Gravity.NO_GRAVITY;
+
+ static final int CUSTOM = 0;
+ static final int SYSTEM = 1;
+
+ int mViewType = CUSTOM;
+
+ public LayoutParams(@NonNull Context c, AttributeSet attrs) {
+ super(c, attrs);
+
+ TypedArray a = c.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.Toolbar_LayoutParams);
+ gravity = a.getInt(
+ com.android.internal.R.styleable.Toolbar_LayoutParams_layout_gravity,
+ Gravity.NO_GRAVITY);
+ a.recycle();
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ this.gravity = Gravity.CENTER_VERTICAL | Gravity.START;
+ }
+
+ public LayoutParams(int width, int height, int gravity) {
+ super(width, height);
+ this.gravity = gravity;
+ }
+
+ public LayoutParams(int gravity) {
+ this(WRAP_CONTENT, MATCH_PARENT, gravity);
+ }
+
+ public LayoutParams(LayoutParams source) {
+ super(source);
+
+ this.gravity = source.gravity;
+ }
+
+ public LayoutParams(MarginLayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+ }
+
+ static class SavedState extends BaseSavedState {
+ public SavedState(Parcel source) {
+ super(source);
+ }
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ }
+
+ public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
+
+ @Override
+ public SavedState createFromParcel(Parcel source) {
+ return new SavedState(source);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+}