TabScrollView.java revision 2b5a13ab55991f55ab7afb107e4401c6fbb5ad64
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.android.browser;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
22import android.content.Context;
23import android.graphics.Canvas;
24import android.graphics.drawable.Drawable;
25import android.util.AttributeSet;
26import android.view.View;
27import android.view.animation.AccelerateInterpolator;
28import android.widget.HorizontalScrollView;
29import android.widget.LinearLayout;
30
31/**
32 * custom view for displaying tabs in the tabbed title bar
33 */
34public class TabScrollView extends HorizontalScrollView {
35
36    private Context mContext;
37    private LinearLayout mContentView;
38    private int mSelected;
39    private Drawable mArrowLeft;
40    private Drawable mArrowRight;
41    private int mAnimationDuration;
42    private int mTabOverlap;
43
44    /**
45     * @param context
46     * @param attrs
47     * @param defStyle
48     */
49    public TabScrollView(Context context, AttributeSet attrs, int defStyle) {
50        super(context, attrs, defStyle);
51        init(context);
52    }
53
54    /**
55     * @param context
56     * @param attrs
57     */
58    public TabScrollView(Context context, AttributeSet attrs) {
59        super(context, attrs);
60        init(context);
61    }
62
63    /**
64     * @param context
65     */
66    public TabScrollView(Context context) {
67        super(context);
68        init(context);
69    }
70
71    private void init(Context ctx) {
72        mContext = ctx;
73        mAnimationDuration = ctx.getResources().getInteger(
74                R.integer.tab_animation_duration);
75        mTabOverlap = (int) ctx.getResources().getDimension(R.dimen.tab_overlap);
76        setHorizontalScrollBarEnabled(false);
77        setOverScrollMode(OVER_SCROLL_NEVER);
78        mContentView = new TabLayout(mContext);
79        mContentView.setOrientation(LinearLayout.HORIZONTAL);
80        mContentView.setLayoutParams(
81                new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
82        addView(mContentView);
83        mSelected = -1;
84        mArrowLeft = ctx.getResources().getDrawable(R.drawable.ic_arrow_left);
85        mArrowRight = ctx.getResources().getDrawable(R.drawable.ic_arrow_right);
86        // prevent ProGuard from removing the property methods
87        setScroll(getScroll());
88    }
89
90    @Override
91    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
92        super.onLayout(changed, left, top, right, bottom);
93        ensureChildVisible(getSelectedTab());
94    }
95
96    void setSelectedTab(int position) {
97        View v = getSelectedTab();
98        if (v != null) {
99            v.setActivated(false);
100        }
101        mSelected = position;
102        v = getSelectedTab();
103        if (v != null) {
104            v.setActivated(true);
105        }
106        requestLayout();
107    }
108
109    int getChildIndex(View v) {
110        return mContentView.indexOfChild(v);
111    }
112
113    View getSelectedTab() {
114        if ((mSelected >= 0) && (mSelected < mContentView.getChildCount())) {
115            return mContentView.getChildAt(mSelected);
116        } else {
117            return null;
118        }
119    }
120
121    void clearTabs() {
122        mContentView.removeAllViews();
123    }
124
125    void addTab(View tab) {
126        mContentView.addView(tab);
127        animateIn(tab);
128        tab.setActivated(false);
129    }
130
131    void removeTab(View tab) {
132        int ix = mContentView.indexOfChild(tab);
133        if (ix == mSelected) {
134            mSelected = -1;
135        } else if (ix < mSelected) {
136            mSelected--;
137        }
138        animateOut(tab);
139    }
140
141    private void ensureChildVisible(View child) {
142        if (child != null) {
143            int childl = child.getLeft();
144            int childr = childl + child.getWidth();
145            int viewl = getScrollX();
146            int viewr = viewl + getWidth();
147            if (childl < viewl) {
148                // need scrolling to left
149                animateScroll(childl);
150            } else if (childr > viewr) {
151                // need scrolling to right
152                animateScroll(childr - viewr + viewl);
153            }
154        }
155    }
156
157    @Override
158    protected void dispatchDraw(Canvas canvas) {
159        super.dispatchDraw(canvas);
160        int l = getScrollX();
161        int r = l + getWidth();
162        int dis = 8;
163        if (l > 0) {
164            int aw = mArrowLeft.getIntrinsicWidth();
165            mArrowLeft.setBounds(l + dis, 0, l + dis + aw, getHeight());
166            mArrowLeft.draw(canvas);
167        }
168        if (r < mContentView.getWidth()) {
169            int aw = mArrowRight.getIntrinsicWidth();
170            mArrowRight.setBounds(r - dis - aw, 0, r - dis, getHeight());
171            mArrowRight.draw(canvas);
172        }
173    }
174
175    private void animateIn(View tab) {
176        ObjectAnimator animator = ObjectAnimator.ofInt(tab, "TranslationX", 500, 0);
177        animator.setDuration(mAnimationDuration);
178        animator.start();
179    }
180
181    private void animateOut(final View tab) {
182        ObjectAnimator animator = ObjectAnimator.ofInt(
183                tab, "TranslationX", 0, getScrollX() - tab.getRight());
184        animator.setDuration(mAnimationDuration);
185        animator.addListener(new AnimatorListenerAdapter() {
186            @Override
187            public void onAnimationEnd(Animator animation) {
188                mContentView.removeView(tab);
189            }
190        });
191        animator.setInterpolator(new AccelerateInterpolator());
192        animator.start();
193    }
194
195    private void animateScroll(int newscroll) {
196        ObjectAnimator animator = ObjectAnimator.ofInt(this, "scroll", getScrollX(), newscroll);
197        animator.setDuration(mAnimationDuration);
198        animator.start();
199    }
200
201    /**
202     * required for animation
203     */
204    public void setScroll(int newscroll) {
205        scrollTo(newscroll, getScrollY());
206    }
207
208    /**
209     * required for animation
210     */
211    public int getScroll() {
212        return getScrollX();
213    }
214
215    class TabLayout extends LinearLayout {
216
217        public TabLayout(Context context) {
218            super(context);
219            setChildrenDrawingOrderEnabled(true);
220        }
221
222        @Override
223        protected void onMeasure(int hspec, int vspec) {
224            super.onMeasure(hspec, vspec);
225            int w = getMeasuredWidth();
226            w -= Math.max(0, mContentView.getChildCount() - 1) * mTabOverlap;
227            setMeasuredDimension(w, getMeasuredHeight());
228        }
229
230        @Override
231        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
232            super.onLayout(changed, left, top, right, bottom);
233            if (getChildCount() > 1) {
234                int nextLeft = getChildAt(0).getRight() - mTabOverlap;
235                for (int i = 1; i < getChildCount(); i++) {
236                    View tab = getChildAt(i);
237                    int w = tab.getRight() - tab.getLeft();
238                    tab.layout(nextLeft, tab.getTop(), nextLeft + w, tab.getBottom());
239                    nextLeft += w - mTabOverlap;
240                }
241            }
242        }
243
244        @Override
245        protected int getChildDrawingOrder(int count, int i) {
246            int next = -1;
247            if ((i == (count - 1)) && (mSelected >= 0)) {
248                next = mSelected;
249            } else {
250                next = count - i - 1;
251                if (next <= mSelected) {
252                    next--;
253                }
254            }
255            return next;
256        }
257
258    }
259
260}
261