summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorAdam Powell <adamp@google.com>2011-08-10 22:49:02 -0700
committerAdam Powell <adamp@google.com>2011-08-10 23:18:34 -0700
commitf5645cbafe7eed33452d888f16726bee8a0cd9fe (patch)
treebf76a8b35438062b443fb679693fd8a983591d37 /core
parentbdbe6939ff336d97f707c7d85be2eca114d3f6a0 (diff)
downloadframeworks_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')
-rw-r--r--core/java/com/android/internal/app/ActionBarImpl.java32
-rw-r--r--core/java/com/android/internal/widget/ActionBarContainer.java3
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java17
-rw-r--r--core/java/com/android/internal/widget/ScrollingTabContainerView.java247
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;