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