diff options
| author | Adam Powell <adamp@google.com> | 2011-06-01 17:50:13 -0700 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-06-01 17:50:13 -0700 |
| commit | dfb25e2091dad983cc3d74f7141ffcc22102e0f1 (patch) | |
| tree | 379255a408dbe8c41c77abdf0eb864f750fd8825 /core/java | |
| parent | 77c1cc0aa4d088f54c3b36a05a19acfa5295c4da (diff) | |
| parent | 8d02deabac62c4a68a335a7b3141795466362b89 (diff) | |
| download | frameworks_base-dfb25e2091dad983cc3d74f7141ffcc22102e0f1.zip frameworks_base-dfb25e2091dad983cc3d74f7141ffcc22102e0f1.tar.gz frameworks_base-dfb25e2091dad983cc3d74f7141ffcc22102e0f1.tar.bz2 | |
Merge "Implement bug 4500971 - Collapsable action views"
Diffstat (limited to 'core/java')
13 files changed, 637 insertions, 118 deletions
diff --git a/core/java/android/view/MenuItem.java b/core/java/android/view/MenuItem.java index 780c52e..dc68264 100644 --- a/core/java/android/view/MenuItem.java +++ b/core/java/android/view/MenuItem.java @@ -51,6 +51,13 @@ public interface MenuItem { * it also has an icon specified. */ public static final int SHOW_AS_ACTION_WITH_TEXT = 4; + + /** + * This item's action view collapses to a normal menu item. + * When expanded, the action view temporarily takes over + * a larger segment of its container. + */ + public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; /** * Interface definition for a callback to be invoked when a menu item is @@ -74,6 +81,34 @@ public interface MenuItem { } /** + * Interface definition for a callback to be invoked when a menu item + * marked with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} is + * expanded or collapsed. + * + * @see MenuItem#expandActionView() + * @see MenuItem#collapseActionView() + * @see MenuItem#setShowAsActionFlags(int) + * @see MenuItem# + */ + public interface OnActionExpandListener { + /** + * Called when a menu item with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} + * is expanded. + * @param item Item that was expanded + * @return true if the item should expand, false if expansion should be suppressed. + */ + public boolean onMenuItemActionExpand(MenuItem item); + + /** + * Called when a menu item with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW} + * is collapsed. + * @param item Item that was collapsed + * @return true if the item should collapse, false if collapsing should be suppressed. + */ + public boolean onMenuItemActionCollapse(MenuItem item); + } + + /** * Return the identifier for this menu item. The identifier can not * be changed after the menu is created. * @@ -421,6 +456,27 @@ public interface MenuItem { public void setShowAsAction(int actionEnum); /** + * Sets how this item should display in the presence of an Action Bar. + * The parameter actionEnum is a flag set. One of {@link #SHOW_AS_ACTION_ALWAYS}, + * {@link #SHOW_AS_ACTION_IF_ROOM}, or {@link #SHOW_AS_ACTION_NEVER} should + * be used, and you may optionally OR the value with {@link #SHOW_AS_ACTION_WITH_TEXT}. + * SHOW_AS_ACTION_WITH_TEXT requests that when the item is shown as an action, + * it should be shown with a text label. + * + * <p>Note: This method differs from {@link #setShowAsAction(int)} only in that it + * returns the current MenuItem instance for call chaining. + * + * @param actionEnum How the item should display. One of + * {@link #SHOW_AS_ACTION_ALWAYS}, {@link #SHOW_AS_ACTION_IF_ROOM}, or + * {@link #SHOW_AS_ACTION_NEVER}. SHOW_AS_ACTION_NEVER is the default. + * + * @see android.app.ActionBar + * @see #setActionView(View) + * @return This MenuItem instance for call chaining. + */ + public MenuItem setShowAsActionFlags(int actionEnum); + + /** * Set an action view for this menu item. An action view will be displayed in place * of an automatically generated menu item element in the UI when this item is shown * as an action within a parent. @@ -453,4 +509,52 @@ public interface MenuItem { * @see #setShowAsAction(int) */ public View getActionView(); + + /** + * Expand the action view associated with this menu item. + * The menu item must have an action view set, as well as + * the showAsAction flag {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. + * If a listener has been set using {@link #setOnActionExpandListener(OnActionExpandListener)} + * it will have its {@link OnActionExpandListener#onMenuItemActionExpand(MenuItem)} + * method invoked. The listener may return false from this method to prevent expanding + * the action view. + * + * @return true if the action view was expanded, false otherwise. + */ + public boolean expandActionView(); + + /** + * Collapse the action view associated with this menu item. + * The menu item must have an action view set, as well as the showAsAction flag + * {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. If a listener has been set using + * {@link #setOnActionExpandListener(OnActionExpandListener)} it will have its + * {@link OnActionExpandListener#onMenuItemActionCollapse(MenuItem)} method invoked. + * The listener may return false from this method to prevent collapsing the action view. + * + * @return true if the action view was collapsed, false otherwise. + */ + public boolean collapseActionView(); + + /** + * Returns true if this menu item's action view has been expanded. + * + * @return true if the item's action view is expanded, false otherwise. + * + * @see #expandActionView() + * @see #collapseActionView() + * @see #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW + * @see OnActionExpandListener + */ + public boolean isActionViewExpanded(); + + /** + * Set an {@link OnActionExpandListener} on this menu item to be notified when + * the associated action view is expanded or collapsed. The menu item must + * be configured to expand or collapse its action view using the flag + * {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}. + * + * @param listener Listener that will respond to expand/collapse events + * @return This menu item instance for call chaining + */ + public MenuItem setOnActionExpandListener(OnActionExpandListener listener); }
\ No newline at end of file diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java index 0ef4861..a4bcf60 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuItem.java +++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java @@ -236,4 +236,31 @@ public class ActionMenuItem implements MenuItem { public MenuItem setActionView(int resId) { throw new UnsupportedOperationException(); } + + @Override + public MenuItem setShowAsActionFlags(int actionEnum) { + setShowAsAction(actionEnum); + return this; + } + + @Override + public boolean expandActionView() { + return false; + } + + @Override + public boolean collapseActionView() { + return false; + } + + @Override + public boolean isActionViewExpanded() { + return false; + } + + @Override + public MenuItem setOnActionExpandListener(OnActionExpandListener listener) { + // No need to save the listener; ActionMenuItem does not support collapsing items. + return this; + } } diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index 0051ec3..91dd7e3 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -112,8 +112,11 @@ public class ActionMenuPresenter extends BaseMenuPresenter { @Override public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) { - final View actionView = item.getActionView(); - return actionView != null ? actionView : super.getItemView(item, convertView, parent); + View actionView = item.getActionView(); + actionView = actionView != null && !item.hasCollapsibleActionView() ? + actionView : super.getItemView(item, convertView, parent); + actionView.setVisibility(item.isActionViewExpanded() ? View.GONE : View.VISIBLE); + return actionView; } @Override @@ -303,7 +306,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter { if (item.requiresActionButton()) { View v = item.getActionView(); - if (v == null) { + if (v == null || item.hasCollapsibleActionView()) { v = getItemView(item, mScrapActionButtonView, parent); if (mScrapActionButtonView == null) { mScrapActionButtonView = v; @@ -329,7 +332,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter { if (isAction) { View v = item.getActionView(); - if (v == null) { + if (v == null || item.hasCollapsibleActionView()) { v = getItemView(item, mScrapActionButtonView, parent); if (mScrapActionButtonView == null) { mScrapActionButtonView = v; diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java index 16f51fd..ddbb08c 100644 --- a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java @@ -192,4 +192,12 @@ public abstract class BaseMenuPresenter implements MenuPresenter { public boolean flagActionItems() { return false; } + + public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { + return false; + } + + public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { + return false; + } } diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java index 2cb2a10..f8d24a3 100644 --- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java @@ -159,6 +159,14 @@ public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClick return false; } + public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { + return false; + } + + public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { + return false; + } + public void saveHierarchyState(Bundle outState) { SparseArray<Parcelable> viewStates = new SparseArray<Parcelable>(); if (mMenuView != null) { diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index e9fcb23..fdfa954 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -744,11 +744,14 @@ public class MenuBuilder implements Menu { if (itemImpl == null || !itemImpl.isEnabled()) { return false; - } + } boolean invoked = itemImpl.invoke(); - if (item.hasSubMenu()) { + if (itemImpl.hasCollapsibleActionView()) { + invoked |= itemImpl.expandActionView(); + if (invoked) close(true); + } else if (item.hasSubMenu()) { close(false); invoked |= dispatchSubMenuSelected((SubMenuBuilder) item.getSubMenu()); @@ -1081,4 +1084,42 @@ public class MenuBuilder implements Menu { boolean getOptionalIconsVisible() { return mOptionalIconsVisible; } + + public boolean expandItemActionView(MenuItemImpl item) { + if (mPresenters.isEmpty()) return false; + + boolean expanded = false; + + stopDispatchingItemsChanged(); + for (WeakReference<MenuPresenter> ref : mPresenters) { + final MenuPresenter presenter = ref.get(); + if (presenter == null) { + mPresenters.remove(ref); + } else if ((expanded = presenter.expandItemActionView(this, item))) { + break; + } + } + startDispatchingItemsChanged(); + + return expanded; + } + + public boolean collapseItemActionView(MenuItemImpl item) { + if (mPresenters.isEmpty()) return false; + + boolean collapsed = false; + + stopDispatchingItemsChanged(); + for (WeakReference<MenuPresenter> ref : mPresenters) { + final MenuPresenter presenter = ref.get(); + if (presenter == null) { + mPresenters.remove(ref); + } else if ((collapsed = presenter.collapseItemActionView(this, item))) { + break; + } + } + startDispatchingItemsChanged(); + + return collapsed; + } } diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java index c6d386d..f2430e4 100644 --- a/core/java/com/android/internal/view/menu/MenuItemImpl.java +++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java @@ -77,6 +77,8 @@ public final class MenuItemImpl implements MenuItem { private int mShowAsAction = SHOW_AS_ACTION_NEVER; private View mActionView; + private OnActionExpandListener mOnActionExpandListener; + private boolean mIsActionViewExpanded = false; /** Used for the icon resource ID if this item does not have an icon */ static final int NO_ICON = 0; @@ -561,4 +563,61 @@ public final class MenuItemImpl implements MenuItem { public View getActionView() { return mActionView; } + + @Override + public MenuItem setShowAsActionFlags(int actionEnum) { + setShowAsAction(actionEnum); + return this; + } + + @Override + public boolean expandActionView() { + if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0 || mActionView == null) { + return false; + } + + if (mOnActionExpandListener == null || + mOnActionExpandListener.onMenuItemActionExpand(this)) { + return mMenu.expandItemActionView(this); + } + + return false; + } + + @Override + public boolean collapseActionView() { + if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0) { + return false; + } + if (mActionView == null) { + // We're already collapsed if we have no action view. + return true; + } + + if (mOnActionExpandListener == null || + mOnActionExpandListener.onMenuItemActionCollapse(this)) { + return mMenu.collapseItemActionView(this); + } + + return false; + } + + @Override + public MenuItem setOnActionExpandListener(OnActionExpandListener listener) { + mOnActionExpandListener = listener; + return this; + } + + public boolean hasCollapsibleActionView() { + return (mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0 && mActionView != null; + } + + public void setActionViewExpanded(boolean isExpanded) { + mIsActionViewExpanded = isExpanded; + mMenu.onItemsChanged(false); + } + + public boolean isActionViewExpanded() { + return mIsActionViewExpanded; + } } diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 38cec29..5767519 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -250,6 +250,14 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On return false; } + public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { + return false; + } + + public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { + return false; + } + private class MenuAdapter extends BaseAdapter { private MenuBuilder mAdapterMenu; diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java index 5baf419..bd66448 100644 --- a/core/java/com/android/internal/view/menu/MenuPresenter.java +++ b/core/java/com/android/internal/view/menu/MenuPresenter.java @@ -107,4 +107,22 @@ public interface MenuPresenter { * @return true if this presenter changed the action status of any items. */ public boolean flagActionItems(); + + /** + * Called when a menu item with a collapsable action view should expand its action view. + * + * @param menu Menu containing the item to be expanded + * @param item Item to be expanded + * @return true if this presenter expanded the action view, false otherwise. + */ + public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item); + + /** + * Called when a menu item with a collapsable action view should collapse its action view. + * + * @param menu Menu containing the item to be collapsed + * @param item Item to be collapsed + * @return true if this presenter collapsed the action view, false otherwise. + */ + public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item); } diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java index 834041f..fb1cd5e 100644 --- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java +++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java @@ -111,4 +111,14 @@ public class SubMenuBuilder extends MenuBuilder implements SubMenu { public SubMenu setHeaderView(View view) { return (SubMenu) super.setHeaderViewInt(view); } + + @Override + public boolean expandItemActionView(MenuItemImpl item) { + return mParentMenu.expandItemActionView(item); + } + + @Override + public boolean collapseItemActionView(MenuItemImpl item) { + return mParentMenu.collapseItemActionView(item); + } } diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java index 788883b..ccbce3e 100644 --- a/core/java/com/android/internal/widget/AbsActionBarView.java +++ b/core/java/com/android/internal/widget/AbsActionBarView.java @@ -30,7 +30,7 @@ import android.view.animation.DecelerateInterpolator; public abstract class AbsActionBarView extends ViewGroup { protected ActionMenuView mMenuView; - protected ActionMenuPresenter mMenuPresenter; + protected ActionMenuPresenter mActionMenuPresenter; protected ActionBarContainer mSplitView; protected Animator mVisibilityAnim; @@ -108,8 +108,8 @@ public abstract class AbsActionBarView extends ViewGroup { } public boolean showOverflowMenu() { - if (mMenuPresenter != null) { - return mMenuPresenter.showOverflowMenu(); + if (mActionMenuPresenter != null) { + return mActionMenuPresenter.showOverflowMenu(); } return false; } @@ -123,26 +123,26 @@ public abstract class AbsActionBarView extends ViewGroup { } public boolean hideOverflowMenu() { - if (mMenuPresenter != null) { - return mMenuPresenter.hideOverflowMenu(); + if (mActionMenuPresenter != null) { + return mActionMenuPresenter.hideOverflowMenu(); } return false; } public boolean isOverflowMenuShowing() { - if (mMenuPresenter != null) { - return mMenuPresenter.isOverflowMenuShowing(); + if (mActionMenuPresenter != null) { + return mActionMenuPresenter.isOverflowMenuShowing(); } return false; } public boolean isOverflowReserved() { - return mMenuPresenter != null && mMenuPresenter.isOverflowReserved(); + return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved(); } public void dismissPopupMenus() { - if (mMenuPresenter != null) { - mMenuPresenter.dismissPopupMenus(); + if (mActionMenuPresenter != null) { + mActionMenuPresenter.dismissPopupMenus(); } } diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index deed1c5..e4c4989 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -167,9 +167,9 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi }); final MenuBuilder menu = (MenuBuilder) mode.getMenu(); - mMenuPresenter = new ActionMenuPresenter(); - menu.addMenuPresenter(mMenuPresenter); - mMenuView = (ActionMenuView) mMenuPresenter.getMenuView(this); + mActionMenuPresenter = new ActionMenuPresenter(); + menu.addMenuPresenter(mActionMenuPresenter); + mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); @@ -178,10 +178,10 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi addView(mMenuView); } else { // Allow full screen width in split mode. - mMenuPresenter.setWidthLimit( + mActionMenuPresenter.setWidthLimit( getContext().getResources().getDisplayMetrics().widthPixels, true); // No limit to the item count; use whatever will fit. - mMenuPresenter.setItemLimit(Integer.MAX_VALUE); + mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); // Span the whole width layoutParams.width = LayoutParams.MATCH_PARENT; mSplitView.addView(mMenuView); @@ -227,24 +227,24 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi @Override public boolean showOverflowMenu() { - if (mMenuPresenter != null) { - return mMenuPresenter.showOverflowMenu(); + if (mActionMenuPresenter != null) { + return mActionMenuPresenter.showOverflowMenu(); } return false; } @Override public boolean hideOverflowMenu() { - if (mMenuPresenter != null) { - return mMenuPresenter.hideOverflowMenu(); + if (mActionMenuPresenter != null) { + return mActionMenuPresenter.hideOverflowMenu(); } return false; } @Override public boolean isOverflowMenuShowing() { - if (mMenuPresenter != null) { - return mMenuPresenter.isOverflowMenuShowing(); + if (mActionMenuPresenter != null) { + return mActionMenuPresenter.isOverflowMenuShowing(); } return false; } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index b7aac14..0cf84d2 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -21,7 +21,10 @@ import com.android.internal.view.menu.ActionMenuItem; import com.android.internal.view.menu.ActionMenuPresenter; import com.android.internal.view.menu.ActionMenuView; import com.android.internal.view.menu.MenuBuilder; +import com.android.internal.view.menu.MenuItemImpl; import com.android.internal.view.menu.MenuPresenter; +import com.android.internal.view.menu.MenuView; +import com.android.internal.view.menu.SubMenuBuilder; import android.app.ActionBar; import android.app.ActionBar.OnNavigationListener; @@ -33,21 +36,22 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; import android.text.TextUtils; -import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.view.Window; import android.widget.AdapterView; import android.widget.FrameLayout; -import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; @@ -87,9 +91,8 @@ public class ActionBarView extends AbsActionBarView { private Drawable mIcon; private Drawable mLogo; - private View mHomeLayout; - private View mHomeAsUpView; - private ImageView mIconView; + private HomeView mHomeLayout; + private HomeView mExpandedHomeLayout; private LinearLayout mTitleLayout; private TextView mTitleView; private TextView mSubtitleView; @@ -124,6 +127,9 @@ public class ActionBarView extends AbsActionBarView { private Runnable mTabSelector; + private ExpandedActionViewMenuPresenter mExpandedMenuPresenter; + View mExpandedActionView; + private final AdapterView.OnItemSelectedListener mNavItemSelectedListener = new AdapterView.OnItemSelectedListener() { public void onItemSelected(AdapterView parent, View view, int position, long id) { @@ -136,7 +142,15 @@ public class ActionBarView extends AbsActionBarView { } }; - private OnClickListener mTabClickListener = null; + private final OnClickListener mExpandedActionViewUpListener = new OnClickListener() { + @Override + public void onClick(View v) { + final MenuItemImpl item = mExpandedMenuPresenter.mCurrentExpandedItem; + if (item != null) { + item.collapseActionView(); + } + } + }; public ActionBarView(Context context, AttributeSet attrs) { super(context, attrs); @@ -187,10 +201,11 @@ public class ActionBarView extends AbsActionBarView { com.android.internal.R.styleable.ActionBar_homeLayout, com.android.internal.R.layout.action_bar_home); - mHomeLayout = inflater.inflate(homeResId, this, false); + mHomeLayout = (HomeView) inflater.inflate(homeResId, this, false); - mHomeAsUpView = mHomeLayout.findViewById(com.android.internal.R.id.up); - mIconView = (ImageView) mHomeLayout.findViewById(com.android.internal.R.id.home); + mExpandedHomeLayout = (HomeView) inflater.inflate(homeResId, this, false); + mExpandedHomeLayout.setUp(true); + mExpandedHomeLayout.setOnClickListener(mExpandedActionViewUpListener); mTitleStyleRes = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0); mSubtitleStyleRes = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0); @@ -298,7 +313,8 @@ public class ActionBarView extends AbsActionBarView { if (menu == mOptionsMenu) return; if (mOptionsMenu != null) { - mOptionsMenu.removeMenuPresenter(mMenuPresenter); + mOptionsMenu.removeMenuPresenter(mActionMenuPresenter); + mOptionsMenu.removeMenuPresenter(mExpandedMenuPresenter); } MenuBuilder builder = (MenuBuilder) menu; @@ -306,13 +322,15 @@ public class ActionBarView extends AbsActionBarView { if (mMenuView != null) { removeView(mMenuView); } - if (mMenuPresenter == null) { - mMenuPresenter = new ActionMenuPresenter(); - mMenuPresenter.setCallback(cb); + if (mActionMenuPresenter == null) { + mActionMenuPresenter = new ActionMenuPresenter(); + mActionMenuPresenter.setCallback(cb); + mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter(); } - builder.addMenuPresenter(mMenuPresenter); + builder.addMenuPresenter(mActionMenuPresenter); + builder.addMenuPresenter(mExpandedMenuPresenter); - final ActionMenuView menuView = (ActionMenuView) mMenuPresenter.getMenuView(this); + final ActionMenuView menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); menuView.setLayoutParams(layoutParams); @@ -320,10 +338,10 @@ public class ActionBarView extends AbsActionBarView { addView(menuView); } else { // Allow full screen width in split mode. - mMenuPresenter.setWidthLimit( + mActionMenuPresenter.setWidthLimit( getContext().getResources().getDisplayMetrics().widthPixels, true); // No limit to the item count; use whatever will fit. - mMenuPresenter.setItemLimit(Integer.MAX_VALUE); + mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); // Span the whole width layoutParams.width = LayoutParams.MATCH_PARENT; if (mSplitView != null) { @@ -411,13 +429,12 @@ public class ActionBarView extends AbsActionBarView { mHomeLayout.setVisibility(vis); if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) { - final boolean isUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0; - mHomeAsUpView.setVisibility(isUp ? VISIBLE : GONE); + mHomeLayout.setUp((options & ActionBar.DISPLAY_HOME_AS_UP) != 0); } if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) { final boolean logoVis = mLogo != null && (options & ActionBar.DISPLAY_USE_LOGO) != 0; - mIconView.setImageDrawable(logoVis ? mLogo : mIcon); + mHomeLayout.setIcon(logoVis ? mLogo : mIcon); } if ((flagsChanged & ActionBar.DISPLAY_SHOW_TITLE) != 0) { @@ -457,7 +474,7 @@ public class ActionBarView extends AbsActionBarView { mIcon = icon; if (icon != null && ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null)) { - mIconView.setImageDrawable(icon); + mHomeLayout.setIcon(icon); } } @@ -468,7 +485,7 @@ public class ActionBarView extends AbsActionBarView { public void setLogo(Drawable logo) { mLogo = logo; if (logo != null && (mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) != 0) { - mIconView.setImageDrawable(logo); + mHomeLayout.setIcon(logo); } } @@ -482,7 +499,7 @@ public class ActionBarView extends AbsActionBarView { private int getPreferredIconDensity() { final Resources res = mContext.getResources(); final int availableHeight = getLayoutParams().height - - mIconView.getPaddingTop() - mIconView.getPaddingBottom(); + mHomeLayout.getVerticalIconPadding(); int iconSize = res.getDimensionPixelSize(android.R.dimen.app_icon_size); if (iconSize * DisplayMetrics.DENSITY_LOW >= availableHeight) { @@ -685,10 +702,13 @@ public class ActionBarView extends AbsActionBarView { int leftOfCenter = availableWidth / 2; int rightOfCenter = leftOfCenter; - if (mHomeLayout.getVisibility() != GONE) { - mHomeLayout.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), + View homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout; + + if (homeLayout.getVisibility() != GONE) { + homeLayout.measure( + MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - final int homeWidth = mHomeLayout.getMeasuredWidth(); + final int homeWidth = homeLayout.getMeasuredWidth(); availableWidth = Math.max(0, availableWidth - homeWidth); leftOfCenter = Math.max(0, availableWidth - homeWidth); } @@ -699,40 +719,42 @@ public class ActionBarView extends AbsActionBarView { rightOfCenter = Math.max(0, rightOfCenter - mMenuView.getMeasuredWidth()); } - boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE && - (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0; - if (showTitle) { - availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0); - leftOfCenter = Math.max(0, leftOfCenter - mTitleLayout.getMeasuredWidth()); - } - - switch (mNavigationMode) { - case ActionBar.NAVIGATION_MODE_LIST: - if (mListNavLayout != null) { - final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding; - availableWidth = Math.max(0, availableWidth - itemPaddingSize); - leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize); - mListNavLayout.measure( - MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - final int listNavWidth = mListNavLayout.getMeasuredWidth(); - availableWidth = Math.max(0, availableWidth - listNavWidth); - leftOfCenter = Math.max(0, leftOfCenter - listNavWidth); - } - break; - case ActionBar.NAVIGATION_MODE_TABS: - if (mTabScrollView != null) { - final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding; - availableWidth = Math.max(0, availableWidth - itemPaddingSize); - leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize); - mTabScrollView.measure( - MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - final int tabWidth = mTabScrollView.getMeasuredWidth(); - availableWidth = Math.max(0, availableWidth - tabWidth); - leftOfCenter = Math.max(0, leftOfCenter - tabWidth); - } - break; + if (mExpandedActionView == null) { + boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE && + (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0; + if (showTitle) { + availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0); + leftOfCenter = Math.max(0, leftOfCenter - mTitleLayout.getMeasuredWidth()); + } + + switch (mNavigationMode) { + case ActionBar.NAVIGATION_MODE_LIST: + if (mListNavLayout != null) { + final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding; + availableWidth = Math.max(0, availableWidth - itemPaddingSize); + leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize); + mListNavLayout.measure( + MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); + final int listNavWidth = mListNavLayout.getMeasuredWidth(); + availableWidth = Math.max(0, availableWidth - listNavWidth); + leftOfCenter = Math.max(0, leftOfCenter - listNavWidth); + } + break; + case ActionBar.NAVIGATION_MODE_TABS: + if (mTabScrollView != null) { + final int itemPaddingSize = showTitle ? mItemPadding * 2 : mItemPadding; + availableWidth = Math.max(0, availableWidth - itemPaddingSize); + leftOfCenter = Math.max(0, leftOfCenter - itemPaddingSize); + mTabScrollView.measure( + MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); + final int tabWidth = mTabScrollView.getMeasuredWidth(); + availableWidth = Math.max(0, availableWidth - tabWidth); + leftOfCenter = Math.max(0, leftOfCenter - tabWidth); + } + break; + } } if (mIndeterminateProgressView != null && @@ -743,8 +765,16 @@ public class ActionBarView extends AbsActionBarView { rightOfCenter - mIndeterminateProgressView.getMeasuredWidth()); } - if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) { - final LayoutParams lp = generateLayoutParams(mCustomNavView.getLayoutParams()); + View customView = null; + if (mExpandedActionView != null) { + customView = mExpandedActionView; + } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && + mCustomNavView != null) { + customView = mCustomNavView; + } + + if (customView != null) { + final LayoutParams lp = generateLayoutParams(customView.getLayoutParams()); final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? (ActionBar.LayoutParams) lp : null; @@ -781,7 +811,7 @@ public class ActionBarView extends AbsActionBarView { customNavWidth = Math.min(leftOfCenter, rightOfCenter) * 2; } - mCustomNavView.measure( + customView.measure( MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode), MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode)); } @@ -822,31 +852,34 @@ public class ActionBarView extends AbsActionBarView { return; } - if (mHomeLayout.getVisibility() != GONE) { - x += positionChild(mHomeLayout, x, y, contentHeight); - } - - final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE && - (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0; - if (showTitle) { - x += positionChild(mTitleLayout, x, y, contentHeight); + View homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout; + if (homeLayout.getVisibility() != GONE) { + x += positionChild(homeLayout, x, y, contentHeight); } - switch (mNavigationMode) { - case ActionBar.NAVIGATION_MODE_STANDARD: - break; - case ActionBar.NAVIGATION_MODE_LIST: - if (mListNavLayout != null) { - if (showTitle) x += mItemPadding; - x += positionChild(mListNavLayout, x, y, contentHeight) + mItemPadding; + if (mExpandedActionView == null) { + final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE && + (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0; + if (showTitle) { + x += positionChild(mTitleLayout, x, y, contentHeight); } - break; - case ActionBar.NAVIGATION_MODE_TABS: - if (mTabScrollView != null) { - if (showTitle) x += mItemPadding; - x += positionChild(mTabScrollView, x, y, contentHeight) + mItemPadding; + + switch (mNavigationMode) { + case ActionBar.NAVIGATION_MODE_STANDARD: + break; + case ActionBar.NAVIGATION_MODE_LIST: + if (mListNavLayout != null) { + if (showTitle) x += mItemPadding; + x += positionChild(mListNavLayout, x, y, contentHeight) + mItemPadding; + } + break; + case ActionBar.NAVIGATION_MODE_TABS: + if (mTabScrollView != null) { + if (showTitle) x += mItemPadding; + x += positionChild(mTabScrollView, x, y, contentHeight) + mItemPadding; + } + break; } - break; } int menuLeft = r - l - getPaddingRight(); @@ -861,13 +894,20 @@ public class ActionBarView extends AbsActionBarView { menuLeft -= mIndeterminateProgressView.getMeasuredWidth(); } - if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { - LayoutParams lp = mCustomNavView.getLayoutParams(); + View customView = null; + if (mExpandedActionView != null) { + customView = mExpandedActionView; + } else if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && + mCustomNavView != null) { + customView = mCustomNavView; + } + if (customView != null) { + LayoutParams lp = customView.getLayoutParams(); final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? (ActionBar.LayoutParams) lp : null; final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY; - final int navWidth = mCustomNavView.getMeasuredWidth(); + final int navWidth = customView.getMeasuredWidth(); int topMargin = 0; int bottomMargin = 0; @@ -907,17 +947,17 @@ public class ActionBarView extends AbsActionBarView { case Gravity.CENTER_VERTICAL: final int paddedTop = mTop + getPaddingTop(); final int paddedBottom = mBottom - getPaddingBottom(); - ypos = ((paddedBottom - paddedTop) - mCustomNavView.getMeasuredHeight()) / 2; + ypos = ((paddedBottom - paddedTop) - customView.getMeasuredHeight()) / 2; break; case Gravity.TOP: ypos = getPaddingTop() + topMargin; break; case Gravity.BOTTOM: - ypos = getHeight() - getPaddingBottom() - mCustomNavView.getMeasuredHeight() + ypos = getHeight() - getPaddingBottom() - customView.getMeasuredHeight() - bottomMargin; break; } - x += positionChild(mCustomNavView, xpos, ypos, contentHeight); + x += positionChild(customView, xpos, ypos, contentHeight); } if (mProgressView != null) { @@ -928,9 +968,83 @@ public class ActionBarView extends AbsActionBarView { } } + @Override + public LayoutParams generateLayoutParams(LayoutParams lp) { + if (lp == null) { + lp = generateDefaultLayoutParams(); + } + return lp; + } + + @Override + public Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + SavedState state = new SavedState(superState); + + if (mExpandedMenuPresenter != null && mExpandedMenuPresenter.mCurrentExpandedItem != null) { + state.expandedMenuItemId = mExpandedMenuPresenter.mCurrentExpandedItem.getItemId(); + } + + state.isOverflowOpen = isOverflowMenuShowing(); + + return state; + } + + @Override + public void onRestoreInstanceState(Parcelable p) { + SavedState state = (SavedState) p; + + super.onRestoreInstanceState(state.getSuperState()); + + if (state.expandedMenuItemId != 0 && + mExpandedMenuPresenter != null && mOptionsMenu != null) { + final MenuItem item = mOptionsMenu.findItem(state.expandedMenuItemId); + if (item != null) { + item.expandActionView(); + } + } + + if (state.isOverflowOpen) { + postShowOverflowMenu(); + } + } + + static class SavedState extends BaseSavedState { + int expandedMenuItemId; + boolean isOverflowOpen; + + SavedState(Parcelable superState) { + super(superState); + } + + private SavedState(Parcel in) { + super(in); + expandedMenuItemId = in.readInt(); + isOverflowOpen = in.readInt() != 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeInt(expandedMenuItemId); + out.writeInt(isOverflowOpen ? 1 : 0); + } + + public static final Parcelable.Creator<SavedState> CREATOR = + new Parcelable.Creator<SavedState>() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + private static class HomeView extends FrameLayout { private View mUpView; - private View mIconView; + private ImageView mIconView; public HomeView(Context context) { this(context, null); @@ -940,12 +1054,24 @@ public class ActionBarView extends AbsActionBarView { super(context, attrs); } + public void setUp(boolean isUp) { + mUpView.setVisibility(isUp ? VISIBLE : GONE); + } + + public void setIcon(Drawable icon) { + mIconView.setImageDrawable(icon); + } + @Override protected void onFinishInflate() { mUpView = findViewById(com.android.internal.R.id.up); mIconView = (ImageView) findViewById(com.android.internal.R.id.home); } + public int getVerticalIconPadding() { + return mIconView.getPaddingTop() + mIconView.getPaddingBottom(); + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0); @@ -983,4 +1109,111 @@ public class ActionBarView extends AbsActionBarView { mIconView.layout(iconLeft, iconTop, iconLeft + iconWidth, iconTop + iconHeight); } } + + private class ExpandedActionViewMenuPresenter implements MenuPresenter { + MenuBuilder mMenu; + MenuItemImpl mCurrentExpandedItem; + + @Override + public void initForMenu(Context context, MenuBuilder menu) { + // Clear the expanded action view when menus change. + mExpandedActionView = null; + if (mCurrentExpandedItem != null) { + mCurrentExpandedItem.collapseActionView(); + } + mMenu = menu; + } + + @Override + public MenuView getMenuView(ViewGroup root) { + return null; + } + + @Override + public void updateMenuView(boolean cleared) { + // Make sure the expanded item we have is still there. + if (mCurrentExpandedItem != null) { + boolean found = false; + final int count = mMenu.size(); + for (int i = 0; i < count; i++) { + final MenuItem item = mMenu.getItem(i); + if (item == mCurrentExpandedItem) { + found = true; + break; + } + } + + if (!found) { + // The item we had expanded disappeared. Collapse. + collapseItemActionView(mMenu, mCurrentExpandedItem); + } + } + } + + @Override + public void setCallback(Callback cb) { + } + + @Override + public boolean onSubMenuSelected(SubMenuBuilder subMenu) { + return false; + } + + @Override + public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { + } + + @Override + public boolean flagActionItems() { + return false; + } + + @Override + public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { + mExpandedActionView = item.getActionView(); + mExpandedHomeLayout.setIcon(item.getIcon()); + mCurrentExpandedItem = item; + if (mExpandedActionView.getParent() != ActionBarView.this) { + addView(mExpandedActionView); + } + if (mExpandedHomeLayout.getParent() != ActionBarView.this) { + addView(mExpandedHomeLayout); + } + mHomeLayout.setVisibility(GONE); + mTitleLayout.setVisibility(GONE); + if (mTabScrollView != null) mTabScrollView.setVisibility(GONE); + if (mSpinner != null) mSpinner.setVisibility(GONE); + if (mCustomNavView != null) mCustomNavView.setVisibility(GONE); + requestLayout(); + item.setActionViewExpanded(true); + return true; + } + + @Override + public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { + removeView(mExpandedActionView); + removeView(mExpandedHomeLayout); + if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) { + mHomeLayout.setVisibility(VISIBLE); + } + if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) { + mTitleLayout.setVisibility(VISIBLE); + } + if (mTabScrollView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { + mTabScrollView.setVisibility(VISIBLE); + } + if (mSpinner != null && mNavigationMode == ActionBar.NAVIGATION_MODE_LIST) { + mSpinner.setVisibility(VISIBLE); + } + if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { + mCustomNavView.setVisibility(VISIBLE); + } + mExpandedActionView = null; + mExpandedHomeLayout.setIcon(null); + mCurrentExpandedItem = null; + requestLayout(); + item.setActionViewExpanded(false); + return true; + } + } } |
