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