Loading core/java/android/widget/LinearLayout.java +9 −0 Original line number Diff line number Diff line Loading @@ -254,6 +254,15 @@ public class LinearLayout extends ViewGroup { return mDividerPadding; } /** * Get the width of the current divider drawable. * * @hide Used internally by framework. */ public int getDividerWidth() { return mDividerWidth; } @Override protected void onDraw(Canvas canvas) { if (mDivider == null) { Loading core/java/com/android/internal/app/ActionBarImpl.java +32 −81 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.internal.app; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuPopupHelper; import com.android.internal.view.menu.SubMenuBuilder; import com.android.internal.widget.AbsActionBarView; import com.android.internal.widget.ActionBarContainer; import com.android.internal.widget.ActionBarContextView; import com.android.internal.widget.ActionBarView; Loading Loading @@ -46,7 +47,6 @@ import android.view.ViewGroup; import android.view.Window; import android.view.animation.DecelerateInterpolator; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import android.widget.SpinnerAdapter; import java.lang.ref.WeakReference; Loading @@ -61,8 +61,6 @@ import java.util.ArrayList; */ public class ActionBarImpl extends ActionBar { private static final String TAG = "ActionBarImpl"; private static final int NORMAL_VIEW = 0; private static final int CONTEXT_VIEW = 1; private Context mContext; private Activity mActivity; Loading @@ -70,8 +68,8 @@ public class ActionBarImpl extends ActionBar { private ActionBarContainer mContainerView; private ActionBarView mActionView; private ActionBarContextView mUpperContextView; private LinearLayout mLowerView; private ActionBarContextView mContextView; private ActionBarContainer mSplitView; private View mContentView; private ViewGroup mExternalTabView; Loading Loading @@ -102,26 +100,6 @@ public class ActionBarImpl extends ActionBar { private static final TimeInterpolator sFadeOutInterpolator = new DecelerateInterpolator(); final AnimatorListener[] mAfterAnimation = new AnimatorListener[] { new AnimatorListenerAdapter() { // NORMAL_VIEW @Override public void onAnimationEnd(Animator animation) { if (mLowerView != null) { mLowerView.removeAllViews(); } mCurrentModeAnim = null; hideAllExcept(NORMAL_VIEW); } }, new AnimatorListenerAdapter() { // CONTEXT_VIEW @Override public void onAnimationEnd(Animator animation) { mCurrentModeAnim = null; hideAllExcept(CONTEXT_VIEW); } } }; final AnimatorListener mHideListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { Loading Loading @@ -160,19 +138,19 @@ public class ActionBarImpl extends ActionBar { private void init(View decor) { mContext = decor.getContext(); mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar); mUpperContextView = (ActionBarContextView) decor.findViewById( mContextView = (ActionBarContextView) decor.findViewById( com.android.internal.R.id.action_context_bar); mLowerView = (LinearLayout) decor.findViewById( com.android.internal.R.id.lower_action_context_bar); mContainerView = (ActionBarContainer) decor.findViewById( com.android.internal.R.id.action_bar_container); mSplitView = (ActionBarContainer) decor.findViewById( com.android.internal.R.id.split_action_bar); if (mActionView == null || mUpperContextView == null || mContainerView == null) { if (mActionView == null || mContextView == null || mContainerView == null) { throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + "with a compatible window decor layout"); } mActionView.setContextView(mUpperContextView); mActionView.setContextView(mContextView); mContextDisplayMode = mActionView.isSplitActionBar() ? CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL; Loading Loading @@ -341,16 +319,16 @@ public class ActionBarImpl extends ActionBar { mActionMode.finish(); } mUpperContextView.killMode(); mContextView.killMode(); ActionMode mode = new ActionModeImpl(callback); if (callback.onCreateActionMode(mode, mode.getMenu())) { mWasHiddenBeforeMode = !isShowing(); mode.invalidate(); mUpperContextView.initForMode(mode); animateTo(CONTEXT_VIEW); if (mLowerView != null) { mContextView.initForMode(mode); animateToMode(true); if (mSplitView != null) { // TODO animate this mLowerView.setVisibility(View.VISIBLE); mSplitView.setVisibility(View.VISIBLE); } mActionMode = mode; return mode; Loading Loading @@ -495,6 +473,10 @@ public class ActionBarImpl extends ActionBar { mContainerView.setTranslationY(-mContainerView.getHeight()); b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0)); } if (mSplitView != null) { mSplitView.setAlpha(0); b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 1)); } anim.addListener(mShowListener); mCurrentShowAnim = anim; anim.start(); Loading Loading @@ -525,6 +507,10 @@ public class ActionBarImpl extends ActionBar { b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", -mContainerView.getHeight())); } if (mSplitView != null) { mSplitView.setAlpha(1); b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 0)); } anim.addListener(mHideListener); mCurrentShowAnim = anim; anim.start(); Loading @@ -537,45 +523,14 @@ public class ActionBarImpl extends ActionBar { return mContainerView.getVisibility() == View.VISIBLE; } long animateTo(int viewIndex) { void animateToMode(boolean toActionMode) { show(false); if (mCurrentModeAnim != null) { mCurrentModeAnim.end(); } AnimatorSet set = new AnimatorSet(); final View targetChild = mContainerView.getChildAt(viewIndex); targetChild.setVisibility(View.VISIBLE); targetChild.setAlpha(0); AnimatorSet.Builder b = set.play(ObjectAnimator.ofFloat(targetChild, "alpha", 1)); final int count = mContainerView.getChildCount(); for (int i = 0; i < count; i++) { final View child = mContainerView.getChildAt(i); if (i == viewIndex || child == mContainerView.getTabContainer()) { continue; } if (child.getVisibility() != View.GONE) { Animator a = ObjectAnimator.ofFloat(child, "alpha", 0); a.setInterpolator(sFadeOutInterpolator); b.with(a); } } set.addListener(mAfterAnimation[viewIndex]); mCurrentModeAnim = set; set.start(); return set.getDuration(); } private void hideAllExcept(int viewIndex) { final int count = mContainerView.getChildCount(); for (int i = 0; i < count; i++) { mContainerView.getChildAt(i).setVisibility(i == viewIndex ? View.VISIBLE : View.GONE); } mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE); } /** Loading Loading @@ -612,14 +567,10 @@ public class ActionBarImpl extends ActionBar { mCallback.onDestroyActionMode(this); mCallback = null; animateTo(NORMAL_VIEW); animateToMode(false); // Clear out the context mode views after the animation finishes mUpperContextView.closeMode(); if (mLowerView != null && mLowerView.getVisibility() != View.GONE) { // TODO Animate this mLowerView.setVisibility(View.GONE); } mContextView.closeMode(); mActionMode = null; if (mWasHiddenBeforeMode) { Loading @@ -636,18 +587,18 @@ public class ActionBarImpl extends ActionBar { @Override public void setCustomView(View view) { mUpperContextView.setCustomView(view); mContextView.setCustomView(view); mCustomView = new WeakReference<View>(view); } @Override public void setSubtitle(CharSequence subtitle) { mUpperContextView.setSubtitle(subtitle); mContextView.setSubtitle(subtitle); } @Override public void setTitle(CharSequence title) { mUpperContextView.setTitle(title); mContextView.setTitle(title); } @Override Loading @@ -662,12 +613,12 @@ public class ActionBarImpl extends ActionBar { @Override public CharSequence getTitle() { return mUpperContextView.getTitle(); return mContextView.getTitle(); } @Override public CharSequence getSubtitle() { return mUpperContextView.getSubtitle(); return mContextView.getSubtitle(); } @Override Loading Loading @@ -707,7 +658,7 @@ public class ActionBarImpl extends ActionBar { return; } invalidate(); mUpperContextView.showOverflowMenu(); mContextView.showOverflowMenu(); } } Loading core/java/com/android/internal/view/menu/ActionMenuPresenter.java +18 −4 Original line number Diff line number Diff line Loading @@ -36,11 +36,14 @@ import java.util.ArrayList; * MenuPresenter for building action menus as seen in the action bar and action modes. */ public class ActionMenuPresenter extends BaseMenuPresenter { private static final String TAG = "ActionMenuPresenter"; private View mOverflowButton; private boolean mReserveOverflow; private int mWidthLimit; private int mActionItemWidthLimit; private int mMaxItems; private boolean mStrictWidthLimit; // Group IDs that have been added as actions - used temporarily, allocated here for reuse. private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray(); Loading Loading @@ -89,11 +92,12 @@ public class ActionMenuPresenter extends BaseMenuPresenter { mScrapActionButtonView = null; } public void setWidthLimit(int width) { public void setWidthLimit(int width, boolean strict) { if (mReserveOverflow) { width -= mOverflowButton.getMeasuredWidth(); } mActionItemWidthLimit = width; mStrictWidthLimit = strict; } public void setItemLimit(int itemCount) { Loading Loading @@ -131,6 +135,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter { if (mReserveOverflow && mMenu.getNonActionItems().size() > 0) { if (mOverflowButton == null) { mOverflowButton = new OverflowMenuButton(mContext); mOverflowButton.setLayoutParams( ((ActionMenuView) mMenuView).generateOverflowButtonLayoutParams()); } ViewGroup parent = (ViewGroup) mOverflowButton.getParent(); if (parent != mMenuView) { Loading Loading @@ -308,6 +314,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter { v.measure(querySpec, querySpec); final int measuredWidth = v.getMeasuredWidth(); widthLimit -= measuredWidth; Log.d(TAG, "flagActionItems required item " + i + " measured width " + measuredWidth + " - " + widthLimit + " left"); if (firstActionWidth == 0) { firstActionWidth = measuredWidth; } Loading @@ -334,13 +342,19 @@ public class ActionMenuPresenter extends BaseMenuPresenter { v.measure(querySpec, querySpec); final int measuredWidth = v.getMeasuredWidth(); widthLimit -= measuredWidth; Log.d(TAG, "flagActionItems requested item " + i + " measured width " + measuredWidth + " - " + widthLimit + " left"); if (firstActionWidth == 0) { firstActionWidth = measuredWidth; } // Did this push the entire first item past halfway? if (widthLimit + firstActionWidth <= 0) { isAction = false; if (mStrictWidthLimit) { isAction = widthLimit >= 0; Log.d(TAG, " --> strict width limit: isAction? " + isAction); } else { // Did this push the entire first item past the limit? isAction = widthLimit + firstActionWidth > 0; Log.d(TAG, " --> normal width limit: isAction? " + isAction); } } Loading core/java/com/android/internal/view/menu/ActionMenuView.java +122 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.content.res.Configuration; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.widget.LinearLayout; Loading @@ -33,6 +34,8 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo private boolean mReserveOverflow; private ActionMenuPresenter mPresenter; private boolean mUpdateContentsBeforeMeasure; private boolean mFormatItems; public ActionMenuView(Context context) { this(context, null); Loading @@ -58,6 +61,95 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } } @Override public void requestLayout() { // Layout can influence how many action items fit. mUpdateContentsBeforeMeasure = true; super.requestLayout(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mUpdateContentsBeforeMeasure && mMenu != null) { mMenu.onItemsChanged(true); mUpdateContentsBeforeMeasure = false; } // If we've been given an exact size to match, apply special formatting during layout. mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY; super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (!mFormatItems) { super.onLayout(changed, left, top, right, bottom); return; } final int childCount = getChildCount(); final int midVertical = (top + bottom) / 2; final int dividerWidth = getDividerWidth(); boolean hasOverflow = false; int overflowWidth = 0; int nonOverflowWidth = 0; int nonOverflowCount = 0; int widthRemaining = right - left - getPaddingRight() - getPaddingLeft(); for (int i = 0; i < childCount; i++) { final View v = getChildAt(i); if (v.getVisibility() == GONE) { continue; } LayoutParams p = (LayoutParams) v.getLayoutParams(); if (p.isOverflowButton) { hasOverflow = true; overflowWidth = v.getMeasuredWidth(); if (hasDividerBeforeChildAt(i)) { overflowWidth += dividerWidth; } int height = v.getMeasuredHeight(); int r = getPaddingRight(); int l = r - overflowWidth; int t = midVertical - (height / 2); int b = t + height; v.layout(l, t, r, b); widthRemaining -= overflowWidth; } else { nonOverflowWidth += v.getMeasuredWidth() + p.leftMargin + p.rightMargin; if (hasDividerBeforeChildAt(i)) { nonOverflowWidth += dividerWidth; } nonOverflowCount++; } } // Try to center non-overflow items with uniformly spaced padding, including on the edges. // Overflow will always pin to the right edge. If there isn't enough room for that, // center in the remaining space. if (nonOverflowWidth <= widthRemaining - overflowWidth) { widthRemaining -= overflowWidth; } final int spacing = (widthRemaining - nonOverflowWidth) / (nonOverflowCount + 1); int startLeft = getPaddingLeft() + overflowWidth + spacing; for (int i = 0; i < childCount; i++) { final View v = getChildAt(i); final LayoutParams lp = (LayoutParams) v.getLayoutParams(); if (v.getVisibility() == GONE || lp.isOverflowButton) { continue; } startLeft += lp.leftMargin; int width = v.getMeasuredWidth(); int height = v.getMeasuredHeight(); int t = midVertical - (height / 2); v.layout(startLeft, t, startLeft + width, t + height); startLeft += width + lp.rightMargin + spacing; } } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); Loading Loading @@ -97,6 +189,12 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo return p instanceof LayoutParams; } public LayoutParams generateOverflowButtonLayoutParams() { LayoutParams result = generateDefaultLayoutParams(); result.isOverflowButton = true; return result; } public boolean invokeItem(MenuItemImpl item) { return mMenu.performItemAction(item, 0); } Loading Loading @@ -127,4 +225,28 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo public boolean needsDividerBefore(); public boolean needsDividerAfter(); } public static class LayoutParams extends LinearLayout.LayoutParams { @ViewDebug.ExportedProperty(category = "layout") public boolean isOverflowButton; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); } public LayoutParams(LayoutParams other) { super((LinearLayout.LayoutParams) other); isOverflowButton = other.isOverflowButton; } public LayoutParams(int width, int height) { super(width, height); isOverflowButton = false; } public LayoutParams(int width, int height, boolean isOverflowButton) { super(width, height); this.isOverflowButton = isOverflowButton; } } } core/java/com/android/internal/view/menu/MenuBuilder.java +2 −0 Original line number Diff line number Diff line Loading @@ -226,6 +226,7 @@ public class MenuBuilder implements Menu { private void dispatchPresenterUpdate(boolean cleared) { if (mPresenters.isEmpty()) return; stopDispatchingItemsChanged(); for (WeakReference<MenuPresenter> ref : mPresenters) { final MenuPresenter presenter = ref.get(); if (presenter == null) { Loading @@ -234,6 +235,7 @@ public class MenuBuilder implements Menu { presenter.updateMenuView(cleared); } } startDispatchingItemsChanged(); } private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu) { Loading Loading
core/java/android/widget/LinearLayout.java +9 −0 Original line number Diff line number Diff line Loading @@ -254,6 +254,15 @@ public class LinearLayout extends ViewGroup { return mDividerPadding; } /** * Get the width of the current divider drawable. * * @hide Used internally by framework. */ public int getDividerWidth() { return mDividerWidth; } @Override protected void onDraw(Canvas canvas) { if (mDivider == null) { Loading
core/java/com/android/internal/app/ActionBarImpl.java +32 −81 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.internal.app; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuPopupHelper; import com.android.internal.view.menu.SubMenuBuilder; import com.android.internal.widget.AbsActionBarView; import com.android.internal.widget.ActionBarContainer; import com.android.internal.widget.ActionBarContextView; import com.android.internal.widget.ActionBarView; Loading Loading @@ -46,7 +47,6 @@ import android.view.ViewGroup; import android.view.Window; import android.view.animation.DecelerateInterpolator; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import android.widget.SpinnerAdapter; import java.lang.ref.WeakReference; Loading @@ -61,8 +61,6 @@ import java.util.ArrayList; */ public class ActionBarImpl extends ActionBar { private static final String TAG = "ActionBarImpl"; private static final int NORMAL_VIEW = 0; private static final int CONTEXT_VIEW = 1; private Context mContext; private Activity mActivity; Loading @@ -70,8 +68,8 @@ public class ActionBarImpl extends ActionBar { private ActionBarContainer mContainerView; private ActionBarView mActionView; private ActionBarContextView mUpperContextView; private LinearLayout mLowerView; private ActionBarContextView mContextView; private ActionBarContainer mSplitView; private View mContentView; private ViewGroup mExternalTabView; Loading Loading @@ -102,26 +100,6 @@ public class ActionBarImpl extends ActionBar { private static final TimeInterpolator sFadeOutInterpolator = new DecelerateInterpolator(); final AnimatorListener[] mAfterAnimation = new AnimatorListener[] { new AnimatorListenerAdapter() { // NORMAL_VIEW @Override public void onAnimationEnd(Animator animation) { if (mLowerView != null) { mLowerView.removeAllViews(); } mCurrentModeAnim = null; hideAllExcept(NORMAL_VIEW); } }, new AnimatorListenerAdapter() { // CONTEXT_VIEW @Override public void onAnimationEnd(Animator animation) { mCurrentModeAnim = null; hideAllExcept(CONTEXT_VIEW); } } }; final AnimatorListener mHideListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { Loading Loading @@ -160,19 +138,19 @@ public class ActionBarImpl extends ActionBar { private void init(View decor) { mContext = decor.getContext(); mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar); mUpperContextView = (ActionBarContextView) decor.findViewById( mContextView = (ActionBarContextView) decor.findViewById( com.android.internal.R.id.action_context_bar); mLowerView = (LinearLayout) decor.findViewById( com.android.internal.R.id.lower_action_context_bar); mContainerView = (ActionBarContainer) decor.findViewById( com.android.internal.R.id.action_bar_container); mSplitView = (ActionBarContainer) decor.findViewById( com.android.internal.R.id.split_action_bar); if (mActionView == null || mUpperContextView == null || mContainerView == null) { if (mActionView == null || mContextView == null || mContainerView == null) { throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + "with a compatible window decor layout"); } mActionView.setContextView(mUpperContextView); mActionView.setContextView(mContextView); mContextDisplayMode = mActionView.isSplitActionBar() ? CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL; Loading Loading @@ -341,16 +319,16 @@ public class ActionBarImpl extends ActionBar { mActionMode.finish(); } mUpperContextView.killMode(); mContextView.killMode(); ActionMode mode = new ActionModeImpl(callback); if (callback.onCreateActionMode(mode, mode.getMenu())) { mWasHiddenBeforeMode = !isShowing(); mode.invalidate(); mUpperContextView.initForMode(mode); animateTo(CONTEXT_VIEW); if (mLowerView != null) { mContextView.initForMode(mode); animateToMode(true); if (mSplitView != null) { // TODO animate this mLowerView.setVisibility(View.VISIBLE); mSplitView.setVisibility(View.VISIBLE); } mActionMode = mode; return mode; Loading Loading @@ -495,6 +473,10 @@ public class ActionBarImpl extends ActionBar { mContainerView.setTranslationY(-mContainerView.getHeight()); b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0)); } if (mSplitView != null) { mSplitView.setAlpha(0); b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 1)); } anim.addListener(mShowListener); mCurrentShowAnim = anim; anim.start(); Loading Loading @@ -525,6 +507,10 @@ public class ActionBarImpl extends ActionBar { b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", -mContainerView.getHeight())); } if (mSplitView != null) { mSplitView.setAlpha(1); b.with(ObjectAnimator.ofFloat(mSplitView, "alpha", 0)); } anim.addListener(mHideListener); mCurrentShowAnim = anim; anim.start(); Loading @@ -537,45 +523,14 @@ public class ActionBarImpl extends ActionBar { return mContainerView.getVisibility() == View.VISIBLE; } long animateTo(int viewIndex) { void animateToMode(boolean toActionMode) { show(false); if (mCurrentModeAnim != null) { mCurrentModeAnim.end(); } AnimatorSet set = new AnimatorSet(); final View targetChild = mContainerView.getChildAt(viewIndex); targetChild.setVisibility(View.VISIBLE); targetChild.setAlpha(0); AnimatorSet.Builder b = set.play(ObjectAnimator.ofFloat(targetChild, "alpha", 1)); final int count = mContainerView.getChildCount(); for (int i = 0; i < count; i++) { final View child = mContainerView.getChildAt(i); if (i == viewIndex || child == mContainerView.getTabContainer()) { continue; } if (child.getVisibility() != View.GONE) { Animator a = ObjectAnimator.ofFloat(child, "alpha", 0); a.setInterpolator(sFadeOutInterpolator); b.with(a); } } set.addListener(mAfterAnimation[viewIndex]); mCurrentModeAnim = set; set.start(); return set.getDuration(); } private void hideAllExcept(int viewIndex) { final int count = mContainerView.getChildCount(); for (int i = 0; i < count; i++) { mContainerView.getChildAt(i).setVisibility(i == viewIndex ? View.VISIBLE : View.GONE); } mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE); } /** Loading Loading @@ -612,14 +567,10 @@ public class ActionBarImpl extends ActionBar { mCallback.onDestroyActionMode(this); mCallback = null; animateTo(NORMAL_VIEW); animateToMode(false); // Clear out the context mode views after the animation finishes mUpperContextView.closeMode(); if (mLowerView != null && mLowerView.getVisibility() != View.GONE) { // TODO Animate this mLowerView.setVisibility(View.GONE); } mContextView.closeMode(); mActionMode = null; if (mWasHiddenBeforeMode) { Loading @@ -636,18 +587,18 @@ public class ActionBarImpl extends ActionBar { @Override public void setCustomView(View view) { mUpperContextView.setCustomView(view); mContextView.setCustomView(view); mCustomView = new WeakReference<View>(view); } @Override public void setSubtitle(CharSequence subtitle) { mUpperContextView.setSubtitle(subtitle); mContextView.setSubtitle(subtitle); } @Override public void setTitle(CharSequence title) { mUpperContextView.setTitle(title); mContextView.setTitle(title); } @Override Loading @@ -662,12 +613,12 @@ public class ActionBarImpl extends ActionBar { @Override public CharSequence getTitle() { return mUpperContextView.getTitle(); return mContextView.getTitle(); } @Override public CharSequence getSubtitle() { return mUpperContextView.getSubtitle(); return mContextView.getSubtitle(); } @Override Loading Loading @@ -707,7 +658,7 @@ public class ActionBarImpl extends ActionBar { return; } invalidate(); mUpperContextView.showOverflowMenu(); mContextView.showOverflowMenu(); } } Loading
core/java/com/android/internal/view/menu/ActionMenuPresenter.java +18 −4 Original line number Diff line number Diff line Loading @@ -36,11 +36,14 @@ import java.util.ArrayList; * MenuPresenter for building action menus as seen in the action bar and action modes. */ public class ActionMenuPresenter extends BaseMenuPresenter { private static final String TAG = "ActionMenuPresenter"; private View mOverflowButton; private boolean mReserveOverflow; private int mWidthLimit; private int mActionItemWidthLimit; private int mMaxItems; private boolean mStrictWidthLimit; // Group IDs that have been added as actions - used temporarily, allocated here for reuse. private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray(); Loading Loading @@ -89,11 +92,12 @@ public class ActionMenuPresenter extends BaseMenuPresenter { mScrapActionButtonView = null; } public void setWidthLimit(int width) { public void setWidthLimit(int width, boolean strict) { if (mReserveOverflow) { width -= mOverflowButton.getMeasuredWidth(); } mActionItemWidthLimit = width; mStrictWidthLimit = strict; } public void setItemLimit(int itemCount) { Loading Loading @@ -131,6 +135,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter { if (mReserveOverflow && mMenu.getNonActionItems().size() > 0) { if (mOverflowButton == null) { mOverflowButton = new OverflowMenuButton(mContext); mOverflowButton.setLayoutParams( ((ActionMenuView) mMenuView).generateOverflowButtonLayoutParams()); } ViewGroup parent = (ViewGroup) mOverflowButton.getParent(); if (parent != mMenuView) { Loading Loading @@ -308,6 +314,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter { v.measure(querySpec, querySpec); final int measuredWidth = v.getMeasuredWidth(); widthLimit -= measuredWidth; Log.d(TAG, "flagActionItems required item " + i + " measured width " + measuredWidth + " - " + widthLimit + " left"); if (firstActionWidth == 0) { firstActionWidth = measuredWidth; } Loading @@ -334,13 +342,19 @@ public class ActionMenuPresenter extends BaseMenuPresenter { v.measure(querySpec, querySpec); final int measuredWidth = v.getMeasuredWidth(); widthLimit -= measuredWidth; Log.d(TAG, "flagActionItems requested item " + i + " measured width " + measuredWidth + " - " + widthLimit + " left"); if (firstActionWidth == 0) { firstActionWidth = measuredWidth; } // Did this push the entire first item past halfway? if (widthLimit + firstActionWidth <= 0) { isAction = false; if (mStrictWidthLimit) { isAction = widthLimit >= 0; Log.d(TAG, " --> strict width limit: isAction? " + isAction); } else { // Did this push the entire first item past the limit? isAction = widthLimit + firstActionWidth > 0; Log.d(TAG, " --> normal width limit: isAction? " + isAction); } } Loading
core/java/com/android/internal/view/menu/ActionMenuView.java +122 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.content.res.Configuration; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.widget.LinearLayout; Loading @@ -33,6 +34,8 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo private boolean mReserveOverflow; private ActionMenuPresenter mPresenter; private boolean mUpdateContentsBeforeMeasure; private boolean mFormatItems; public ActionMenuView(Context context) { this(context, null); Loading @@ -58,6 +61,95 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } } @Override public void requestLayout() { // Layout can influence how many action items fit. mUpdateContentsBeforeMeasure = true; super.requestLayout(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mUpdateContentsBeforeMeasure && mMenu != null) { mMenu.onItemsChanged(true); mUpdateContentsBeforeMeasure = false; } // If we've been given an exact size to match, apply special formatting during layout. mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY; super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (!mFormatItems) { super.onLayout(changed, left, top, right, bottom); return; } final int childCount = getChildCount(); final int midVertical = (top + bottom) / 2; final int dividerWidth = getDividerWidth(); boolean hasOverflow = false; int overflowWidth = 0; int nonOverflowWidth = 0; int nonOverflowCount = 0; int widthRemaining = right - left - getPaddingRight() - getPaddingLeft(); for (int i = 0; i < childCount; i++) { final View v = getChildAt(i); if (v.getVisibility() == GONE) { continue; } LayoutParams p = (LayoutParams) v.getLayoutParams(); if (p.isOverflowButton) { hasOverflow = true; overflowWidth = v.getMeasuredWidth(); if (hasDividerBeforeChildAt(i)) { overflowWidth += dividerWidth; } int height = v.getMeasuredHeight(); int r = getPaddingRight(); int l = r - overflowWidth; int t = midVertical - (height / 2); int b = t + height; v.layout(l, t, r, b); widthRemaining -= overflowWidth; } else { nonOverflowWidth += v.getMeasuredWidth() + p.leftMargin + p.rightMargin; if (hasDividerBeforeChildAt(i)) { nonOverflowWidth += dividerWidth; } nonOverflowCount++; } } // Try to center non-overflow items with uniformly spaced padding, including on the edges. // Overflow will always pin to the right edge. If there isn't enough room for that, // center in the remaining space. if (nonOverflowWidth <= widthRemaining - overflowWidth) { widthRemaining -= overflowWidth; } final int spacing = (widthRemaining - nonOverflowWidth) / (nonOverflowCount + 1); int startLeft = getPaddingLeft() + overflowWidth + spacing; for (int i = 0; i < childCount; i++) { final View v = getChildAt(i); final LayoutParams lp = (LayoutParams) v.getLayoutParams(); if (v.getVisibility() == GONE || lp.isOverflowButton) { continue; } startLeft += lp.leftMargin; int width = v.getMeasuredWidth(); int height = v.getMeasuredHeight(); int t = midVertical - (height / 2); v.layout(startLeft, t, startLeft + width, t + height); startLeft += width + lp.rightMargin + spacing; } } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); Loading Loading @@ -97,6 +189,12 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo return p instanceof LayoutParams; } public LayoutParams generateOverflowButtonLayoutParams() { LayoutParams result = generateDefaultLayoutParams(); result.isOverflowButton = true; return result; } public boolean invokeItem(MenuItemImpl item) { return mMenu.performItemAction(item, 0); } Loading Loading @@ -127,4 +225,28 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo public boolean needsDividerBefore(); public boolean needsDividerAfter(); } public static class LayoutParams extends LinearLayout.LayoutParams { @ViewDebug.ExportedProperty(category = "layout") public boolean isOverflowButton; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); } public LayoutParams(LayoutParams other) { super((LinearLayout.LayoutParams) other); isOverflowButton = other.isOverflowButton; } public LayoutParams(int width, int height) { super(width, height); isOverflowButton = false; } public LayoutParams(int width, int height, boolean isOverflowButton) { super(width, height); this.isOverflowButton = isOverflowButton; } } }
core/java/com/android/internal/view/menu/MenuBuilder.java +2 −0 Original line number Diff line number Diff line Loading @@ -226,6 +226,7 @@ public class MenuBuilder implements Menu { private void dispatchPresenterUpdate(boolean cleared) { if (mPresenters.isEmpty()) return; stopDispatchingItemsChanged(); for (WeakReference<MenuPresenter> ref : mPresenters) { final MenuPresenter presenter = ref.get(); if (presenter == null) { Loading @@ -234,6 +235,7 @@ public class MenuBuilder implements Menu { presenter.updateMenuView(cleared); } } startDispatchingItemsChanged(); } private boolean dispatchSubMenuSelected(SubMenuBuilder subMenu) { Loading