/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.widget; import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.app.ActionBar; import android.content.Context; import android.graphics.drawable.Drawable; import android.text.TextUtils.TruncateAt; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; public class ScrollingTabContainerView extends HorizontalScrollView { Runnable mTabSelector; private TabClickListener mTabClickListener; private LinearLayout mTabLayout; int mMaxTabWidth; protected Animator mVisibilityAnim; protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener(); private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator(); private static final int FADE_DURATION = 200; public ScrollingTabContainerView(Context context) { super(context); setHorizontalScrollBarEnabled(false); } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int widthMode = MeasureSpec.getMode(widthMeasureSpec); setFillViewport(widthMode == MeasureSpec.EXACTLY); final int childCount = getChildCount(); if (childCount > 1 && (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) { if (childCount > 2) { mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f); } else { mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2; } } else { mMaxTabWidth = -1; } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } public void setTabSelected(int position) { if (mTabLayout == null) { return; } final int tabCount = mTabLayout.getChildCount(); for (int i = 0; i < tabCount; i++) { final View child = mTabLayout.getChildAt(i); final boolean isSelected = i == position; child.setSelected(isSelected); if (isSelected) { animateToTab(position); } } } public void setContentHeight(int contentHeight) { mTabLayout.getLayoutParams().height = contentHeight; requestLayout(); } public void animateToVisibility(int visibility) { if (mVisibilityAnim != null) { mVisibilityAnim.cancel(); } if (visibility == VISIBLE) { if (getVisibility() != VISIBLE) { setAlpha(0); } ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1); anim.setDuration(FADE_DURATION); anim.setInterpolator(sAlphaInterpolator); anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); anim.start(); } else { ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0); anim.setDuration(FADE_DURATION); anim.setInterpolator(sAlphaInterpolator); anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); anim.start(); } } public void animateToTab(int position) { final View tabView = mTabLayout.getChildAt(position); if (mTabSelector != null) { removeCallbacks(mTabSelector); } mTabSelector = new Runnable() { public void run() { final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2; smoothScrollTo(scrollPos, 0); mTabSelector = null; } }; post(mTabSelector); } public void setTabLayout(LinearLayout tabLayout) { if (mTabLayout != tabLayout) { if (mTabLayout != null) { ((ViewGroup) mTabLayout.getParent()).removeView(mTabLayout); } if (tabLayout != null) { addView(tabLayout); } mTabLayout = tabLayout; } } public LinearLayout getTabLayout() { return mTabLayout; } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mTabSelector != null) { removeCallbacks(mTabSelector); } } private TabView createTabView(ActionBar.Tab tab) { final TabView tabView = new TabView(getContext(), tab); tabView.setFocusable(true); if (mTabClickListener == null) { mTabClickListener = new TabClickListener(); } tabView.setOnClickListener(mTabClickListener); return tabView; } public void addTab(ActionBar.Tab tab, boolean setSelected) { View tabView = createTabView(tab); mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1)); if (setSelected) { tabView.setSelected(true); } } public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { final TabView tabView = createTabView(tab); mTabLayout.addView(tabView, position, new LinearLayout.LayoutParams( 0, LayoutParams.MATCH_PARENT, 1)); if (setSelected) { tabView.setSelected(true); } } public void updateTab(int position) { ((TabView) mTabLayout.getChildAt(position)).update(); } public void removeTabAt(int position) { if (mTabLayout != null) { mTabLayout.removeViewAt(position); } } public void removeAllTabs() { if (mTabLayout != null) { mTabLayout.removeAllViews(); } } private class TabView extends LinearLayout { private ActionBar.Tab mTab; private TextView mTextView; private ImageView mIconView; private View mCustomView; public TabView(Context context, ActionBar.Tab tab) { super(context, null, com.android.internal.R.attr.actionBarTabStyle); mTab = tab; update(); } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // Re-measure if we went beyond our maximum size. if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) { super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY), heightMeasureSpec); } } public void update() { final ActionBar.Tab tab = mTab; final View custom = tab.getCustomView(); if (custom != null) { addView(custom); mCustomView = custom; if (mTextView != null) mTextView.setVisibility(GONE); if (mIconView != null) { mIconView.setVisibility(GONE); mIconView.setImageDrawable(null); } } else { if (mCustomView != null) { removeView(mCustomView); mCustomView = null; } final Drawable icon = tab.getIcon(); final CharSequence text = tab.getText(); if (icon != null) { if (mIconView == null) { ImageView iconView = new ImageView(getContext()); LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.CENTER_VERTICAL; iconView.setLayoutParams(lp); addView(iconView, 0); mIconView = iconView; } mIconView.setImageDrawable(icon); mIconView.setVisibility(VISIBLE); } else if (mIconView != null) { mIconView.setVisibility(GONE); mIconView.setImageDrawable(null); } if (text != null) { if (mTextView == null) { TextView textView = new TextView(getContext(), null, com.android.internal.R.attr.actionBarTabTextStyle); textView.setEllipsize(TruncateAt.END); LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.CENTER_VERTICAL; textView.setLayoutParams(lp); addView(textView); mTextView = textView; } mTextView.setText(text); mTextView.setVisibility(VISIBLE); } else if (mTextView != null) { mTextView.setVisibility(GONE); mTextView.setText(null); } } } public ActionBar.Tab getTab() { return mTab; } } private class TabClickListener implements OnClickListener { public void onClick(View view) { TabView tabView = (TabView) view; tabView.getTab().select(); final int tabCount = mTabLayout.getChildCount(); for (int i = 0; i < tabCount; i++) { final View child = mTabLayout.getChildAt(i); child.setSelected(child == view); } } } protected class VisibilityAnimListener implements Animator.AnimatorListener { private boolean mCanceled = false; private int mFinalVisibility; public VisibilityAnimListener withFinalVisibility(int visibility) { mFinalVisibility = visibility; return this; } @Override public void onAnimationStart(Animator animation) { setVisibility(VISIBLE); mVisibilityAnim = animation; mCanceled = false; } @Override public void onAnimationEnd(Animator animation) { if (mCanceled) return; mVisibilityAnim = null; setVisibility(mFinalVisibility); } @Override public void onAnimationCancel(Animator animation) { mCanceled = true; } @Override public void onAnimationRepeat(Animator animation) { } } }