ScrollingTabContainerView.java revision 45c0b1954d7dfa6e2590ed76b915a98ae971414c
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.internal.widget;
17
18import android.animation.Animator;
19import android.animation.ObjectAnimator;
20import android.animation.TimeInterpolator;
21import android.app.ActionBar;
22import android.content.Context;
23import android.graphics.drawable.Drawable;
24import android.text.TextUtils.TruncateAt;
25import android.view.Gravity;
26import android.view.View;
27import android.view.ViewGroup;
28import android.view.animation.DecelerateInterpolator;
29import android.widget.HorizontalScrollView;
30import android.widget.ImageView;
31import android.widget.LinearLayout;
32import android.widget.TextView;
33
34public class ScrollingTabContainerView extends HorizontalScrollView {
35    Runnable mTabSelector;
36    private TabClickListener mTabClickListener;
37
38    private LinearLayout mTabLayout;
39
40    int mMaxTabWidth;
41
42    protected Animator mVisibilityAnim;
43    protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
44
45    private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator();
46
47    private static final int FADE_DURATION = 200;
48
49    public ScrollingTabContainerView(Context context) {
50        super(context);
51        setHorizontalScrollBarEnabled(false);
52    }
53
54    @Override
55    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
56        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
57        setFillViewport(widthMode == MeasureSpec.EXACTLY);
58
59        final int childCount = getChildCount();
60        if (childCount > 1 &&
61                (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
62            if (childCount > 2) {
63                mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f);
64            } else {
65                mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;
66            }
67        } else {
68            mMaxTabWidth = -1;
69        }
70
71        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
72    }
73
74    public void setTabSelected(int position) {
75        if (mTabLayout == null) {
76            return;
77        }
78
79        final int tabCount = mTabLayout.getChildCount();
80        for (int i = 0; i < tabCount; i++) {
81            final View child = mTabLayout.getChildAt(i);
82            final boolean isSelected = i == position;
83            child.setSelected(isSelected);
84            if (isSelected) {
85                animateToTab(position);
86            }
87        }
88    }
89
90    public void setContentHeight(int contentHeight) {
91        mTabLayout.getLayoutParams().height = contentHeight;
92        requestLayout();
93    }
94
95    public void animateToVisibility(int visibility) {
96        if (mVisibilityAnim != null) {
97            mVisibilityAnim.cancel();
98        }
99        if (visibility == VISIBLE) {
100            if (getVisibility() != VISIBLE) {
101                setAlpha(0);
102            }
103            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
104            anim.setDuration(FADE_DURATION);
105            anim.setInterpolator(sAlphaInterpolator);
106
107            anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
108            anim.start();
109        } else {
110            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
111            anim.setDuration(FADE_DURATION);
112            anim.setInterpolator(sAlphaInterpolator);
113
114            anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
115            anim.start();
116        }
117    }
118
119    public void animateToTab(int position) {
120        final View tabView = mTabLayout.getChildAt(position);
121        if (mTabSelector != null) {
122            removeCallbacks(mTabSelector);
123        }
124        mTabSelector = new Runnable() {
125            public void run() {
126                final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
127                smoothScrollTo(scrollPos, 0);
128                mTabSelector = null;
129            }
130        };
131        post(mTabSelector);
132    }
133
134    public void setTabLayout(LinearLayout tabLayout) {
135        if (mTabLayout != tabLayout) {
136            if (mTabLayout != null) {
137                ((ViewGroup) mTabLayout.getParent()).removeView(mTabLayout);
138            }
139            if (tabLayout != null) {
140                addView(tabLayout);
141            }
142            mTabLayout = tabLayout;
143        }
144    }
145
146    public LinearLayout getTabLayout() {
147        return mTabLayout;
148    }
149
150    @Override
151    public void onDetachedFromWindow() {
152        super.onDetachedFromWindow();
153        if (mTabSelector != null) {
154            removeCallbacks(mTabSelector);
155        }
156    }
157
158    private TabView createTabView(ActionBar.Tab tab) {
159        final TabView tabView = new TabView(getContext(), tab);
160        tabView.setFocusable(true);
161
162        if (mTabClickListener == null) {
163            mTabClickListener = new TabClickListener();
164        }
165        tabView.setOnClickListener(mTabClickListener);
166        return tabView;
167    }
168
169    public void addTab(ActionBar.Tab tab, boolean setSelected) {
170        View tabView = createTabView(tab);
171        mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0,
172                LayoutParams.MATCH_PARENT, 1));
173        if (setSelected) {
174            tabView.setSelected(true);
175        }
176    }
177
178    public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
179        final TabView tabView = createTabView(tab);
180        mTabLayout.addView(tabView, position, new LinearLayout.LayoutParams(
181                0, LayoutParams.MATCH_PARENT, 1));
182        if (setSelected) {
183            tabView.setSelected(true);
184        }
185    }
186
187    public void updateTab(int position) {
188        ((TabView) mTabLayout.getChildAt(position)).update();
189    }
190
191    public void removeTabAt(int position) {
192        if (mTabLayout != null) {
193            mTabLayout.removeViewAt(position);
194        }
195    }
196
197    public void removeAllTabs() {
198        if (mTabLayout != null) {
199            mTabLayout.removeAllViews();
200        }
201    }
202
203    private class TabView extends LinearLayout {
204        private ActionBar.Tab mTab;
205        private TextView mTextView;
206        private ImageView mIconView;
207        private View mCustomView;
208
209        public TabView(Context context, ActionBar.Tab tab) {
210            super(context, null, com.android.internal.R.attr.actionBarTabStyle);
211            mTab = tab;
212
213            update();
214        }
215
216        @Override
217        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
218            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
219
220            // Re-measure if we went beyond our maximum size.
221            if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) {
222                super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY),
223                        heightMeasureSpec);
224            }
225        }
226
227        public void update() {
228            final ActionBar.Tab tab = mTab;
229            final View custom = tab.getCustomView();
230            if (custom != null) {
231                addView(custom);
232                mCustomView = custom;
233                if (mTextView != null) mTextView.setVisibility(GONE);
234                if (mIconView != null) {
235                    mIconView.setVisibility(GONE);
236                    mIconView.setImageDrawable(null);
237                }
238            } else {
239                if (mCustomView != null) {
240                    removeView(mCustomView);
241                    mCustomView = null;
242                }
243
244                final Drawable icon = tab.getIcon();
245                final CharSequence text = tab.getText();
246
247                if (icon != null) {
248                    if (mIconView == null) {
249                        ImageView iconView = new ImageView(getContext());
250                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
251                                LayoutParams.WRAP_CONTENT);
252                        lp.gravity = Gravity.CENTER_VERTICAL;
253                        iconView.setLayoutParams(lp);
254                        addView(iconView, 0);
255                        mIconView = iconView;
256                    }
257                    mIconView.setImageDrawable(icon);
258                    mIconView.setVisibility(VISIBLE);
259                } else if (mIconView != null) {
260                    mIconView.setVisibility(GONE);
261                    mIconView.setImageDrawable(null);
262                }
263
264                if (text != null) {
265                    if (mTextView == null) {
266                        TextView textView = new TextView(getContext(), null,
267                                com.android.internal.R.attr.actionBarTabTextStyle);
268                        textView.setEllipsize(TruncateAt.END);
269                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
270                                LayoutParams.WRAP_CONTENT);
271                        lp.gravity = Gravity.CENTER_VERTICAL;
272                        textView.setLayoutParams(lp);
273                        addView(textView);
274                        mTextView = textView;
275                    }
276                    mTextView.setText(text);
277                    mTextView.setVisibility(VISIBLE);
278                } else if (mTextView != null) {
279                    mTextView.setVisibility(GONE);
280                    mTextView.setText(null);
281                }
282            }
283        }
284
285        public ActionBar.Tab getTab() {
286            return mTab;
287        }
288    }
289
290    private class TabClickListener implements OnClickListener {
291        public void onClick(View view) {
292            TabView tabView = (TabView) view;
293            tabView.getTab().select();
294            final int tabCount = mTabLayout.getChildCount();
295            for (int i = 0; i < tabCount; i++) {
296                final View child = mTabLayout.getChildAt(i);
297                child.setSelected(child == view);
298            }
299        }
300    }
301
302    protected class VisibilityAnimListener implements Animator.AnimatorListener {
303        private boolean mCanceled = false;
304        private int mFinalVisibility;
305
306        public VisibilityAnimListener withFinalVisibility(int visibility) {
307            mFinalVisibility = visibility;
308            return this;
309        }
310
311        @Override
312        public void onAnimationStart(Animator animation) {
313            setVisibility(VISIBLE);
314            mVisibilityAnim = animation;
315            mCanceled = false;
316        }
317
318        @Override
319        public void onAnimationEnd(Animator animation) {
320            if (mCanceled) return;
321
322            mVisibilityAnim = null;
323            setVisibility(mFinalVisibility);
324        }
325
326        @Override
327        public void onAnimationCancel(Animator animation) {
328            mCanceled = true;
329        }
330
331        @Override
332        public void onAnimationRepeat(Animator animation) {
333        }
334    }
335}
336