diff options
| author | Adam Powell <adamp@google.com> | 2011-05-23 18:14:09 -0700 |
|---|---|---|
| committer | Adam Powell <adamp@google.com> | 2011-05-25 11:55:56 -0700 |
| commit | f8ac6b7394cfd37f01471bb35475ff2930eee140 (patch) | |
| tree | c15928961faf0cfe7436280cb653f22e2a18c3c3 /core/java | |
| parent | feeba076c660265ac390a32ff0f0c95747714459 (diff) | |
| download | frameworks_base-f8ac6b7394cfd37f01471bb35475ff2930eee140.zip frameworks_base-f8ac6b7394cfd37f01471bb35475ff2930eee140.tar.gz frameworks_base-f8ac6b7394cfd37f01471bb35475ff2930eee140.tar.bz2 | |
Action bar tab layout
Sync with UX on action bar tab layout behavior.
Make action bar tabs behave better with configuration changes.
LinearLayout now supports largest child measurement in unspecified
mode.
Change-Id: Id520641715a61c07e64124a0c5a70912996c98a0
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/app/Activity.java | 4 | ||||
| -rw-r--r-- | core/java/android/widget/LinearLayout.java | 54 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/ActionBarImpl.java | 103 | ||||
| -rw-r--r-- | core/java/com/android/internal/widget/ActionBarView.java | 200 | ||||
| -rw-r--r-- | core/java/com/android/internal/widget/ScrollingTabContainerView.java | 261 |
5 files changed, 424 insertions, 198 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 87369ab..3877bd0 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1398,6 +1398,10 @@ public class Activity extends ContextThemeWrapper public void onConfigurationChanged(Configuration newConfig) { mCalled = true; + if (mActionBar != null) { + mActionBar.onConfigurationChanged(newConfig); + } + mFragments.dispatchConfigurationChanged(newConfig); if (mWindow != null) { diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index bac849e..6f30452 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -696,7 +696,8 @@ public class LinearLayout extends ViewGroup { mTotalLength += mDividerHeight; } - if (useLargestChild && heightMode == MeasureSpec.AT_MOST) { + if (useLargestChild && + (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) { mTotalLength = 0; for (int i = 0; i < count; ++i) { @@ -809,6 +810,31 @@ public class LinearLayout extends ViewGroup { } else { alternativeMaxWidth = Math.max(alternativeMaxWidth, weightedMaxWidth); + + + // We have no limit, so make all weighted views as tall as the largest child. + // Children will have already been measured once. + if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) { + for (int i = 0; i < count; i++) { + final View child = getVirtualChildAt(i); + + if (child == null || child.getVisibility() == View.GONE) { + continue; + } + + final LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) child.getLayoutParams(); + + float childExtra = lp.weight; + if (childExtra > 0) { + child.measure( + MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(), + MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(largestChildHeight, + MeasureSpec.EXACTLY)); + } + } + } } if (!allFillParent && widthMode != MeasureSpec.EXACTLY) { @@ -1044,7 +1070,8 @@ public class LinearLayout extends ViewGroup { maxHeight = Math.max(maxHeight, ascent + descent); } - if (useLargestChild && widthMode == MeasureSpec.AT_MOST) { + if (useLargestChild && + (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) { mTotalLength = 0; for (int i = 0; i < count; ++i) { @@ -1200,6 +1227,29 @@ public class LinearLayout extends ViewGroup { } } else { alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight); + + // We have no limit, so make all weighted views as wide as the largest child. + // Children will have already been measured once. + if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) { + for (int i = 0; i < count; i++) { + final View child = getVirtualChildAt(i); + + if (child == null || child.getVisibility() == View.GONE) { + continue; + } + + final LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) child.getLayoutParams(); + + float childExtra = lp.weight; + if (childExtra > 0) { + child.measure( + MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), + MeasureSpec.EXACTLY)); + } + } + } } if (!allFillParent && heightMode != MeasureSpec.EXACTLY) { diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 1e576ce..183cfbd 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -16,25 +16,27 @@ package com.android.internal.app; +import com.android.internal.R; 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; +import com.android.internal.widget.ScrollingTabContainerView; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.animation.TimeInterpolator; import android.app.ActionBar; import android.app.Activity; import android.app.Dialog; import android.app.FragmentTransaction; import android.content.Context; +import android.content.res.Configuration; +import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Handler; import android.view.ActionMode; @@ -43,10 +45,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.view.Window; -import android.view.animation.DecelerateInterpolator; -import android.widget.HorizontalScrollView; import android.widget.SpinnerAdapter; import java.lang.ref.WeakReference; @@ -71,7 +70,7 @@ public class ActionBarImpl extends ActionBar { private ActionBarContextView mContextView; private ActionBarContainer mSplitView; private View mContentView; - private ViewGroup mExternalTabView; + private ScrollingTabContainerView mTabScrollView; private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>(); @@ -90,16 +89,17 @@ public class ActionBarImpl extends ActionBar { private static final int INVALID_POSITION = -1; private int mContextDisplayMode; + private boolean mHasEmbeddedTabs; + private int mContentHeight; final Handler mHandler = new Handler(); + Runnable mTabSelector; private Animator mCurrentShowAnim; private Animator mCurrentModeAnim; private boolean mShowHideAnimationEnabled; boolean mWasHiddenBeforeMode; - private static final TimeInterpolator sFadeOutInterpolator = new DecelerateInterpolator(); - final AnimatorListener mHideListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -150,21 +150,59 @@ 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; - if (!mActionView.hasEmbeddedTabs()) { - HorizontalScrollView tabScroller = new HorizontalScrollView(mContext); - ViewGroup tabContainer = mActionView.createTabContainer(); - tabScroller.setHorizontalFadingEdgeEnabled(true); - tabScroller.addView(tabContainer); + TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar); + mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0); + a.recycle(); + } + + public void onConfigurationChanged(Configuration newConfig) { + mHasEmbeddedTabs = mContext.getResources().getBoolean( + com.android.internal.R.bool.action_bar_embed_tabs); + + // 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); + } + + TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar); + mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0); + a.recycle(); + + if (mTabScrollView != null) { + mTabScrollView.getLayoutParams().height = mContentHeight; + mTabScrollView.requestLayout(); + } + } + + private void ensureTabsExist() { + if (mTabScrollView != null) { + return; + } + + ScrollingTabContainerView tabScroller = mActionView.createTabContainer(); + + if (mHasEmbeddedTabs) { + tabScroller.setVisibility(View.VISIBLE); + mActionView.setEmbeddedTabView(tabScroller); + } else { tabScroller.setVisibility(getNavigationMode() == NAVIGATION_MODE_TABS ? View.VISIBLE : View.GONE); - mActionView.setExternalTabLayout(tabContainer); mContainerView.setTabContainer(tabScroller); - mExternalTabView = tabScroller; } + mTabScrollView = tabScroller; } /** @@ -269,7 +307,7 @@ public class ActionBarImpl extends ActionBar { selectTab(null); } mTabs.clear(); - mActionView.removeAllTabs(); + mTabScrollView.removeAllTabs(); mSavedTabPosition = INVALID_POSITION; } @@ -365,7 +403,8 @@ public class ActionBarImpl extends ActionBar { @Override public void addTab(Tab tab, boolean setSelected) { - mActionView.addTab(tab, setSelected); + ensureTabsExist(); + mTabScrollView.addTab(tab, setSelected); configureTab(tab, mTabs.size()); if (setSelected) { selectTab(tab); @@ -374,7 +413,8 @@ public class ActionBarImpl extends ActionBar { @Override public void addTab(Tab tab, int position, boolean setSelected) { - mActionView.addTab(tab, position, setSelected); + ensureTabsExist(); + mTabScrollView.addTab(tab, position, setSelected); configureTab(tab, position); if (setSelected) { selectTab(tab); @@ -393,9 +433,14 @@ public class ActionBarImpl extends ActionBar { @Override public void removeTabAt(int position) { + if (mTabScrollView == null) { + // No tabs around to remove + return; + } + int selectedTabPosition = mSelectedTab != null ? mSelectedTab.getPosition() : mSavedTabPosition; - mActionView.removeTabAt(position); + mTabScrollView.removeTabAt(position); TabImpl removedTab = mTabs.remove(position); if (removedTab != null) { removedTab.setPosition(-1); @@ -424,9 +469,10 @@ public class ActionBarImpl extends ActionBar { if (mSelectedTab == tab) { if (mSelectedTab != null) { mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans); + mTabScrollView.animateToTab(tab.getPosition()); } } else { - mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION); + mTabScrollView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION); if (mSelectedTab != null) { mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans); } @@ -705,7 +751,9 @@ public class ActionBarImpl extends ActionBar { @Override public Tab setCustomView(View view) { mCustomView = view; - if (mPosition >= 0) mActionView.updateTab(mPosition); + if (mPosition >= 0) { + mTabScrollView.updateTab(mPosition); + } return this; } @@ -736,7 +784,9 @@ public class ActionBarImpl extends ActionBar { @Override public Tab setIcon(Drawable icon) { mIcon = icon; - if (mPosition >= 0) mActionView.updateTab(mPosition); + if (mPosition >= 0) { + mTabScrollView.updateTab(mPosition); + } return this; } @@ -748,7 +798,9 @@ public class ActionBarImpl extends ActionBar { @Override public Tab setText(CharSequence text) { mText = text; - if (mPosition >= 0) mActionView.updateTab(mPosition); + if (mPosition >= 0) { + mTabScrollView.updateTab(mPosition); + } return this; } @@ -818,15 +870,16 @@ public class ActionBarImpl extends ActionBar { mSavedTabPosition = getSelectedNavigationIndex(); selectTab(null); if (!mActionView.hasEmbeddedTabs()) { - mExternalTabView.setVisibility(View.GONE); + mTabScrollView.setVisibility(View.GONE); } break; } mActionView.setNavigationMode(mode); switch (mode) { case NAVIGATION_MODE_TABS: + ensureTabsExist(); if (!mActionView.hasEmbeddedTabs()) { - mExternalTabView.setVisibility(View.VISIBLE); + mTabScrollView.setVisibility(View.VISIBLE); } if (mSavedTabPosition != INVALID_POSITION) { setSelectedNavigationItem(mSavedTabPosition); diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index f1887eb..ff04735 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -78,7 +78,7 @@ public class ActionBarView extends AbsActionBarView { private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.LEFT | Gravity.CENTER_VERTICAL; - private final int mContentHeight; + private int mContentHeight; private int mNavigationMode; private int mDisplayOptions = ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP; @@ -95,8 +95,7 @@ public class ActionBarView extends AbsActionBarView { private TextView mSubtitleView; private Spinner mSpinner; private LinearLayout mListNavLayout; - private HorizontalScrollView mTabScrollView; - private ViewGroup mTabLayout; + private ScrollingTabContainerView mTabScrollView; private View mCustomNavView; private ProgressBar mProgressView; private ProgressBar mIndeterminateProgressView; @@ -122,6 +121,8 @@ public class ActionBarView extends AbsActionBarView { private SpinnerAdapter mSpinnerAdapter; private OnNavigationListener mCallback; + private Runnable mTabSelector; + private final AdapterView.OnItemSelectedListener mNavItemSelectedListener = new AdapterView.OnItemSelectedListener() { public void onItemSelected(AdapterView parent, View view, int position, long id) { @@ -199,8 +200,6 @@ public class ActionBarView extends AbsActionBarView { mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0); mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0); - mIncludeTabs = a.getBoolean(R.styleable.ActionBar_embeddedTabs, true); - setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT)); final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0); @@ -229,6 +228,12 @@ public class ActionBarView extends AbsActionBarView { } @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + removeCallbacks(mTabSelector); + } + + @Override public boolean shouldDelayChildPressedState() { return false; } @@ -247,6 +252,11 @@ public class ActionBarView extends AbsActionBarView { addView(mIndeterminateProgressView); } + public void setContentHeight(int height) { + mContentHeight = height; + requestLayout(); + } + public void setSplitActionBar(boolean splitActionBar) { if (mSplitActionBar != splitActionBar) { if (mMenuView != null) { @@ -271,8 +281,9 @@ public class ActionBarView extends AbsActionBarView { return mIncludeTabs; } - public void setExternalTabLayout(ViewGroup tabLayout) { - mTabLayout = tabLayout; + public void setEmbeddedTabView(ScrollingTabContainerView tabs) { + mTabScrollView = tabs; + mIncludeTabs = tabs != null; } public void setCallback(OnNavigationListener callback) { @@ -489,7 +500,7 @@ public class ActionBarView extends AbsActionBarView { } break; case ActionBar.NAVIGATION_MODE_TABS: - if (mTabScrollView != null) { + if (mTabScrollView != null && mIncludeTabs) { removeView(mTabScrollView); } } @@ -513,8 +524,7 @@ public class ActionBarView extends AbsActionBarView { addView(mListNavLayout); break; case ActionBar.NAVIGATION_MODE_TABS: - ensureTabsExist(); - if (mTabScrollView != null) { + if (mTabScrollView != null && mIncludeTabs) { addView(mTabScrollView); } break; @@ -523,24 +533,17 @@ public class ActionBarView extends AbsActionBarView { requestLayout(); } } - - private void ensureTabsExist() { - if (!mIncludeTabs) return; - - if (mTabScrollView == null) { - mTabScrollView = new HorizontalScrollView(getContext()); - mTabScrollView.setHorizontalFadingEdgeEnabled(true); - mTabLayout = createTabContainer(); - mTabScrollView.addView(mTabLayout); - } - } - public ViewGroup createTabContainer() { - ViewGroup result = new LinearLayout(getContext(), null, + public ScrollingTabContainerView createTabContainer() { + final LinearLayout tabLayout = new LinearLayout(getContext(), null, com.android.internal.R.attr.actionBarTabBarStyle); - result.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, - mContentHeight)); - return result; + 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) { @@ -574,51 +577,6 @@ public class ActionBarView extends AbsActionBarView { return mDisplayOptions; } - private TabView createTabView(ActionBar.Tab tab) { - final TabView tabView = new TabView(getContext(), tab); - tabView.setFocusable(true); - - if (mTabClickListener == null) { - mTabClickListener = new TabClickListener(); - } - tabView.setOnClickListener(mTabClickListener); - return tabView; - } - - public void addTab(ActionBar.Tab tab, boolean setSelected) { - ensureTabsExist(); - View tabView = createTabView(tab); - mTabLayout.addView(tabView); - if (setSelected) { - tabView.setSelected(true); - } - } - - public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { - ensureTabsExist(); - final TabView tabView = createTabView(tab); - mTabLayout.addView(tabView, position); - if (setSelected) { - tabView.setSelected(true); - } - } - - public void updateTab(int position) { - ((TabView) mTabLayout.getChildAt(position)).update(); - } - - public void removeTabAt(int position) { - if (mTabLayout != null) { - mTabLayout.removeViewAt(position); - } - } - - public void removeAllTabs() { - if (mTabLayout != null) { - mTabLayout.removeAllViews(); - } - } - @Override protected LayoutParams generateDefaultLayoutParams() { // Used by custom nav views if they don't supply layout params. Everything else @@ -667,15 +625,6 @@ public class ActionBarView extends AbsActionBarView { addView(mTitleLayout); } - public void setTabSelected(int position) { - ensureTabsExist(); - final int tabCount = mTabLayout.getChildCount(); - for (int i = 0; i < tabCount; i++) { - final View child = mTabLayout.getChildAt(i); - child.setSelected(i == position); - } - } - public void setContextView(ActionBarContextView view) { mContextView = view; } @@ -948,97 +897,6 @@ public class ActionBarView extends AbsActionBarView { } } - private static class TabView extends LinearLayout { - private ActionBar.Tab mTab; - private TextView mTextView; - private ImageView mIconView; - private View mCustomView; - - public TabView(Context context, ActionBar.Tab tab) { - super(context, null, com.android.internal.R.attr.actionBarTabStyle); - mTab = tab; - - update(); - - setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.MATCH_PARENT, 1)); - } - - public void update() { - final ActionBar.Tab tab = mTab; - final View custom = tab.getCustomView(); - if (custom != null) { - addView(custom); - mCustomView = custom; - if (mTextView != null) mTextView.setVisibility(GONE); - if (mIconView != null) { - mIconView.setVisibility(GONE); - mIconView.setImageDrawable(null); - } - } else { - if (mCustomView != null) { - removeView(mCustomView); - mCustomView = null; - } - - final Drawable icon = tab.getIcon(); - final CharSequence text = tab.getText(); - - if (icon != null) { - if (mIconView == null) { - ImageView iconView = new ImageView(getContext()); - LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER_VERTICAL; - iconView.setLayoutParams(lp); - addView(iconView, 0); - mIconView = iconView; - } - mIconView.setImageDrawable(icon); - mIconView.setVisibility(VISIBLE); - } else if (mIconView != null) { - mIconView.setVisibility(GONE); - mIconView.setImageDrawable(null); - } - - if (text != null) { - if (mTextView == null) { - TextView textView = new TextView(getContext(), null, - com.android.internal.R.attr.actionBarTabTextStyle); - textView.setSingleLine(); - textView.setEllipsize(TruncateAt.END); - LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER_VERTICAL; - textView.setLayoutParams(lp); - addView(textView); - mTextView = textView; - } - mTextView.setText(text); - mTextView.setVisibility(VISIBLE); - } else { - mTextView.setVisibility(GONE); - } - } - } - - public ActionBar.Tab getTab() { - return mTab; - } - } - - private class TabClickListener implements OnClickListener { - public void onClick(View view) { - TabView tabView = (TabView) view; - tabView.getTab().select(); - final int tabCount = mTabLayout.getChildCount(); - for (int i = 0; i < tabCount; i++) { - final View child = mTabLayout.getChildAt(i); - child.setSelected(child == view); - } - } - } - private static class HomeView extends FrameLayout { private View mUpView; private View mIconView; diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java new file mode 100644 index 0000000..c7d37f2 --- /dev/null +++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget; + +import android.app.ActionBar; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.TextUtils.TruncateAt; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class ScrollingTabContainerView extends HorizontalScrollView { + Runnable mTabSelector; + private TabClickListener mTabClickListener; + + private LinearLayout mTabLayout; + + int mMaxTabWidth; + + public ScrollingTabContainerView(Context context) { + super(context); + setHorizontalScrollBarEnabled(false); + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + setFillViewport(widthMode == MeasureSpec.EXACTLY); + + final int childCount = getChildCount(); + if (childCount > 1 && + (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) { + if (childCount > 2) { + mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f); + } else { + mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2; + } + } else { + mMaxTabWidth = -1; + } + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + public void setTabSelected(int position) { + if (mTabLayout == null) { + return; + } + + final int tabCount = mTabLayout.getChildCount(); + for (int i = 0; i < tabCount; i++) { + final View child = mTabLayout.getChildAt(i); + final boolean isSelected = i == position; + child.setSelected(isSelected); + if (isSelected) { + animateToTab(position); + } + } + } + + public void animateToTab(int position) { + final View tabView = mTabLayout.getChildAt(position); + if (mTabSelector != null) { + removeCallbacks(mTabSelector); + } + mTabSelector = new Runnable() { + public void run() { + final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2; + smoothScrollTo(scrollPos, 0); + mTabSelector = null; + } + }; + 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; + } + } + + public LinearLayout getTabLayout() { + return mTabLayout; + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mTabSelector != null) { + removeCallbacks(mTabSelector); + } + } + + private TabView createTabView(ActionBar.Tab tab) { + final TabView tabView = new TabView(getContext(), tab); + tabView.setFocusable(true); + + if (mTabClickListener == null) { + mTabClickListener = new TabClickListener(); + } + tabView.setOnClickListener(mTabClickListener); + return tabView; + } + + public void addTab(ActionBar.Tab tab, boolean setSelected) { + View tabView = createTabView(tab); + mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, + LayoutParams.MATCH_PARENT, 1)); + if (setSelected) { + tabView.setSelected(true); + } + } + + public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { + final TabView tabView = createTabView(tab); + mTabLayout.addView(tabView, position, new LinearLayout.LayoutParams( + 0, LayoutParams.MATCH_PARENT, 1)); + if (setSelected) { + tabView.setSelected(true); + } + } + + public void updateTab(int position) { + ((TabView) mTabLayout.getChildAt(position)).update(); + } + + public void removeTabAt(int position) { + if (mTabLayout != null) { + mTabLayout.removeViewAt(position); + } + } + + public void removeAllTabs() { + if (mTabLayout != null) { + mTabLayout.removeAllViews(); + } + } + + 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) { + super(context, null, com.android.internal.R.attr.actionBarTabStyle); + mTab = tab; + + update(); + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + // Re-measure if we went beyond our maximum size. + if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) { + super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY), + heightMeasureSpec); + } + } + + public void update() { + final ActionBar.Tab tab = mTab; + final View custom = tab.getCustomView(); + if (custom != null) { + addView(custom); + mCustomView = custom; + if (mTextView != null) mTextView.setVisibility(GONE); + if (mIconView != null) { + mIconView.setVisibility(GONE); + mIconView.setImageDrawable(null); + } + } else { + if (mCustomView != null) { + removeView(mCustomView); + mCustomView = null; + } + + final Drawable icon = tab.getIcon(); + final CharSequence text = tab.getText(); + + if (icon != null) { + if (mIconView == null) { + ImageView iconView = new ImageView(getContext()); + LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER_VERTICAL; + iconView.setLayoutParams(lp); + addView(iconView, 0); + mIconView = iconView; + } + mIconView.setImageDrawable(icon); + mIconView.setVisibility(VISIBLE); + } else if (mIconView != null) { + mIconView.setVisibility(GONE); + mIconView.setImageDrawable(null); + } + + if (text != null) { + if (mTextView == null) { + TextView textView = new TextView(getContext(), null, + com.android.internal.R.attr.actionBarTabTextStyle); + textView.setSingleLine(); + textView.setEllipsize(TruncateAt.END); + LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER_VERTICAL; + textView.setLayoutParams(lp); + addView(textView); + mTextView = textView; + } + mTextView.setText(text); + mTextView.setVisibility(VISIBLE); + } else { + mTextView.setVisibility(GONE); + } + } + } + + public ActionBar.Tab getTab() { + return mTab; + } + } + + private class TabClickListener implements OnClickListener { + public void onClick(View view) { + TabView tabView = (TabView) view; + tabView.getTab().select(); + final int tabCount = mTabLayout.getChildCount(); + for (int i = 0; i < tabCount; i++) { + final View child = mTabLayout.getChildAt(i); + child.setSelected(child == view); + } + } + } +} |
