aboutsummaryrefslogtreecommitdiffstats
path: root/com_actionbarsherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java
diff options
context:
space:
mode:
Diffstat (limited to 'com_actionbarsherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java')
-rw-r--r--com_actionbarsherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java545
1 files changed, 545 insertions, 0 deletions
diff --git a/com_actionbarsherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java b/com_actionbarsherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java
new file mode 100644
index 000000000..82b72d8e2
--- /dev/null
+++ b/com_actionbarsherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java
@@ -0,0 +1,545 @@
+/*
+ * 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.actionbarsherlock.internal.widget;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils.TruncateAt;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.widget.BaseAdapter;
+import android.widget.HorizontalScrollView;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.internal.nineoldandroids.animation.Animator;
+import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator;
+
+/**
+ * This widget implements the dynamic action bar tab behavior that can change
+ * across different configurations or circumstances.
+ */
+public class ScrollingTabContainerView extends HorizontalScrollView
+ implements IcsAdapterView.OnItemSelectedListener {
+ //UNUSED private static final String TAG = "ScrollingTabContainerView";
+ Runnable mTabSelector;
+ private TabClickListener mTabClickListener;
+
+ private IcsLinearLayout mTabLayout;
+ private IcsSpinner mTabSpinner;
+ private boolean mAllowCollapse;
+
+ private LayoutInflater mInflater;
+
+ int mMaxTabWidth;
+ private int mContentHeight;
+ private int mSelectedTabIndex;
+
+ protected Animator mVisibilityAnim;
+ protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
+
+ private static final /*Time*/Interpolator sAlphaInterpolator = new DecelerateInterpolator();
+
+ private static final int FADE_DURATION = 200;
+
+ public ScrollingTabContainerView(Context context) {
+ super(context);
+ setHorizontalScrollBarEnabled(false);
+
+ TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar,
+ R.attr.actionBarStyle, 0);
+ setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0));
+ a.recycle();
+
+ mInflater = LayoutInflater.from(context);
+
+ 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);
+ final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
+ setFillViewport(lockedExpanded);
+
+ final int childCount = mTabLayout.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;
+ }
+
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY);
+
+ 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);
+ }
+ }
+
+ /**
+ * 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);
+ final boolean isSelected = i == position;
+ child.setSelected(isSelected);
+ if (isSelected) {
+ animateToTab(position);
+ }
+ }
+ }
+
+ public void setContentHeight(int contentHeight) {
+ mContentHeight = contentHeight;
+ requestLayout();
+ }
+
+ private IcsLinearLayout createTabLayout() {
+ final IcsLinearLayout tabLayout = (IcsLinearLayout) LayoutInflater.from(getContext())
+ .inflate(R.layout.abs__action_bar_tab_bar_view, null);
+ tabLayout.setLayoutParams(new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
+ return tabLayout;
+ }
+
+ private IcsSpinner createSpinner() {
+ final IcsSpinner spinner = new IcsSpinner(getContext(), null,
+ 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);
+
+ // Action bar can change size on configuration changes.
+ // Reread the desired height from the theme-specified style.
+ TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar,
+ R.attr.actionBarStyle, 0);
+ setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0));
+ a.recycle();
+ }
+
+ public void animateToVisibility(int visibility) {
+ if (mVisibilityAnim != null) {
+ mVisibilityAnim.cancel();
+ }
+ if (visibility == VISIBLE) {
+ if (getVisibility() != VISIBLE) {
+ setAlpha(0);
+ }
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
+ anim.setDuration(FADE_DURATION);
+ anim.setInterpolator(sAlphaInterpolator);
+
+ anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ anim.start();
+ } else {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
+ anim.setDuration(FADE_DURATION);
+ anim.setInterpolator(sAlphaInterpolator);
+
+ anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+ anim.start();
+ }
+ }
+
+ public void animateToTab(final 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);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mTabSelector != null) {
+ // Re-post the selector we saved
+ post(mTabSelector);
+ }
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mTabSelector != null) {
+ removeCallbacks(mTabSelector);
+ }
+ }
+
+ private TabView createTabView(ActionBar.Tab tab, boolean forAdapter) {
+ //Workaround for not being able to pass a defStyle on pre-3.0
+ final TabView tabView = (TabView)mInflater.inflate(R.layout.abs__action_bar_tab, null);
+ tabView.init(this, 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();
+ }
+ tabView.setOnClickListener(mTabClickListener);
+ }
+ return tabView;
+ }
+
+ public void addTab(ActionBar.Tab tab, boolean setSelected) {
+ TabView tabView = createTabView(tab, false);
+ mTabLayout.addView(tabView, new IcsLinearLayout.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, false);
+ mTabLayout.addView(tabView, position, new IcsLinearLayout.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) {
+ mTabLayout.removeViewAt(position);
+ if (mTabSpinner != null) {
+ ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
+ }
+ if (mAllowCollapse) {
+ requestLayout();
+ }
+ }
+
+ public void removeAllTabs() {
+ mTabLayout.removeAllViews();
+ if (mTabSpinner != null) {
+ ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
+ }
+ if (mAllowCollapse) {
+ requestLayout();
+ }
+ }
+
+ @Override
+ public void onItemSelected(IcsAdapterView<?> parent, View view, int position, long id) {
+ TabView tabView = (TabView) view;
+ tabView.getTab().select();
+ }
+
+ @Override
+ public void onNothingSelected(IcsAdapterView<?> parent) {
+ }
+
+ public static class TabView extends LinearLayout {
+ private ScrollingTabContainerView mParent;
+ private ActionBar.Tab mTab;
+ private CapitalizingTextView mTextView;
+ private ImageView mIconView;
+ private View mCustomView;
+
+ public TabView(Context context, AttributeSet attrs) {
+ //TODO super(context, null, R.attr.actionBarTabStyle);
+ super(context, attrs);
+ }
+
+ public void init(ScrollingTabContainerView parent, ActionBar.Tab tab, boolean forList) {
+ mParent = parent;
+ mTab = tab;
+
+ if (forList) {
+ setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
+ }
+
+ update();
+ }
+
+ public void bindTab(ActionBar.Tab tab) {
+ 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 (mParent.mMaxTabWidth > 0 && getMeasuredWidth() > mParent.mMaxTabWidth) {
+ super.onMeasure(MeasureSpec.makeMeasureSpec(mParent.mMaxTabWidth, MeasureSpec.EXACTLY),
+ heightMeasureSpec);
+ }
+ }
+
+ public void update() {
+ final ActionBar.Tab tab = mTab;
+ final View custom = tab.getCustomView();
+ if (custom != null) {
+ final ViewParent customParent = custom.getParent();
+ if (customParent != this) {
+ if (customParent != null) ((ViewGroup) customParent).removeView(custom);
+ 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) {
+ CapitalizingTextView textView = new CapitalizingTextView(getContext(), null,
+ R.attr.actionBarTabTextStyle);
+ 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.setTextCompat(text);
+ mTextView.setVisibility(VISIBLE);
+ } else if (mTextView != null) {
+ mTextView.setVisibility(GONE);
+ mTextView.setText(null);
+ }
+
+ if (mIconView != null) {
+ mIconView.setContentDescription(tab.getContentDescription());
+ }
+ }
+ }
+
+ public ActionBar.Tab getTab() {
+ return mTab;
+ }
+ }
+
+ 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;
+ 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);
+ }
+ }
+ }
+
+ protected class VisibilityAnimListener implements Animator.AnimatorListener {
+ private boolean mCanceled = false;
+ private int mFinalVisibility;
+
+ public VisibilityAnimListener withFinalVisibility(int visibility) {
+ mFinalVisibility = visibility;
+ return this;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ setVisibility(VISIBLE);
+ mVisibilityAnim = animation;
+ mCanceled = false;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCanceled) return;
+
+ mVisibilityAnim = null;
+ setVisibility(mFinalVisibility);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCanceled = true;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ }
+}