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