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