Source code
package android.support.design.widget;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.R;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v4.widget.TextViewCompat;
import android.support.v7.internal.widget.TintManager;
import android.text.Layout;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
public class TabLayout extends HorizontalScrollView {
private static final int ANIMATION_DURATION = 300;
private static final int DEFAULT_GAP_TEXT_ICON = 8;
private static final int DEFAULT_HEIGHT = 48;
private static final int DEFAULT_HEIGHT_WITH_TEXT_ICON = 72;
private static final int FIXED_WRAP_GUTTER_MIN = 16;
public static final int GRAVITY_CENTER = 1;
public static final int GRAVITY_FILL = 0;
private static final int INVALID_WIDTH = -1;
public static final int MODE_FIXED = 1;
public static final int MODE_SCROLLABLE = 0;
private static final int MOTION_NON_ADJACENT_OFFSET = 24;
private static final int TAB_MIN_WIDTH_MARGIN = 56;
private int mContentInsetStart;
private ValueAnimatorCompat mIndicatorAnimator;
private int mMode;
private OnTabSelectedListener mOnTabSelectedListener;
private final int mRequestedTabMaxWidth;
private final int mRequestedTabMinWidth;
private ValueAnimatorCompat mScrollAnimator;
private final int mScrollableTabMinWidth;
private Tab mSelectedTab;
private final int mTabBackgroundResId;
private OnClickListener mTabClickListener;
private int mTabGravity;
private int mTabMaxWidth;
private int mTabPaddingBottom;
private int mTabPaddingEnd;
private int mTabPaddingStart;
private int mTabPaddingTop;
private final SlidingTabStrip mTabStrip;
private int mTabTextAppearance;
private ColorStateList mTabTextColors;
private float mTabTextMultiLineSize;
private float mTabTextSize;
private final ArrayList<Tab> mTabs;
@Retention(RetentionPolicy.SOURCE)
public @interface Mode {
}
public interface OnTabSelectedListener {
void onTabReselected(Tab tab);
void onTabSelected(Tab tab);
void onTabUnselected(Tab tab);
}
private class SlidingTabStrip extends LinearLayout {
private ValueAnimatorCompat mCurrentAnimator;
private int mIndicatorLeft = -1;
private int mIndicatorRight = -1;
private int mSelectedIndicatorHeight;
private final Paint mSelectedIndicatorPaint;
private int mSelectedPosition = -1;
private float mSelectionOffset;
SlidingTabStrip(Context context) {
super(context);
setWillNotDraw(false);
this.mSelectedIndicatorPaint = new Paint();
}
void setSelectedIndicatorColor(int color) {
if (this.mSelectedIndicatorPaint.getColor() != color) {
this.mSelectedIndicatorPaint.setColor(color);
ViewCompat.postInvalidateOnAnimation(this);
}
}
void setSelectedIndicatorHeight(int height) {
if (this.mSelectedIndicatorHeight != height) {
this.mSelectedIndicatorHeight = height;
ViewCompat.postInvalidateOnAnimation(this);
}
}
boolean childrenNeedLayout() {
int z = getChildCount();
for (int i = 0; i < z; i++) {
if (getChildAt(i).getWidth() <= 0) {
return true;
}
}
return false;
}
void setIndicatorPositionFromTabPosition(int position, float positionOffset) {
this.mSelectedPosition = position;
this.mSelectionOffset = positionOffset;
updateIndicatorPosition();
}
float getIndicatorPosition() {
return ((float) this.mSelectedPosition) + this.mSelectionOffset;
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (MeasureSpec.getMode(widthMeasureSpec) == 1073741824 && TabLayout.this.mMode == 1 && TabLayout.this.mTabGravity == 1) {
int i;
int count = getChildCount();
int largestTabWidth = 0;
int z = count;
for (i = 0; i < z; i++) {
View child = getChildAt(i);
if (child.getVisibility() == 0) {
largestTabWidth = Math.max(largestTabWidth, child.getMeasuredWidth());
}
}
if (largestTabWidth > 0) {
boolean remeasure = false;
if (largestTabWidth * count <= getMeasuredWidth() - (TabLayout.this.dpToPx(16) * 2)) {
for (i = 0; i < count; i++) {
LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
if (lp.width != largestTabWidth || lp.weight != 0.0f) {
lp.width = largestTabWidth;
lp.weight = 0.0f;
remeasure = true;
}
}
} else {
TabLayout.this.mTabGravity = 0;
TabLayout.this.updateTabViews(false);
remeasure = true;
}
if (remeasure) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
}
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (this.mCurrentAnimator == null || !this.mCurrentAnimator.isRunning()) {
updateIndicatorPosition();
return;
}
this.mCurrentAnimator.cancel();
animateIndicatorToPosition(this.mSelectedPosition, Math.round((1.0f - this.mCurrentAnimator.getAnimatedFraction()) * ((float) this.mCurrentAnimator.getDuration())));
}
private void updateIndicatorPosition() {
int right;
int left;
View selectedTitle = getChildAt(this.mSelectedPosition);
if (selectedTitle == null || selectedTitle.getWidth() <= 0) {
right = -1;
left = -1;
} else {
left = selectedTitle.getLeft();
right = selectedTitle.getRight();
if (this.mSelectionOffset > 0.0f && this.mSelectedPosition < getChildCount() - 1) {
View nextTitle = getChildAt(this.mSelectedPosition + 1);
left = (int) ((this.mSelectionOffset * ((float) nextTitle.getLeft())) + ((1.0f - this.mSelectionOffset) * ((float) left)));
right = (int) ((this.mSelectionOffset * ((float) nextTitle.getRight())) + ((1.0f - this.mSelectionOffset) * ((float) right)));
}
}
setIndicatorPosition(left, right);
}
private void setIndicatorPosition(int left, int right) {
if (left != this.mIndicatorLeft || right != this.mIndicatorRight) {
this.mIndicatorLeft = left;
this.mIndicatorRight = right;
ViewCompat.postInvalidateOnAnimation(this);
}
}
void animateIndicatorToPosition(final int position, int duration) {
int startLeft;
int startRight;
boolean isRtl = ViewCompat.getLayoutDirection(this) == 1;
View targetView = getChildAt(position);
final int targetLeft = targetView.getLeft();
final int targetRight = targetView.getRight();
if (Math.abs(position - this.mSelectedPosition) <= 1) {
startLeft = this.mIndicatorLeft;
startRight = this.mIndicatorRight;
} else {
int offset = TabLayout.this.dpToPx(24);
if (position < this.mSelectedPosition) {
if (isRtl) {
startRight = targetLeft - offset;
startLeft = startRight;
} else {
startRight = targetRight + offset;
startLeft = startRight;
}
} else if (isRtl) {
startRight = targetRight + offset;
startLeft = startRight;
} else {
startRight = targetLeft - offset;
startLeft = startRight;
}
}
if (startLeft != targetLeft || startRight != targetRight) {
ValueAnimatorCompat animator = TabLayout.this.mIndicatorAnimator = ViewUtils.createAnimator();
animator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
animator.setDuration(duration);
animator.setFloatValues(0.0f, 1.0f);
animator.setUpdateListener(new AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimatorCompat animator) {
float fraction = animator.getAnimatedFraction();
SlidingTabStrip.this.setIndicatorPosition(AnimationUtils.lerp(startLeft, targetLeft, fraction), AnimationUtils.lerp(startRight, targetRight, fraction));
}
});
animator.setListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(ValueAnimatorCompat animator) {
SlidingTabStrip.this.mSelectedPosition = position;
SlidingTabStrip.this.mSelectionOffset = 0.0f;
}
public void onAnimationCancel(ValueAnimatorCompat animator) {
SlidingTabStrip.this.mSelectedPosition = position;
SlidingTabStrip.this.mSelectionOffset = 0.0f;
}
});
animator.start();
this.mCurrentAnimator = animator;
}
}
public void draw(Canvas canvas) {
super.draw(canvas);
if (this.mIndicatorLeft >= 0 && this.mIndicatorRight > this.mIndicatorLeft) {
canvas.drawRect((float) this.mIndicatorLeft, (float) (getHeight() - this.mSelectedIndicatorHeight), (float) this.mIndicatorRight, (float) getHeight(), this.mSelectedIndicatorPaint);
}
}
}
public static final class Tab {
public static final int INVALID_POSITION = -1;
private CharSequence mContentDesc;
private View mCustomView;
private Drawable mIcon;
private final TabLayout mParent;
private int mPosition = -1;
private Object mTag;
private CharSequence mText;
Tab(TabLayout parent) {
this.mParent = parent;
}
@Nullable
public Object getTag() {
return this.mTag;
}
@NonNull
public Tab setTag(@Nullable Object tag) {
this.mTag = tag;
return this;
}
@Nullable
public View getCustomView() {
return this.mCustomView;
}
@NonNull
public Tab setCustomView(@Nullable View view) {
this.mCustomView = view;
if (this.mPosition >= 0) {
this.mParent.updateTab(this.mPosition);
}
return this;
}
@NonNull
public Tab setCustomView(@LayoutRes int layoutResId) {
return setCustomView(LayoutInflater.from(this.mParent.getContext()).inflate(layoutResId, null));
}
@Nullable
public Drawable getIcon() {
return this.mIcon;
}
public int getPosition() {
return this.mPosition;
}
void setPosition(int position) {
this.mPosition = position;
}
@Nullable
public CharSequence getText() {
return this.mText;
}
@NonNull
public Tab setIcon(@Nullable Drawable icon) {
this.mIcon = icon;
if (this.mPosition >= 0) {
this.mParent.updateTab(this.mPosition);
}
return this;
}
@NonNull
public Tab setIcon(@DrawableRes int resId) {
return setIcon(TintManager.getDrawable(this.mParent.getContext(), resId));
}
@NonNull
public Tab setText(@Nullable CharSequence text) {
this.mText = text;
if (this.mPosition >= 0) {
this.mParent.updateTab(this.mPosition);
}
return this;
}
@NonNull
public Tab setText(@StringRes int resId) {
return setText(this.mParent.getResources().getText(resId));
}
public void select() {
this.mParent.selectTab(this);
}
public boolean isSelected() {
return this.mParent.getSelectedTabPosition() == this.mPosition;
}
@NonNull
public Tab setContentDescription(@StringRes int resId) {
return setContentDescription(this.mParent.getResources().getText(resId));
}
@NonNull
public Tab setContentDescription(@Nullable CharSequence contentDesc) {
this.mContentDesc = contentDesc;
if (this.mPosition >= 0) {
this.mParent.updateTab(this.mPosition);
}
return this;
}
@Nullable
public CharSequence getContentDescription() {
return this.mContentDesc;
}
}
@Retention(RetentionPolicy.SOURCE)
public @interface TabGravity {
}
class TabView extends LinearLayout implements OnLongClickListener {
private ImageView mCustomIconView;
private TextView mCustomTextView;
private View mCustomView;
private int mDefaultMaxLines = 2;
private ImageView mIconView;
private final Tab mTab;
private TextView mTextView;
public TabView(Context context, Tab tab) {
super(context);
this.mTab = tab;
if (TabLayout.this.mTabBackgroundResId != 0) {
setBackgroundDrawable(TintManager.getDrawable(context, TabLayout.this.mTabBackgroundResId));
}
ViewCompat.setPaddingRelative(this, TabLayout.this.mTabPaddingStart, TabLayout.this.mTabPaddingTop, TabLayout.this.mTabPaddingEnd, TabLayout.this.mTabPaddingBottom);
setGravity(17);
setOrientation(1);
update();
}
public void setSelected(boolean selected) {
boolean changed = isSelected() != selected;
super.setSelected(selected);
if (changed && selected) {
sendAccessibilityEvent(4);
if (this.mTextView != null) {
this.mTextView.setSelected(selected);
}
if (this.mIconView != null) {
this.mIconView.setSelected(selected);
}
}
}
@TargetApi(14)
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(android.support.v7.app.ActionBar.Tab.class.getName());
}
@TargetApi(14)
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(android.support.v7.app.ActionBar.Tab.class.getName());
}
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int specWidth = MeasureSpec.getSize(widthMeasureSpec);
int maxWidth = TabLayout.this.getTabMaxWidth();
if (maxWidth <= 0 || (specWidth != 0 && specWidth <= maxWidth)) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} else {
super.onMeasure(MeasureSpec.makeMeasureSpec(TabLayout.this.mTabMaxWidth, Integer.MIN_VALUE), heightMeasureSpec);
}
if (this.mTextView != null) {
Resources res = getResources();
float textSize = TabLayout.this.mTabTextSize;
int maxLines = this.mDefaultMaxLines;
if (this.mIconView != null && this.mIconView.getVisibility() == 0) {
maxLines = 1;
} else if (this.mTextView != null && this.mTextView.getLineCount() > 1) {
textSize = TabLayout.this.mTabTextMultiLineSize;
}
float curTextSize = this.mTextView.getTextSize();
int curLineCount = this.mTextView.getLineCount();
int curMaxLines = TextViewCompat.getMaxLines(this.mTextView);
if (textSize != curTextSize || (curMaxLines >= 0 && maxLines != curMaxLines)) {
boolean updateTextView = true;
if (TabLayout.this.mMode == 1 && textSize > curTextSize && curLineCount == 1) {
Layout layout = this.mTextView.getLayout();
if (layout == null || approximateLineWidth(layout, 0, textSize) > ((float) layout.getWidth())) {
updateTextView = false;
}
}
if (updateTextView) {
this.mTextView.setTextSize(0, textSize);
this.mTextView.setMaxLines(maxLines);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
}
final void update() {
Tab tab = this.mTab;
View custom = tab.getCustomView();
if (custom != null) {
TabView customParent = custom.getParent();
if (customParent != this) {
if (customParent != null) {
customParent.removeView(custom);
}
addView(custom);
}
this.mCustomView = custom;
if (this.mTextView != null) {
this.mTextView.setVisibility(8);
}
if (this.mIconView != null) {
this.mIconView.setVisibility(8);
this.mIconView.setImageDrawable(null);
}
this.mCustomTextView = (TextView) custom.findViewById(16908308);
if (this.mCustomTextView != null) {
this.mDefaultMaxLines = TextViewCompat.getMaxLines(this.mCustomTextView);
}
this.mCustomIconView = (ImageView) custom.findViewById(16908294);
} else {
if (this.mCustomView != null) {
removeView(this.mCustomView);
this.mCustomView = null;
}
this.mCustomTextView = null;
this.mCustomIconView = null;
}
if (this.mCustomView == null) {
if (this.mIconView == null) {
ImageView iconView = (ImageView) LayoutInflater.from(getContext()).inflate(R.layout.design_layout_tab_icon, this, false);
addView(iconView, 0);
this.mIconView = iconView;
}
if (this.mTextView == null) {
TextView textView = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.design_layout_tab_text, this, false);
addView(textView);
this.mTextView = textView;
this.mDefaultMaxLines = TextViewCompat.getMaxLines(this.mTextView);
}
this.mTextView.setTextAppearance(getContext(), TabLayout.this.mTabTextAppearance);
if (TabLayout.this.mTabTextColors != null) {
this.mTextView.setTextColor(TabLayout.this.mTabTextColors);
}
updateTextAndIcon(tab, this.mTextView, this.mIconView);
} else if (this.mCustomTextView != null || this.mCustomIconView != null) {
updateTextAndIcon(tab, this.mCustomTextView, this.mCustomIconView);
}
}
private void updateTextAndIcon(Tab tab, TextView textView, ImageView iconView) {
boolean hasText;
Drawable icon = tab.getIcon();
CharSequence text = tab.getText();
if (iconView != null) {
if (icon != null) {
iconView.setImageDrawable(icon);
iconView.setVisibility(0);
setVisibility(0);
} else {
iconView.setVisibility(8);
iconView.setImageDrawable(null);
}
iconView.setContentDescription(tab.getContentDescription());
}
if (TextUtils.isEmpty(text)) {
hasText = false;
} else {
hasText = true;
}
if (textView != null) {
if (hasText) {
textView.setText(text);
textView.setContentDescription(tab.getContentDescription());
textView.setVisibility(0);
setVisibility(0);
} else {
textView.setVisibility(8);
textView.setText(null);
}
}
if (iconView != null) {
MarginLayoutParams lp = (MarginLayoutParams) iconView.getLayoutParams();
int bottomMargin = 0;
if (hasText && iconView.getVisibility() == 0) {
bottomMargin = TabLayout.this.dpToPx(8);
}
if (bottomMargin != lp.bottomMargin) {
lp.bottomMargin = bottomMargin;
iconView.requestLayout();
}
}
if (hasText || TextUtils.isEmpty(tab.getContentDescription())) {
setOnLongClickListener(null);
setLongClickable(false);
return;
}
setOnLongClickListener(this);
}
public boolean onLongClick(View v) {
int[] screenPos = new int[2];
getLocationOnScreen(screenPos);
Context context = getContext();
int width = getWidth();
int height = getHeight();
int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
Toast cheatSheet = Toast.makeText(context, this.mTab.getContentDescription(), 0);
cheatSheet.setGravity(49, (screenPos[0] + (width / 2)) - (screenWidth / 2), height);
cheatSheet.show();
return true;
}
public Tab getTab() {
return this.mTab;
}
private float approximateLineWidth(Layout layout, int line, float textSize) {
return layout.getLineWidth(line) * (textSize / layout.getPaint().getTextSize());
}
}
public static class TabLayoutOnPageChangeListener implements OnPageChangeListener {
private int mPreviousScrollState;
private int mScrollState;
private final WeakReference<TabLayout> mTabLayoutRef;
public TabLayoutOnPageChangeListener(TabLayout tabLayout) {
this.mTabLayoutRef = new WeakReference(tabLayout);
}
public void onPageScrollStateChanged(int state) {
this.mPreviousScrollState = this.mScrollState;
this.mScrollState = state;
}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
boolean updateText = true;
TabLayout tabLayout = (TabLayout) this.mTabLayoutRef.get();
if (tabLayout != null) {
if (!(this.mScrollState == 1 || (this.mScrollState == 2 && this.mPreviousScrollState == 1))) {
updateText = false;
}
tabLayout.setScrollPosition(position, positionOffset, updateText);
}
}
public void onPageSelected(int position) {
TabLayout tabLayout = (TabLayout) this.mTabLayoutRef.get();
if (tabLayout != null && tabLayout.getSelectedTabPosition() != position) {
tabLayout.selectTab(tabLayout.getTabAt(position), this.mScrollState == 0);
}
}
}
public static class ViewPagerOnTabSelectedListener implements OnTabSelectedListener {
private final ViewPager mViewPager;
public ViewPagerOnTabSelectedListener(ViewPager viewPager) {
this.mViewPager = viewPager;
}
public void onTabSelected(Tab tab) {
this.mViewPager.setCurrentItem(tab.getPosition());
}
public void onTabUnselected(Tab tab) {
}
public void onTabReselected(Tab tab) {
}
}
public TabLayout(Context context) {
this(context, null);
}
public TabLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mTabs = new ArrayList();
this.mTabMaxWidth = ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED;
ThemeUtils.checkAppCompatTheme(context);
setHorizontalScrollBarEnabled(false);
this.mTabStrip = new SlidingTabStrip(context);
addView(this.mTabStrip, -2, -1);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout, defStyleAttr, R.style.Widget_Design_TabLayout);
this.mTabStrip.setSelectedIndicatorHeight(a.getDimensionPixelSize(R.styleable.TabLayout_tabIndicatorHeight, 0));
this.mTabStrip.setSelectedIndicatorColor(a.getColor(R.styleable.TabLayout_tabIndicatorColor, 0));
int dimensionPixelSize = a.getDimensionPixelSize(R.styleable.TabLayout_tabPadding, 0);
this.mTabPaddingBottom = dimensionPixelSize;
this.mTabPaddingEnd = dimensionPixelSize;
this.mTabPaddingTop = dimensionPixelSize;
this.mTabPaddingStart = dimensionPixelSize;
this.mTabPaddingStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingStart, this.mTabPaddingStart);
this.mTabPaddingTop = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingTop, this.mTabPaddingTop);
this.mTabPaddingEnd = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingEnd, this.mTabPaddingEnd);
this.mTabPaddingBottom = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingBottom, this.mTabPaddingBottom);
this.mTabTextAppearance = a.getResourceId(R.styleable.TabLayout_tabTextAppearance, R.style.TextAppearance_Design_Tab);
TypedArray ta = context.obtainStyledAttributes(this.mTabTextAppearance, R.styleable.TextAppearance);
try {
this.mTabTextSize = (float) ta.getDimensionPixelSize(R.styleable.TextAppearance_android_textSize, 0);
this.mTabTextColors = ta.getColorStateList(R.styleable.TextAppearance_android_textColor);
if (a.hasValue(R.styleable.TabLayout_tabTextColor)) {
this.mTabTextColors = a.getColorStateList(R.styleable.TabLayout_tabTextColor);
}
if (a.hasValue(R.styleable.TabLayout_tabSelectedTextColor)) {
this.mTabTextColors = createColorStateList(this.mTabTextColors.getDefaultColor(), a.getColor(R.styleable.TabLayout_tabSelectedTextColor, 0));
}
this.mRequestedTabMinWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMinWidth, -1);
this.mRequestedTabMaxWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMaxWidth, -1);
this.mTabBackgroundResId = a.getResourceId(R.styleable.TabLayout_tabBackground, 0);
this.mContentInsetStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabContentStart, 0);
this.mMode = a.getInt(R.styleable.TabLayout_tabMode, 1);
this.mTabGravity = a.getInt(R.styleable.TabLayout_tabGravity, 0);
a.recycle();
Resources res = getResources();
this.mTabTextMultiLineSize = (float) res.getDimensionPixelSize(R.dimen.design_tab_text_size_2line);
this.mScrollableTabMinWidth = res.getDimensionPixelSize(R.dimen.design_tab_scrollable_min_width);
applyModeAndGravity();
} finally {
ta.recycle();
}
}
public void setSelectedTabIndicatorColor(@ColorInt int color) {
this.mTabStrip.setSelectedIndicatorColor(color);
}
public void setSelectedTabIndicatorHeight(int height) {
this.mTabStrip.setSelectedIndicatorHeight(height);
}
public void setScrollPosition(int position, float positionOffset, boolean updateSelectedText) {
if ((this.mIndicatorAnimator == null || !this.mIndicatorAnimator.isRunning()) && position >= 0 && position < this.mTabStrip.getChildCount()) {
this.mTabStrip.setIndicatorPositionFromTabPosition(position, positionOffset);
scrollTo(calculateScrollXForTab(position, positionOffset), 0);
if (updateSelectedText) {
setSelectedTabView(Math.round(((float) position) + positionOffset));
}
}
}
private float getScrollPosition() {
return this.mTabStrip.getIndicatorPosition();
}
public void addTab(@NonNull Tab tab) {
addTab(tab, this.mTabs.isEmpty());
}
public void addTab(@NonNull Tab tab, int position) {
addTab(tab, position, this.mTabs.isEmpty());
}
public void addTab(@NonNull Tab tab, boolean setSelected) {
if (tab.mParent != this) {
throw new IllegalArgumentException("Tab belongs to a different TabLayout.");
}
addTabView(tab, setSelected);
configureTab(tab, this.mTabs.size());
if (setSelected) {
tab.select();
}
}
public void addTab(@NonNull Tab tab, int position, boolean setSelected) {
if (tab.mParent != this) {
throw new IllegalArgumentException("Tab belongs to a different TabLayout.");
}
addTabView(tab, position, setSelected);
configureTab(tab, position);
if (setSelected) {
tab.select();
}
}
public void setOnTabSelectedListener(OnTabSelectedListener onTabSelectedListener) {
this.mOnTabSelectedListener = onTabSelectedListener;
}
@NonNull
public Tab newTab() {
return new Tab(this);
}
public int getTabCount() {
return this.mTabs.size();
}
@Nullable
public Tab getTabAt(int index) {
return (Tab) this.mTabs.get(index);
}
public int getSelectedTabPosition() {
return this.mSelectedTab != null ? this.mSelectedTab.getPosition() : -1;
}
public void removeTab(Tab tab) {
if (tab.mParent != this) {
throw new IllegalArgumentException("Tab does not belong to this TabLayout.");
}
removeTabAt(tab.getPosition());
}
public void removeTabAt(int position) {
int selectedTabPosition;
if (this.mSelectedTab != null) {
selectedTabPosition = this.mSelectedTab.getPosition();
} else {
selectedTabPosition = 0;
}
removeTabViewAt(position);
Tab removedTab = (Tab) this.mTabs.remove(position);
if (removedTab != null) {
removedTab.setPosition(-1);
}
int newTabCount = this.mTabs.size();
for (int i = position; i < newTabCount; i++) {
((Tab) this.mTabs.get(i)).setPosition(i);
}
if (selectedTabPosition == position) {
selectTab(this.mTabs.isEmpty() ? null : (Tab) this.mTabs.get(Math.max(0, position - 1)));
}
}
public void removeAllTabs() {
this.mTabStrip.removeAllViews();
Iterator<Tab> i = this.mTabs.iterator();
while (i.hasNext()) {
((Tab) i.next()).setPosition(-1);
i.remove();
}
this.mSelectedTab = null;
}
public void setTabMode(int mode) {
if (mode != this.mMode) {
this.mMode = mode;
applyModeAndGravity();
}
}
public int getTabMode() {
return this.mMode;
}
public void setTabGravity(int gravity) {
if (this.mTabGravity != gravity) {
this.mTabGravity = gravity;
applyModeAndGravity();
}
}
public int getTabGravity() {
return this.mTabGravity;
}
public void setTabTextColors(@Nullable ColorStateList textColor) {
if (this.mTabTextColors != textColor) {
this.mTabTextColors = textColor;
updateAllTabs();
}
}
@Nullable
public ColorStateList getTabTextColors() {
return this.mTabTextColors;
}
public void setTabTextColors(int normalColor, int selectedColor) {
setTabTextColors(createColorStateList(normalColor, selectedColor));
}
public void setupWithViewPager(@NonNull ViewPager viewPager) {
PagerAdapter adapter = viewPager.getAdapter();
if (adapter == null) {
throw new IllegalArgumentException("ViewPager does not have a PagerAdapter set");
}
setTabsFromPagerAdapter(adapter);
viewPager.addOnPageChangeListener(new TabLayoutOnPageChangeListener(this));
setOnTabSelectedListener(new ViewPagerOnTabSelectedListener(viewPager));
if (adapter.getCount() > 0) {
int curItem = viewPager.getCurrentItem();
if (getSelectedTabPosition() != curItem) {
selectTab(getTabAt(curItem));
}
}
}
public void setTabsFromPagerAdapter(@NonNull PagerAdapter adapter) {
removeAllTabs();
int count = adapter.getCount();
for (int i = 0; i < count; i++) {
addTab(newTab().setText(adapter.getPageTitle(i)));
}
}
private void updateAllTabs() {
int z = this.mTabStrip.getChildCount();
for (int i = 0; i < z; i++) {
updateTab(i);
}
}
private TabView createTabView(Tab tab) {
TabView tabView = new TabView(getContext(), tab);
tabView.setFocusable(true);
tabView.setMinimumWidth(getTabMinWidth());
if (this.mTabClickListener == null) {
this.mTabClickListener = new OnClickListener() {
public void onClick(View view) {
((TabView) view).getTab().select();
}
};
}
tabView.setOnClickListener(this.mTabClickListener);
return tabView;
}
private void configureTab(Tab tab, int position) {
tab.setPosition(position);
this.mTabs.add(position, tab);
int count = this.mTabs.size();
for (int i = position + 1; i < count; i++) {
((Tab) this.mTabs.get(i)).setPosition(i);
}
}
private void updateTab(int position) {
TabView view = (TabView) this.mTabStrip.getChildAt(position);
if (view != null) {
view.update();
}
}
private void addTabView(Tab tab, boolean setSelected) {
TabView tabView = createTabView(tab);
this.mTabStrip.addView(tabView, createLayoutParamsForTabs());
if (setSelected) {
tabView.setSelected(true);
}
}
private void addTabView(Tab tab, int position, boolean setSelected) {
TabView tabView = createTabView(tab);
this.mTabStrip.addView(tabView, position, createLayoutParamsForTabs());
if (setSelected) {
tabView.setSelected(true);
}
}
private LayoutParams createLayoutParamsForTabs() {
LayoutParams lp = new LayoutParams(-2, -1);
updateTabViewLayoutParams(lp);
return lp;
}
private void updateTabViewLayoutParams(LayoutParams lp) {
if (this.mMode == 1 && this.mTabGravity == 0) {
lp.width = 0;
lp.weight = 1.0f;
return;
}
lp.width = -2;
lp.weight = 0.0f;
}
private int dpToPx(int dps) {
return Math.round(getResources().getDisplayMetrics().density * ((float) dps));
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int idealHeight = (dpToPx(getDefaultHeight()) + getPaddingTop()) + getPaddingBottom();
switch (MeasureSpec.getMode(heightMeasureSpec)) {
case Integer.MIN_VALUE:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(idealHeight, MeasureSpec.getSize(heightMeasureSpec)), 1073741824);
break;
case 0:
heightMeasureSpec = MeasureSpec.makeMeasureSpec(idealHeight, 1073741824);
break;
}
int specWidth = MeasureSpec.getSize(widthMeasureSpec);
if (MeasureSpec.getMode(widthMeasureSpec) != 0) {
int i;
if (this.mRequestedTabMaxWidth > 0) {
i = this.mRequestedTabMaxWidth;
} else {
i = specWidth - dpToPx(56);
}
this.mTabMaxWidth = i;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (getChildCount() == 1) {
View child = getChildAt(0);
boolean remeasure = false;
switch (this.mMode) {
case 0:
if (child.getMeasuredWidth() < getMeasuredWidth()) {
remeasure = true;
} else {
remeasure = false;
}
break;
case 1:
remeasure = child.getMeasuredWidth() != getMeasuredWidth();
break;
}
if (remeasure) {
child.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), 1073741824), getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom(), child.getLayoutParams().height));
}
}
}
private void removeTabViewAt(int position) {
this.mTabStrip.removeViewAt(position);
requestLayout();
}
private void animateToTab(int newPosition) {
if (newPosition != -1) {
if (getWindowToken() == null || !ViewCompat.isLaidOut(this) || this.mTabStrip.childrenNeedLayout()) {
setScrollPosition(newPosition, 0.0f, true);
return;
}
int startScrollX = getScrollX();
int targetScrollX = calculateScrollXForTab(newPosition, 0.0f);
if (startScrollX != targetScrollX) {
if (this.mScrollAnimator == null) {
this.mScrollAnimator = ViewUtils.createAnimator();
this.mScrollAnimator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
this.mScrollAnimator.setDuration(ANIMATION_DURATION);
this.mScrollAnimator.setUpdateListener(new AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimatorCompat animator) {
TabLayout.this.scrollTo(animator.getAnimatedIntValue(), 0);
}
});
}
this.mScrollAnimator.setIntValues(startScrollX, targetScrollX);
this.mScrollAnimator.start();
}
this.mTabStrip.animateIndicatorToPosition(newPosition, ANIMATION_DURATION);
}
}
private void setSelectedTabView(int position) {
int tabCount = this.mTabStrip.getChildCount();
if (position < tabCount && !this.mTabStrip.getChildAt(position).isSelected()) {
int i = 0;
while (i < tabCount) {
this.mTabStrip.getChildAt(i).setSelected(i == position);
i++;
}
}
}
void selectTab(Tab tab) {
selectTab(tab, true);
}
void selectTab(Tab tab, boolean updateIndicator) {
if (this.mSelectedTab != tab) {
if (updateIndicator) {
int newPosition = tab != null ? tab.getPosition() : -1;
if (newPosition != -1) {
setSelectedTabView(newPosition);
}
if ((this.mSelectedTab == null || this.mSelectedTab.getPosition() == -1) && newPosition != -1) {
setScrollPosition(newPosition, 0.0f, true);
} else {
animateToTab(newPosition);
}
}
if (!(this.mSelectedTab == null || this.mOnTabSelectedListener == null)) {
this.mOnTabSelectedListener.onTabUnselected(this.mSelectedTab);
}
this.mSelectedTab = tab;
if (this.mSelectedTab != null && this.mOnTabSelectedListener != null) {
this.mOnTabSelectedListener.onTabSelected(this.mSelectedTab);
}
} else if (this.mSelectedTab != null) {
if (this.mOnTabSelectedListener != null) {
this.mOnTabSelectedListener.onTabReselected(this.mSelectedTab);
}
animateToTab(tab.getPosition());
}
}
private int calculateScrollXForTab(int position, float positionOffset) {
int nextWidth = 0;
if (this.mMode != 0) {
return 0;
}
int selectedWidth;
View selectedChild = this.mTabStrip.getChildAt(position);
View nextChild = position + 1 < this.mTabStrip.getChildCount() ? this.mTabStrip.getChildAt(position + 1) : null;
if (selectedChild != null) {
selectedWidth = selectedChild.getWidth();
} else {
selectedWidth = 0;
}
if (nextChild != null) {
nextWidth = nextChild.getWidth();
}
return ((selectedChild.getLeft() + ((int) ((((float) (selectedWidth + nextWidth)) * positionOffset) * 0.5f))) + (selectedChild.getWidth() / 2)) - (getWidth() / 2);
}
private void applyModeAndGravity() {
int paddingStart = 0;
if (this.mMode == 0) {
paddingStart = Math.max(0, this.mContentInsetStart - this.mTabPaddingStart);
}
ViewCompat.setPaddingRelative(this.mTabStrip, paddingStart, 0, 0, 0);
switch (this.mMode) {
case 0:
this.mTabStrip.setGravity(8388611);
break;
case 1:
this.mTabStrip.setGravity(1);
break;
}
updateTabViews(true);
}
private void updateTabViews(boolean requestLayout) {
for (int i = 0; i < this.mTabStrip.getChildCount(); i++) {
View child = this.mTabStrip.getChildAt(i);
child.setMinimumWidth(getTabMinWidth());
updateTabViewLayoutParams((LayoutParams) child.getLayoutParams());
if (requestLayout) {
child.requestLayout();
}
}
}
private static ColorStateList createColorStateList(int defaultColor, int selectedColor) {
states = new int[2][];
int[] colors = new int[]{SELECTED_STATE_SET, selectedColor};
int i = 0 + 1;
states[i] = EMPTY_STATE_SET;
colors[i] = defaultColor;
i++;
return new ColorStateList(states, colors);
}
private int getDefaultHeight() {
boolean hasIconAndText = false;
int count = this.mTabs.size();
for (int i = 0; i < count; i++) {
Tab tab = (Tab) this.mTabs.get(i);
if (tab != null && tab.getIcon() != null && !TextUtils.isEmpty(tab.getText())) {
hasIconAndText = true;
break;
}
}
if (hasIconAndText) {
return 72;
}
return 48;
}
private int getTabMinWidth() {
if (this.mRequestedTabMinWidth != -1) {
return this.mRequestedTabMinWidth;
}
return this.mMode == 0 ? this.mScrollableTabMinWidth : 0;
}
private int getTabMaxWidth() {
return this.mTabMaxWidth;
}
}