Project: cow
Code Examples
/*
 * 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.animation.DecelerateInterpolator; 
import android.view.animation.Interpolator; 
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; 
 
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 AdapterView.OnItemSelectedListener { 
    //UNUSED private static final String TAG = "ScrollingTabContainerView"; 
    Runnable mTabSelector; 
    private TabClickListener mTabClickListener; 
 
    private IcsLinearLayout mTabLayout; 
    private Spinner 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.FILL_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.FILL_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.FILL_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 = new IcsLinearLayout(getContext(), null, R.attr.actionBarTabBarStyle); 
        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
                R.attr.actionDropDownStyle); 
        spinner.setLayoutParams(new LinearLayout.LayoutParams( 
                LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.FILL_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.FILL_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.FILL_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.FILL_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(AdapterView<?> parent, View view, int position, long id) { 
        TabView tabView = (TabView) view; 
        tabView.getTab().select(); 
    } 
 
    @Override 
    public void onNothingSelected(AdapterView<?> parent) { 
    } 
 
    public static class TabView extends LinearLayout { 
        private ScrollingTabContainerView mParent; 
        private ActionBar.Tab mTab; 
        private TextView 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) { 
                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
                                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.setText(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) { 
        } 
    } 
}