From 9b4bee0f14bbd137b0797127aff2df46a6321ec5 Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Wed, 27 Apr 2011 19:24:47 -0700 Subject: [PATCH] Introducing the split action bar. Apply (or extend) the theme Theme.Holo.SplitActionBarWhenNarrow or Theme.Holo.Light.SplitActionBarWhenNarrow to enable splitting the action bar across both the top and bottom of the screen. This places the action menu along the bottom, leaving more room at the top for titles, navigation, and custom views and more room at the bottom for menu items. TODO: Refine layout of the action menu when placed at the bottom of the screen. Make action modes split as well. Change-Id: I92c91f99c533f26aecf6b828ed041386c4f16922 --- api/current.txt | 2 + core/java/android/app/Activity.java | 1 + .../android/internal/app/ActionBarImpl.java | 20 ++++---- .../view/menu/ActionMenuPresenter.java | 20 ++++++-- .../internal/widget/ActionBarView.java | 48 +++++++++++++++++-- core/res/res/layout/screen_action_bar.xml | 3 +- .../res/layout/screen_action_bar_overlay.xml | 3 +- core/res/res/values-w480dp/bools.xml | 1 + core/res/res/values/attrs.xml | 5 ++ core/res/res/values/bools.xml | 1 + core/res/res/values/public.xml | 2 + core/res/res/values/themes.xml | 13 +++++ .../internal/policy/impl/PhoneWindow.java | 15 ++++++ 13 files changed, 114 insertions(+), 20 deletions(-) diff --git a/api/current.txt b/api/current.txt index 5aa840d320587..a98ffae888995 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1490,9 +1490,11 @@ package android { field public static final int Theme_Holo_Light_Dialog_NoActionBar_MinWidth = 16973942; // 0x1030076 field public static final int Theme_Holo_Light_NoActionBar = 16974064; // 0x10300f0 field public static final int Theme_Holo_Light_Panel = 16973948; // 0x103007c + field public static final int Theme_Holo_Light_SplitActionBarWhenNarrow = 16974067; // 0x10300f3 field public static final int Theme_Holo_NoActionBar = 16973932; // 0x103006c field public static final int Theme_Holo_NoActionBar_Fullscreen = 16973933; // 0x103006d field public static final int Theme_Holo_Panel = 16973947; // 0x103007b + field public static final int Theme_Holo_SplitActionBarWhenNarrow = 16974066; // 0x10300f2 field public static final int Theme_Holo_Wallpaper = 16973949; // 0x103007d field public static final int Theme_Holo_Wallpaper_NoTitleBar = 16973950; // 0x103007e field public static final int Theme_InputMethod = 16973908; // 0x1030054 diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index f5849c24e59a3..02b6619dfaf94 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2482,6 +2482,7 @@ public class Activity extends ContextThemeWrapper break; case Window.FEATURE_ACTION_BAR: + initActionBar(); mActionBar.dispatchMenuVisibilityChanged(false); break; } diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index dccfa6c537fb3..57df259acca96 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -71,7 +71,7 @@ public class ActionBarImpl extends ActionBar { private ActionBarContainer mContainerView; private ActionBarView mActionView; private ActionBarContextView mUpperContextView; - private LinearLayout mLowerContextView; + private LinearLayout mLowerView; private View mContentView; private ViewGroup mExternalTabView; @@ -106,8 +106,8 @@ public class ActionBarImpl extends ActionBar { new AnimatorListenerAdapter() { // NORMAL_VIEW @Override public void onAnimationEnd(Animator animation) { - if (mLowerContextView != null) { - mLowerContextView.removeAllViews(); + if (mLowerView != null) { + mLowerView.removeAllViews(); } mCurrentModeAnim = null; hideAllExcept(NORMAL_VIEW); @@ -162,7 +162,7 @@ public class ActionBarImpl extends ActionBar { mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar); mUpperContextView = (ActionBarContextView) decor.findViewById( com.android.internal.R.id.action_context_bar); - mLowerContextView = (LinearLayout) decor.findViewById( + 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); @@ -173,8 +173,8 @@ public class ActionBarImpl extends ActionBar { } mActionView.setContextView(mUpperContextView); - mContextDisplayMode = mLowerContextView == null ? - CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT; + mContextDisplayMode = mActionView.isSplitActionBar() ? + CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL; if (!mActionView.hasEmbeddedTabs()) { HorizontalScrollView tabScroller = new HorizontalScrollView(mContext); @@ -348,9 +348,9 @@ public class ActionBarImpl extends ActionBar { mode.invalidate(); mUpperContextView.initForMode(mode); animateTo(CONTEXT_VIEW); - if (mLowerContextView != null) { + if (mLowerView != null) { // TODO animate this - mLowerContextView.setVisibility(View.VISIBLE); + mLowerView.setVisibility(View.VISIBLE); } mActionMode = mode; return mode; @@ -616,9 +616,9 @@ public class ActionBarImpl extends ActionBar { // Clear out the context mode views after the animation finishes mUpperContextView.closeMode(); - if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) { + if (mLowerView != null && mLowerView.getVisibility() != View.GONE) { // TODO Animate this - mLowerContextView.setVisibility(View.GONE); + mLowerView.setVisibility(View.GONE); } mActionMode = null; diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index a05fa53e425a1..e210b78c268f4 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -73,10 +73,11 @@ public class ActionMenuPresenter extends BaseMenuPresenter { int width = mWidthLimit; if (mReserveOverflow) { - OverflowMenuButton button = new OverflowMenuButton(mContext); - mOverflowButton = button; - final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - mOverflowButton.measure(spec, spec); + if (mOverflowButton == null) { + mOverflowButton = new OverflowMenuButton(mContext); + final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + mOverflowButton.measure(spec, spec); + } width -= mOverflowButton.getMeasuredWidth(); } else { mOverflowButton = null; @@ -88,6 +89,17 @@ public class ActionMenuPresenter extends BaseMenuPresenter { mScrapActionButtonView = null; } + public void setWidthLimit(int width) { + if (mReserveOverflow) { + width -= mOverflowButton.getMeasuredWidth(); + } + mActionItemWidthLimit = width; + } + + public void setItemLimit(int itemCount) { + mMaxItems = itemCount; + } + @Override public MenuView getMenuView(ViewGroup root) { MenuView result = super.getMenuView(root); diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index a572e11683720..0c13f7bfbe60e 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -110,7 +110,7 @@ public class ActionBarView extends ViewGroup { private int mProgressStyle; private int mIndeterminateProgressStyle; - private boolean mShowMenu; + private boolean mSplitActionBar; private boolean mUserTitle; private boolean mIncludeTabs; @@ -119,6 +119,7 @@ public class ActionBarView extends ViewGroup { private ActionMenuPresenter mActionMenuPresenter; private ActionBarContextView mContextView; + private ViewGroup mSplitView; private ActionMenuItem mLogoNavItem; @@ -245,6 +246,26 @@ public class ActionBarView extends ViewGroup { addView(mIndeterminateProgressView); } + public void setSplitActionBar(boolean splitActionBar) { + if (mSplitActionBar != splitActionBar) { + if (mMenuView != null) { + if (splitActionBar) { + removeView(mMenuView); + if (mSplitView != null) { + mSplitView.addView(mMenuView); + } + } else { + addView(mMenuView); + } + } + mSplitActionBar = splitActionBar; + } + } + + public boolean isSplitActionBar() { + return mSplitActionBar; + } + public boolean hasEmbeddedTabs() { return mIncludeTabs; } @@ -284,10 +305,29 @@ public class ActionBarView extends ViewGroup { final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); menuView.setLayoutParams(layoutParams); - addView(menuView); + if (!mSplitActionBar) { + addView(menuView); + } else { + // Allow full screen width in split mode. + mActionMenuPresenter.setWidthLimit( + getContext().getResources().getDisplayMetrics().widthPixels); + // No limit to the item count; use whatever will fit. + mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); + if (mSplitView != null) { + mSplitView.addView(menuView); + } // We'll add this later if we missed it this time. + } mMenuView = menuView; } + public void setSplitView(ViewGroup splitView) { + mSplitView = splitView; + splitView.setVisibility(VISIBLE); + if (mMenuView != null) { + splitView.addView(mMenuView); + } + } + public boolean showOverflowMenu() { if (mActionMenuPresenter != null) { return mActionMenuPresenter.showOverflowMenu(); @@ -718,7 +758,7 @@ public class ActionBarView extends ViewGroup { leftOfCenter -= homeWidth; } - if (mMenuView != null) { + if (mMenuView != null && mMenuView.getParent() == this) { availableWidth = measureChildView(mMenuView, availableWidth, childSpecHeight, 0); rightOfCenter -= mMenuView.getMeasuredWidth(); @@ -880,7 +920,7 @@ public class ActionBarView extends ViewGroup { } int menuLeft = r - l - getPaddingRight(); - if (mMenuView != null) { + if (mMenuView != null && mMenuView.getParent() == this) { positionChildInverse(mMenuView, menuLeft, y, contentHeight); menuLeft -= mMenuView.getMeasuredWidth(); } diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml index 70af26519ea63..14af446abf205 100644 --- a/core/res/res/layout/screen_action_bar.xml +++ b/core/res/res/layout/screen_action_bar.xml @@ -47,5 +47,6 @@ This is an optimized layout for a screen with the Action Bar enabled. android:layout_width="match_parent" android:layout_height="wrap_content" style="?android:attr/actionBarStyle" - android:visibility="gone" /> + android:visibility="gone" + android:gravity="center"/> diff --git a/core/res/res/layout/screen_action_bar_overlay.xml b/core/res/res/layout/screen_action_bar_overlay.xml index cfa335e51d473..aebbe411566f6 100644 --- a/core/res/res/layout/screen_action_bar_overlay.xml +++ b/core/res/res/layout/screen_action_bar_overlay.xml @@ -51,5 +51,6 @@ the Action Bar enabled overlaying application content. android:layout_height="wrap_content" android:layout_alignParentBottom="true" style="?android:attr/actionBarStyle" - android:visibility="gone" /> + android:visibility="gone" + android:gravity="center"/> diff --git a/core/res/res/values-w480dp/bools.xml b/core/res/res/values-w480dp/bools.xml index c202d90d403bc..8206e792dbe59 100644 --- a/core/res/res/values-w480dp/bools.xml +++ b/core/res/res/values-w480dp/bools.xml @@ -19,4 +19,5 @@ true true + false diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 3a44e4f73a82f..dac3bd24aa220 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -299,6 +299,10 @@ when there is not reserved space for their UI (such as an Action Bar). --> + + + @@ -1429,6 +1433,7 @@ + + + + + diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 73003c855f847..27b7e1c874196 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -2587,6 +2587,21 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if ((localFeatures & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { mActionBar.initIndeterminateProgress(); } + + final boolean splitActionBar = getWindowStyle().getBoolean( + com.android.internal.R.styleable.Window_windowSplitActionBar, false); + if (splitActionBar) { + final ViewGroup splitView = (ViewGroup) findViewById( + com.android.internal.R.id.lower_action_context_bar); + if (splitView != null) { + mActionBar.setSplitActionBar(splitActionBar); + mActionBar.setSplitView(splitView); + } else { + Log.e(TAG, "Window style requested split action bar with " + + "incompatible window decor! Ignoring request."); + } + } + // Post the panel invalidate for later; avoid application onCreateOptionsMenu // being called in the middle of onCreate or similar. mDecor.post(new Runnable() {