ScrollingTabContainerView.java revision 7f8f79a1ff086c04a3ad2a442b1d39a8186e3e50
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 animateToVisibility(int visibility) {
91        if (mVisibilityAnim != null) {
92            mVisibilityAnim.cancel();
93        }
94        if (visibility == VISIBLE) {
95            if (getVisibility() != VISIBLE) {
96                setAlpha(0);
97            }
98            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
99            anim.setDuration(FADE_DURATION);
100            anim.setInterpolator(sAlphaInterpolator);
101
102            anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
103            anim.start();
104        } else {
105            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
106            anim.setDuration(FADE_DURATION);
107            anim.setInterpolator(sAlphaInterpolator);
108
109            anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
110            anim.start();
111        }
112    }
113
114    public void animateToTab(int position) {
115        final View tabView = mTabLayout.getChildAt(position);
116        if (mTabSelector != null) {
117            removeCallbacks(mTabSelector);
118        }
119        mTabSelector = new Runnable() {
120            public void run() {
121                final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
122                smoothScrollTo(scrollPos, 0);
123                mTabSelector = null;
124            }
125        };
126        post(mTabSelector);
127    }
128
129    public void setTabLayout(LinearLayout tabLayout) {
130        if (mTabLayout != tabLayout) {
131            if (mTabLayout != null) {
132                ((ViewGroup) mTabLayout.getParent()).removeView(mTabLayout);
133            }
134            if (tabLayout != null) {
135                addView(tabLayout);
136            }
137            mTabLayout = tabLayout;
138        }
139    }
140
141    public LinearLayout getTabLayout() {
142        return mTabLayout;
143    }
144
145    @Override
146    public void onDetachedFromWindow() {
147        super.onDetachedFromWindow();
148        if (mTabSelector != null) {
149            removeCallbacks(mTabSelector);
150        }
151    }
152
153    private TabView createTabView(ActionBar.Tab tab) {
154        final TabView tabView = new TabView(getContext(), tab);
155        tabView.setFocusable(true);
156
157        if (mTabClickListener == null) {
158            mTabClickListener = new TabClickListener();
159        }
160        tabView.setOnClickListener(mTabClickListener);
161        return tabView;
162    }
163
164    public void addTab(ActionBar.Tab tab, boolean setSelected) {
165        View tabView = createTabView(tab);
166        mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0,
167                LayoutParams.MATCH_PARENT, 1));
168        if (setSelected) {
169            tabView.setSelected(true);
170        }
171    }
172
173    public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
174        final TabView tabView = createTabView(tab);
175        mTabLayout.addView(tabView, position, new LinearLayout.LayoutParams(
176                0, LayoutParams.MATCH_PARENT, 1));
177        if (setSelected) {
178            tabView.setSelected(true);
179        }
180    }
181
182    public void updateTab(int position) {
183        ((TabView) mTabLayout.getChildAt(position)).update();
184    }
185
186    public void removeTabAt(int position) {
187        if (mTabLayout != null) {
188            mTabLayout.removeViewAt(position);
189        }
190    }
191
192    public void removeAllTabs() {
193        if (mTabLayout != null) {
194            mTabLayout.removeAllViews();
195        }
196    }
197
198    private class TabView extends LinearLayout {
199        private ActionBar.Tab mTab;
200        private TextView mTextView;
201        private ImageView mIconView;
202        private View mCustomView;
203
204        public TabView(Context context, ActionBar.Tab tab) {
205            super(context, null, com.android.internal.R.attr.actionBarTabStyle);
206            mTab = tab;
207
208            update();
209        }
210
211        @Override
212        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
213            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
214
215            // Re-measure if we went beyond our maximum size.
216            if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) {
217                super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY),
218                        heightMeasureSpec);
219            }
220        }
221
222        public void update() {
223            final ActionBar.Tab tab = mTab;
224            final View custom = tab.getCustomView();
225            if (custom != null) {
226                addView(custom);
227                mCustomView = custom;
228                if (mTextView != null) mTextView.setVisibility(GONE);
229                if (mIconView != null) {
230                    mIconView.setVisibility(GONE);
231                    mIconView.setImageDrawable(null);
232                }
233            } else {
234                if (mCustomView != null) {
235                    removeView(mCustomView);
236                    mCustomView = null;
237                }
238
239                final Drawable icon = tab.getIcon();
240                final CharSequence text = tab.getText();
241
242                if (icon != null) {
243                    if (mIconView == null) {
244                        ImageView iconView = new ImageView(getContext());
245                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
246                                LayoutParams.WRAP_CONTENT);
247                        lp.gravity = Gravity.CENTER_VERTICAL;
248                        iconView.setLayoutParams(lp);
249                        addView(iconView, 0);
250                        mIconView = iconView;
251                    }
252                    mIconView.setImageDrawable(icon);
253                    mIconView.setVisibility(VISIBLE);
254                } else if (mIconView != null) {
255                    mIconView.setVisibility(GONE);
256                    mIconView.setImageDrawable(null);
257                }
258
259                if (text != null) {
260                    if (mTextView == null) {
261                        TextView textView = new TextView(getContext(), null,
262                                com.android.internal.R.attr.actionBarTabTextStyle);
263                        textView.setEllipsize(TruncateAt.END);
264                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
265                                LayoutParams.WRAP_CONTENT);
266                        lp.gravity = Gravity.CENTER_VERTICAL;
267                        textView.setLayoutParams(lp);
268                        addView(textView);
269                        mTextView = textView;
270                    }
271                    mTextView.setText(text);
272                    mTextView.setVisibility(VISIBLE);
273                } else if (mTextView != null) {
274                    mTextView.setVisibility(GONE);
275                    mTextView.setText(null);
276                }
277            }
278        }
279
280        public ActionBar.Tab getTab() {
281            return mTab;
282        }
283    }
284
285    private class TabClickListener implements OnClickListener {
286        public void onClick(View view) {
287            TabView tabView = (TabView) view;
288            tabView.getTab().select();
289            final int tabCount = mTabLayout.getChildCount();
290            for (int i = 0; i < tabCount; i++) {
291                final View child = mTabLayout.getChildAt(i);
292                child.setSelected(child == view);
293            }
294        }
295    }
296
297    protected class VisibilityAnimListener implements Animator.AnimatorListener {
298        private boolean mCanceled = false;
299        private int mFinalVisibility;
300
301        public VisibilityAnimListener withFinalVisibility(int visibility) {
302            mFinalVisibility = visibility;
303            return this;
304        }
305
306        @Override
307        public void onAnimationStart(Animator animation) {
308            setVisibility(VISIBLE);
309            mVisibilityAnim = animation;
310            mCanceled = false;
311        }
312
313        @Override
314        public void onAnimationEnd(Animator animation) {
315            if (mCanceled) return;
316
317            mVisibilityAnim = null;
318            setVisibility(mFinalVisibility);
319        }
320
321        @Override
322        public void onAnimationCancel(Animator animation) {
323            mCanceled = true;
324        }
325
326        @Override
327        public void onAnimationRepeat(Animator animation) {
328        }
329    }
330}
331