diff options
author | Adam Powell <adamp@google.com> | 2011-08-10 22:49:02 -0700 |
---|---|---|
committer | Adam Powell <adamp@google.com> | 2011-08-10 23:18:34 -0700 |
commit | f5645cbafe7eed33452d888f16726bee8a0cd9fe (patch) | |
tree | bf76a8b35438062b443fb679693fd8a983591d37 /core | |
parent | bdbe6939ff336d97f707c7d85be2eca114d3f6a0 (diff) | |
download | frameworks_base-f5645cbafe7eed33452d888f16726bee8a0cd9fe.zip frameworks_base-f5645cbafe7eed33452d888f16726bee8a0cd9fe.tar.gz frameworks_base-f5645cbafe7eed33452d888f16726bee8a0cd9fe.tar.bz2 |
Fix bug 5122319 - When action bar tabs run out of space they should
collapse in to a spinner.
When tabs are not given the option of dropping to their own row,
collapse them into a spinner when they would measure too large to be
visible all at once.
Fix bug 5095167 - zombie tabs return when they shouldn't when activity
handles its own orientation changes
Change-Id: I074419d99a22aa5dd1cbc00a66e600ec5cb0b54a
Diffstat (limited to 'core')
4 files changed, 235 insertions, 64 deletions
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 008f400..0df7bcc 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -156,8 +156,6 @@ public class ActionBarImpl extends ActionBar { "with a compatible window decor layout"); } - mHasEmbeddedTabs = mContext.getResources().getBoolean( - com.android.internal.R.bool.action_bar_embed_tabs); mActionView.setContextView(mContextView); mContextDisplayMode = mActionView.isSplitActionBar() ? CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL; @@ -166,25 +164,31 @@ public class ActionBarImpl extends ActionBar { // Newer apps need to enable it explicitly. setHomeButtonEnabled(mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH); + + setHasEmbeddedTabs(mContext.getResources().getBoolean( + com.android.internal.R.bool.action_bar_embed_tabs)); } public void onConfigurationChanged(Configuration newConfig) { - mHasEmbeddedTabs = mContext.getResources().getBoolean( - com.android.internal.R.bool.action_bar_embed_tabs); + setHasEmbeddedTabs(mContext.getResources().getBoolean( + com.android.internal.R.bool.action_bar_embed_tabs)); + } + private void setHasEmbeddedTabs(boolean hasEmbeddedTabs) { + mHasEmbeddedTabs = hasEmbeddedTabs; // Switch tab layout configuration if needed if (!mHasEmbeddedTabs) { mActionView.setEmbeddedTabView(null); mContainerView.setTabContainer(mTabScrollView); } else { mContainerView.setTabContainer(null); - if (mTabScrollView != null) { - mTabScrollView.setVisibility(View.VISIBLE); - } mActionView.setEmbeddedTabView(mTabScrollView); } - mActionView.setCollapsable(!mHasEmbeddedTabs && - getNavigationMode() == NAVIGATION_MODE_TABS); + final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS; + if (mTabScrollView != null) { + mTabScrollView.setVisibility(isInTabMode ? View.VISIBLE : View.GONE); + } + mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode); } private void ensureTabsExist() { @@ -192,7 +196,7 @@ public class ActionBarImpl extends ActionBar { return; } - ScrollingTabContainerView tabScroller = mActionView.createTabContainer(); + ScrollingTabContainerView tabScroller = new ScrollingTabContainerView(mContext); if (mHasEmbeddedTabs) { tabScroller.setVisibility(View.VISIBLE); @@ -925,18 +929,14 @@ public class ActionBarImpl extends ActionBar { case NAVIGATION_MODE_TABS: mSavedTabPosition = getSelectedNavigationIndex(); selectTab(null); - if (!mActionView.hasEmbeddedTabs()) { - mTabScrollView.setVisibility(View.GONE); - } + mTabScrollView.setVisibility(View.GONE); break; } mActionView.setNavigationMode(mode); switch (mode) { case NAVIGATION_MODE_TABS: ensureTabsExist(); - if (!mActionView.hasEmbeddedTabs()) { - mTabScrollView.setVisibility(View.VISIBLE); - } + mTabScrollView.setVisibility(View.VISIBLE); if (mSavedTabPosition != INVALID_POSITION) { setSelectedNavigationItem(mSavedTabPosition); mSavedTabPosition = INVALID_POSITION; diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index a2d492b..b4d2d72 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -102,7 +102,7 @@ public class ActionBarContainer extends FrameLayout { return true; } - public void setTabContainer(View tabView) { + public void setTabContainer(ScrollingTabContainerView tabView) { if (mTabContainer != null) { removeView(mTabContainer); } @@ -110,6 +110,7 @@ public class ActionBarContainer extends FrameLayout { if (tabView != null) { addView(tabView); tabView.getLayoutParams().width = LayoutParams.MATCH_PARENT; + tabView.setAllowCollapse(false); } } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 61df5c7..181958c 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -332,7 +332,10 @@ public class ActionBarView extends AbsActionBarView { mIncludeTabs = tabs != null; if (mIncludeTabs && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { addView(mTabScrollView); - mTabScrollView.getLayoutParams().width = LayoutParams.WRAP_CONTENT; + ViewGroup.LayoutParams lp = mTabScrollView.getLayoutParams(); + lp.width = LayoutParams.WRAP_CONTENT; + lp.height = LayoutParams.MATCH_PARENT; + tabs.setAllowCollapse(true); } } @@ -649,18 +652,6 @@ public class ActionBarView extends AbsActionBarView { } } - public ScrollingTabContainerView createTabContainer() { - final LinearLayout tabLayout = new LinearLayout(getContext(), null, - com.android.internal.R.attr.actionBarTabBarStyle); - tabLayout.setMeasureWithLargestChildEnabled(true); - tabLayout.setLayoutParams(new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, mContentHeight)); - - final ScrollingTabContainerView scroller = new ScrollingTabContainerView(mContext); - scroller.setTabLayout(tabLayout); - return scroller; - } - public void setDropdownAdapter(SpinnerAdapter adapter) { mSpinnerAdapter = adapter; if (mSpinner != null) { diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java index 718d249..0e4c9ef 100644 --- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java +++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java @@ -30,18 +30,32 @@ import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; +import android.widget.AdapterView; +import android.widget.BaseAdapter; import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.Spinner; import android.widget.TextView; -public class ScrollingTabContainerView extends HorizontalScrollView { +/** + * This widget implements the dynamic action bar tab behavior that can change + * across different configurations or circumstances. + */ +public class ScrollingTabContainerView extends HorizontalScrollView + implements AdapterView.OnItemSelectedListener { + private static final String TAG = "ScrollingTabContainerView"; Runnable mTabSelector; private TabClickListener mTabClickListener; private LinearLayout mTabLayout; + private Spinner mTabSpinner; + private boolean mAllowCollapse; int mMaxTabWidth; + private int mContentHeight; + private int mSelectedTabIndex; protected Animator mVisibilityAnim; protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener(); @@ -53,14 +67,19 @@ public class ScrollingTabContainerView extends HorizontalScrollView { public ScrollingTabContainerView(Context context) { super(context); setHorizontalScrollBarEnabled(false); + + mTabLayout = createTabLayout(); + addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.MATCH_PARENT)); } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int widthMode = MeasureSpec.getMode(widthMeasureSpec); - setFillViewport(widthMode == MeasureSpec.EXACTLY); + final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY; + setFillViewport(lockedExpanded); - final int childCount = getChildCount(); + final int childCount = mTabLayout.getChildCount(); if (childCount > 1 && (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) { if (childCount > 2) { @@ -72,14 +91,85 @@ public class ScrollingTabContainerView extends HorizontalScrollView { mMaxTabWidth = -1; } + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + if (heightMode != MeasureSpec.UNSPECIFIED) { + if (mContentHeight == 0 && heightMode == MeasureSpec.EXACTLY) { + // Use this as our content height. + mContentHeight = heightSize; + } + heightSize = Math.min(heightSize, mContentHeight); + heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode); + } + + final boolean canCollapse = !lockedExpanded && mAllowCollapse; + + if (canCollapse) { + // See if we should expand + mTabLayout.measure(MeasureSpec.UNSPECIFIED, heightMeasureSpec); + if (mTabLayout.getMeasuredWidth() > MeasureSpec.getSize(widthMeasureSpec)) { + performCollapse(); + } else { + performExpand(); + } + } else { + performExpand(); + } + + final int oldWidth = getMeasuredWidth(); super.onMeasure(widthMeasureSpec, heightMeasureSpec); + final int newWidth = getMeasuredWidth(); + + if (lockedExpanded && oldWidth != newWidth) { + // Recenter the tab display if we're at a new (scrollable) size. + setTabSelected(mSelectedTabIndex); + } } - public void setTabSelected(int position) { - if (mTabLayout == null) { - return; + /** + * Indicates whether this view is collapsed into a dropdown menu instead + * of traditional tabs. + * @return true if showing as a spinner + */ + private boolean isCollapsed() { + return mTabSpinner != null && mTabSpinner.getParent() == this; + } + + public void setAllowCollapse(boolean allowCollapse) { + mAllowCollapse = allowCollapse; + } + + private void performCollapse() { + if (isCollapsed()) return; + + if (mTabSpinner == null) { + mTabSpinner = createSpinner(); } + removeView(mTabLayout); + addView(mTabSpinner, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + if (mTabSpinner.getAdapter() == null) { + mTabSpinner.setAdapter(new TabAdapter()); + } + if (mTabSelector != null) { + removeCallbacks(mTabSelector); + mTabSelector = null; + } + mTabSpinner.setSelection(mSelectedTabIndex); + } + private boolean performExpand() { + if (!isCollapsed()) return false; + + removeView(mTabSpinner); + addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + setTabSelected(mTabSpinner.getSelectedItemPosition()); + return false; + } + + public void setTabSelected(int position) { + mSelectedTabIndex = position; final int tabCount = mTabLayout.getChildCount(); for (int i = 0; i < tabCount; i++) { final View child = mTabLayout.getChildAt(i); @@ -92,10 +182,28 @@ public class ScrollingTabContainerView extends HorizontalScrollView { } public void setContentHeight(int contentHeight) { - mTabLayout.getLayoutParams().height = contentHeight; + mContentHeight = contentHeight; requestLayout(); } + private LinearLayout createTabLayout() { + final LinearLayout tabLayout = new LinearLayout(getContext(), null, + com.android.internal.R.attr.actionBarTabBarStyle); + tabLayout.setMeasureWithLargestChildEnabled(true); + tabLayout.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT)); + return tabLayout; + } + + private Spinner createSpinner() { + final Spinner spinner = new Spinner(getContext(), null, + com.android.internal.R.attr.actionDropDownStyle); + spinner.setLayoutParams(new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT)); + spinner.setOnItemSelectedListener(this); + return spinner; + } + @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -132,7 +240,7 @@ public class ScrollingTabContainerView extends HorizontalScrollView { } } - public void animateToTab(int position) { + public void animateToTab(final int position) { final View tabView = mTabLayout.getChildAt(position); if (mTabSelector != null) { removeCallbacks(mTabSelector); @@ -147,22 +255,15 @@ public class ScrollingTabContainerView extends HorizontalScrollView { post(mTabSelector); } - public void setTabLayout(LinearLayout tabLayout) { - if (mTabLayout != tabLayout) { - if (mTabLayout != null) { - ((ViewGroup) mTabLayout.getParent()).removeView(mTabLayout); - } - if (tabLayout != null) { - addView(tabLayout); - } - mTabLayout = tabLayout; + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mTabSelector != null) { + // Re-post the selector we saved + post(mTabSelector); } } - public LinearLayout getTabLayout() { - return mTabLayout; - } - @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); @@ -171,61 +272,112 @@ public class ScrollingTabContainerView extends HorizontalScrollView { } } - private TabView createTabView(ActionBar.Tab tab) { - final TabView tabView = new TabView(getContext(), tab); - tabView.setFocusable(true); + private TabView createTabView(ActionBar.Tab tab, boolean forAdapter) { + final TabView tabView = new TabView(getContext(), tab, forAdapter); + if (forAdapter) { + tabView.setBackgroundDrawable(null); + tabView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT, + mContentHeight)); + } else { + tabView.setFocusable(true); - if (mTabClickListener == null) { - mTabClickListener = new TabClickListener(); + if (mTabClickListener == null) { + mTabClickListener = new TabClickListener(); + } + tabView.setOnClickListener(mTabClickListener); } - tabView.setOnClickListener(mTabClickListener); return tabView; } public void addTab(ActionBar.Tab tab, boolean setSelected) { - View tabView = createTabView(tab); + TabView tabView = createTabView(tab, false); mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1)); + if (mTabSpinner != null) { + ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged(); + } if (setSelected) { tabView.setSelected(true); } + if (mAllowCollapse) { + requestLayout(); + } } public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { - final TabView tabView = createTabView(tab); + final TabView tabView = createTabView(tab, false); mTabLayout.addView(tabView, position, new LinearLayout.LayoutParams( 0, LayoutParams.MATCH_PARENT, 1)); + if (mTabSpinner != null) { + ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged(); + } if (setSelected) { tabView.setSelected(true); } + if (mAllowCollapse) { + requestLayout(); + } } public void updateTab(int position) { ((TabView) mTabLayout.getChildAt(position)).update(); + if (mTabSpinner != null) { + ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged(); + } + if (mAllowCollapse) { + requestLayout(); + } } public void removeTabAt(int position) { - if (mTabLayout != null) { - mTabLayout.removeViewAt(position); + mTabLayout.removeViewAt(position); + if (mTabSpinner != null) { + ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged(); + } + if (mAllowCollapse) { + requestLayout(); } } public void removeAllTabs() { - if (mTabLayout != null) { - mTabLayout.removeAllViews(); + mTabLayout.removeAllViews(); + if (mTabSpinner != null) { + ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged(); + } + if (mAllowCollapse) { + requestLayout(); } } + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + TabView tabView = (TabView) view; + tabView.getTab().select(); + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + } + private class TabView extends LinearLayout { private ActionBar.Tab mTab; private TextView mTextView; private ImageView mIconView; private View mCustomView; - public TabView(Context context, ActionBar.Tab tab) { + public TabView(Context context, ActionBar.Tab tab, boolean forList) { super(context, null, com.android.internal.R.attr.actionBarTabStyle); mTab = tab; + if (forList) { + setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); + } + + update(); + } + + public void bindTab(ActionBar.Tab tab) { + mTab = tab; update(); } @@ -303,6 +455,33 @@ public class ScrollingTabContainerView extends HorizontalScrollView { } } + private class TabAdapter extends BaseAdapter { + @Override + public int getCount() { + return mTabLayout.getChildCount(); + } + + @Override + public Object getItem(int position) { + return ((TabView) mTabLayout.getChildAt(position)).getTab(); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = createTabView((ActionBar.Tab) getItem(position), true); + } else { + ((TabView) convertView).bindTab((ActionBar.Tab) getItem(position)); + } + return convertView; + } + } + private class TabClickListener implements OnClickListener { public void onClick(View view) { TabView tabView = (TabView) view; |