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 
17 package com.android.browser;
18 
19 import com.android.browser.TabBar.TabView;
20 
21 import android.animation.ObjectAnimator;
22 import android.content.Context;
23 import android.util.AttributeSet;
24 import android.view.View;
25 import android.widget.HorizontalScrollView;
26 import android.widget.LinearLayout;
27 
28 /**
29  * custom view for displaying tabs in the tabbed title bar
30  */
31 public class TabScrollView extends HorizontalScrollView {
32 
33     private LinearLayout mContentView;
34     private int mSelected;
35     private int mAnimationDuration;
36     private int mTabOverlap;
37 
38     /**
39      * @param context
40      * @param attrs
41      * @param defStyle
42      */
TabScrollView(Context context, AttributeSet attrs, int defStyle)43     public TabScrollView(Context context, AttributeSet attrs, int defStyle) {
44         super(context, attrs, defStyle);
45         init(context);
46     }
47 
48     /**
49      * @param context
50      * @param attrs
51      */
TabScrollView(Context context, AttributeSet attrs)52     public TabScrollView(Context context, AttributeSet attrs) {
53         super(context, attrs);
54         init(context);
55     }
56 
57     /**
58      * @param context
59      */
TabScrollView(Context context)60     public TabScrollView(Context context) {
61         super(context);
62         init(context);
63     }
64 
init(Context ctx)65     private void init(Context ctx) {
66         mAnimationDuration = ctx.getResources().getInteger(
67                 R.integer.tab_animation_duration);
68         mTabOverlap = (int) ctx.getResources().getDimension(R.dimen.tab_overlap);
69         setHorizontalScrollBarEnabled(false);
70         setOverScrollMode(OVER_SCROLL_NEVER);
71         mContentView = new TabLayout(ctx);
72         mContentView.setOrientation(LinearLayout.HORIZONTAL);
73         mContentView.setLayoutParams(
74                 new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
75         mContentView.setPadding(
76                 (int) ctx.getResources().getDimension(R.dimen.tab_first_padding_left),
77                 0, 0, 0);
78         addView(mContentView);
79         mSelected = -1;
80         // prevent ProGuard from removing the property methods
81         setScroll(getScroll());
82     }
83 
84     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)85     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
86         super.onLayout(changed, left, top, right, bottom);
87         ensureChildVisible(getSelectedTab());
88     }
89 
90     // in case of a configuration change, adjust tab width
updateLayout()91     protected void updateLayout() {
92         final int count = mContentView.getChildCount();
93         for (int i = 0; i < count; i++) {
94             final TabView tv = (TabView) mContentView.getChildAt(i);
95             tv.updateLayoutParams();
96         }
97         ensureChildVisible(getSelectedTab());
98     }
99 
setSelectedTab(int position)100     void setSelectedTab(int position) {
101         View v = getSelectedTab();
102         if (v != null) {
103             v.setActivated(false);
104         }
105         mSelected = position;
106         v = getSelectedTab();
107         if (v != null) {
108             v.setActivated(true);
109         }
110         requestLayout();
111     }
112 
getChildIndex(View v)113     int getChildIndex(View v) {
114         return mContentView.indexOfChild(v);
115     }
116 
getSelectedTab()117     View getSelectedTab() {
118         if ((mSelected >= 0) && (mSelected < mContentView.getChildCount())) {
119             return mContentView.getChildAt(mSelected);
120         } else {
121             return null;
122         }
123     }
124 
clearTabs()125     void clearTabs() {
126         mContentView.removeAllViews();
127     }
128 
addTab(View tab)129     void addTab(View tab) {
130         mContentView.addView(tab);
131         tab.setActivated(false);
132     }
133 
removeTab(View tab)134     void removeTab(View tab) {
135         int ix = mContentView.indexOfChild(tab);
136         if (ix == mSelected) {
137             mSelected = -1;
138         } else if (ix < mSelected) {
139             mSelected--;
140         }
141         mContentView.removeView(tab);
142     }
143 
ensureChildVisible(View child)144     private void ensureChildVisible(View child) {
145         if (child != null) {
146             int childl = child.getLeft();
147             int childr = childl + child.getWidth();
148             int viewl = getScrollX();
149             int viewr = viewl + getWidth();
150             if (childl < viewl) {
151                 // need scrolling to left
152                 animateScroll(childl);
153             } else if (childr > viewr) {
154                 // need scrolling to right
155                 animateScroll(childr - viewr + viewl);
156             }
157         }
158     }
159 
160 // TODO: These animations are broken and don't work correctly, removing for now
161 //       as animateOut is actually causing issues
162 //    private void animateIn(View tab) {
163 //        ObjectAnimator animator = ObjectAnimator.ofInt(tab, "TranslationX", 500, 0);
164 //        animator.setDuration(mAnimationDuration);
165 //        animator.start();
166 //    }
167 //
168 //    private void animateOut(final View tab) {
169 //        ObjectAnimator animator = ObjectAnimator.ofInt(
170 //                tab, "TranslationX", 0, getScrollX() - tab.getRight());
171 //        animator.setDuration(mAnimationDuration);
172 //        animator.addListener(new AnimatorListenerAdapter() {
173 //            @Override
174 //            public void onAnimationEnd(Animator animation) {
175 //                mContentView.removeView(tab);
176 //            }
177 //        });
178 //        animator.setInterpolator(new AccelerateInterpolator());
179 //        animator.start();
180 //    }
181 
animateScroll(int newscroll)182     private void animateScroll(int newscroll) {
183         ObjectAnimator animator = ObjectAnimator.ofInt(this, "scroll", getScrollX(), newscroll);
184         animator.setDuration(mAnimationDuration);
185         animator.start();
186     }
187 
188     /**
189      * required for animation
190      */
setScroll(int newscroll)191     public void setScroll(int newscroll) {
192         scrollTo(newscroll, getScrollY());
193     }
194 
195     /**
196      * required for animation
197      */
getScroll()198     public int getScroll() {
199         return getScrollX();
200     }
201 
202     @Override
onScrollChanged(int l, int t, int oldl, int oldt)203     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
204         super.onScrollChanged(l, t, oldl, oldt);
205 
206         // TabViews base their drawing based on their absolute position within the
207         // window. When hardware accelerated, we need to recreate their display list
208         // when they scroll
209         if (isHardwareAccelerated()) {
210             int count = mContentView.getChildCount();
211             for (int i = 0; i < count; i++) {
212                 mContentView.getChildAt(i).invalidate();
213             }
214         }
215     }
216 
217     class TabLayout extends LinearLayout {
218 
TabLayout(Context context)219         public TabLayout(Context context) {
220             super(context);
221             setChildrenDrawingOrderEnabled(true);
222         }
223 
224         @Override
onMeasure(int hspec, int vspec)225         protected void onMeasure(int hspec, int vspec) {
226             super.onMeasure(hspec, vspec);
227             int w = getMeasuredWidth();
228             w -= Math.max(0, mContentView.getChildCount() - 1) * mTabOverlap;
229             setMeasuredDimension(w, getMeasuredHeight());
230         }
231 
232         @Override
onLayout(boolean changed, int left, int top, int right, int bottom)233         protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
234             super.onLayout(changed, left, top, right, bottom);
235             if (getChildCount() > 1) {
236                 int nextLeft = getChildAt(0).getRight() - mTabOverlap;
237                 for (int i = 1; i < getChildCount(); i++) {
238                     View tab = getChildAt(i);
239                     int w = tab.getRight() - tab.getLeft();
240                     tab.layout(nextLeft, tab.getTop(), nextLeft + w, tab.getBottom());
241                     nextLeft += w - mTabOverlap;
242                 }
243             }
244         }
245 
246         @Override
getChildDrawingOrder(int count, int i)247         protected int getChildDrawingOrder(int count, int i) {
248             int next = -1;
249             if ((i == (count - 1)) && (mSelected >= 0) && (mSelected < count)) {
250                 next = mSelected;
251             } else {
252                 next = count - i - 1;
253                 if (next <= mSelected && next > 0) {
254                     next--;
255                 }
256             }
257             return next;
258         }
259 
260     }
261 
262 }
263