am.widget.indicatortabstrip.IndicatorTabStrip.java Source code

Java tutorial

Introduction

Here is the source code for am.widget.indicatortabstrip.IndicatorTabStrip.java

Source

/*
 * Copyright (C) 2014 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 am.widget.indicatortabstrip;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.v4.content.ContextCompat;
import am.widget.basetabstrip.BaseTabStrip;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPager;
import android.text.TextPaint;
import android.util.AttributeSet;

/**
 * TabStripItem?5
 *
 * @author Alex
 */
@ViewPager.DecorView
public class IndicatorTabStrip extends BaseTabStrip {

    private static final int[] ATTRS = new int[] { android.R.attr.textSize, android.R.attr.textColor,
            android.R.attr.divider };
    private static final int DEFAULT_TEXT_SIZE = 14;// ?dp
    private static final int DEFAULT_TEXT_COLOR = 0xff000000;// 
    private static final int DEFAULT_ITEM_WIDTH = 64;// ??dp
    private static final int DEFAULT_ITEM_HEIGHT = 32;// ??dp
    private static final int DEFAULT_TAG_TEXT_SIZE = 11;// Tag?dp
    private static final int DEFAULT_TAG_TEXT_COLOR = 0xffffffff;// Tag
    private static final int DEFAULT_TAG_MIN_SIZE = 15;// Tag??dp
    public static final int INDICATOR_WIDTH_MODE_SET = 0;// 
    public static final int INDICATOR_WIDTH_MODE_TAB = 1;// ?
    public static final int INDICATOR_WIDTH_BY_DRAWABLE = -1;// 
    public static final int INDICATOR_HEIGHT_BY_DRAWABLE = -1;// 
    public static final int TAG_MIN_SIZE_MODE_HAS_TEXT = 0;// ??
    public static final int TAG_MIN_SIZE_MODE_ALWAYS = 1;// ?
    private final TextPaint mTextPaint;// 
    private float mTextSize;// ?
    private float mTextDesc;// ??
    private ColorStateList mTextColor;// 
    private float mTextScale;// 
    private ColorStateList mGradient;// ???
    private Drawable mDivider;// Divider
    private Drawable mIndicator;// 
    private int mIndicatorWidthMode;// ?
    private int mIndicatorPadding;// Padding???
    private int mIndicatorWidth;// 
    private int mIndicatorHeight;// 
    private Drawable mInterval;// ?
    private int mMinItemWidth;// ??
    private int mMinItemHeight;// ??
    private ItemTabAdapter mAdapter;// Tag
    private float mTagTextSize;// Tag?
    private float mTagTextDesc;// Tag??
    private int mTagTextColor;// Tag
    private Drawable mTagBackground;// Tag
    private int mTagMinSizeMode;// Tag??
    private int mTagMinWidth;// Tag?
    private int mTagMinHeight;// Tag?
    private TagLocation mTagLocation;// Tag
    private Rect mTextMeasureBounds = new Rect();// ?
    private int mCurrentPager = 0;
    private int mNextPager = 0;
    private float mOffset = 1;

    public IndicatorTabStrip(Context context) {
        this(context, null);
    }

    public IndicatorTabStrip(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public IndicatorTabStrip(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setItemClickable(true);
        setClickSmoothScroll(true);
        final float density = getResources().getDisplayMetrics().density;
        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextAlign(Align.CENTER);
        if (Build.VERSION.SDK_INT > 4) {
            updateTextPaintDensity();
        }
        mTagLocation = new TagLocation(TagLocation.LOCATION_EDGE);
        final TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
        int n = a.getIndexCount();
        int textSize = (int) (DEFAULT_TEXT_SIZE * density);
        ColorStateList textColors = null;
        Drawable divider = null;
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
            case 0:
                textSize = a.getDimensionPixelSize(attr, textSize);
                break;
            case 1:
                textColors = a.getColorStateList(attr);
                break;
            case 2:
                divider = a.getDrawable(attr);
                break;
            }
        }
        a.recycle();
        float scale = 1;
        Drawable itemBackground = null;
        ColorStateList gradient = null;
        Drawable indicator = null;
        int indicatorWidthMode = INDICATOR_WIDTH_MODE_SET;
        int indicatorPadding = 0;
        int indicatorWidth = INDICATOR_WIDTH_BY_DRAWABLE;
        int indicatorHeight = INDICATOR_HEIGHT_BY_DRAWABLE;
        Drawable interval = null;
        int minItemWidth = (int) (DEFAULT_ITEM_WIDTH * density);
        int minItemHeight = (int) (DEFAULT_ITEM_HEIGHT * density);
        int tagTextSize = (int) (DEFAULT_TAG_TEXT_SIZE * density);
        int tagTextColor = DEFAULT_TAG_TEXT_COLOR;
        Drawable tagBackground = getDefaultTagBackground();
        int tagMinSizeMode = TAG_MIN_SIZE_MODE_HAS_TEXT;
        int tagMinWidth = (int) (DEFAULT_TAG_MIN_SIZE * density);
        int tagMinHeight = (int) (DEFAULT_TAG_MIN_SIZE * density);
        int tagPaddingLeft = 0;
        int tagPaddingTop = 0;
        int tagPaddingRight = 0;
        int tagPaddingBottom = 0;
        int tagMarginLeft = 0;
        int tagMarginTop = 0;
        int tagMarginRight = 0;
        int tagMarginBottom = 0;
        TypedArray custom = context.obtainStyledAttributes(attrs, R.styleable.IndicatorTabStrip);
        textSize = custom.getDimensionPixelSize(R.styleable.IndicatorTabStrip_ttsTextSize, textSize);
        if (custom.hasValue(R.styleable.IndicatorTabStrip_ttsTextColor))
            textColors = custom.getColorStateList(R.styleable.IndicatorTabStrip_ttsTextColor);
        scale = custom.getFloat(R.styleable.IndicatorTabStrip_ttsTextScale, scale);
        if (custom.hasValue(R.styleable.IndicatorTabStrip_ttsBackground))
            itemBackground = custom.getDrawable(R.styleable.IndicatorTabStrip_ttsBackground);
        if (custom.hasValue(R.styleable.IndicatorTabStrip_ttsGradient))
            gradient = custom.getColorStateList(R.styleable.IndicatorTabStrip_ttsGradient);
        if (custom.hasValue(R.styleable.IndicatorTabStrip_ttsDivider))
            divider = custom.getDrawable(R.styleable.IndicatorTabStrip_ttsDivider);
        if (custom.hasValue(R.styleable.IndicatorTabStrip_ttsIndicator))
            indicator = custom.getDrawable(R.styleable.IndicatorTabStrip_ttsIndicator);
        indicatorWidthMode = custom.getInt(R.styleable.IndicatorTabStrip_ttsIndicatorWidthMode, indicatorWidthMode);
        indicatorPadding = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsIndicatorPadding,
                indicatorPadding);
        indicatorWidth = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsIndicatorWidth,
                indicatorWidth);
        indicatorHeight = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsIndicatorHeight,
                indicatorHeight);
        if (custom.hasValue(R.styleable.IndicatorTabStrip_ttsInterval))
            interval = custom.getDrawable(R.styleable.IndicatorTabStrip_ttsInterval);
        minItemWidth = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsMinItemWidth, minItemWidth);
        minItemHeight = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsMinItemHeight,
                minItemHeight);
        tagTextSize = custom.getDimensionPixelSize(R.styleable.IndicatorTabStrip_ttsTagTextSize, tagTextSize);
        tagTextColor = custom.getColor(R.styleable.IndicatorTabStrip_ttsTagTextColor, tagTextColor);
        if (custom.hasValue(R.styleable.IndicatorTabStrip_ttsTagBackground))
            tagBackground = custom.getDrawable(R.styleable.IndicatorTabStrip_ttsTagBackground);
        tagMinSizeMode = custom.getInt(R.styleable.IndicatorTabStrip_ttsTagMinSizeMode, tagMinSizeMode);
        tagMinWidth = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsTagMinWidth, tagMinWidth);
        tagMinHeight = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsTagMinHeight, tagMinHeight);
        if (custom.hasValue(R.styleable.IndicatorTabStrip_ttsTagPadding)) {
            final int padding = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsTagPadding, 0);
            tagPaddingLeft = padding;
            tagPaddingTop = padding;
            tagPaddingRight = padding;
            tagPaddingBottom = padding;
        }
        tagPaddingLeft = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsTagPaddingLeft,
                tagPaddingLeft);
        tagPaddingTop = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsTagPaddingTop,
                tagPaddingTop);
        tagPaddingRight = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsTagPaddingRight,
                tagPaddingRight);
        tagPaddingBottom = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsTagPaddingBottom,
                tagPaddingBottom);
        if (custom.hasValue(R.styleable.IndicatorTabStrip_ttsTagMargin)) {
            final int margin = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsTagMargin, 0);
            tagMarginLeft = margin;
            tagMarginTop = margin;
            tagMarginRight = margin;
            tagMarginBottom = margin;
        }
        tagMarginLeft = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsTagMarginLeft,
                tagMarginLeft);
        tagMarginTop = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsTagMarginTop, tagMarginTop);
        tagMarginRight = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsTagMarginRight,
                tagMarginRight);
        tagMarginBottom = custom.getDimensionPixelOffset(R.styleable.IndicatorTabStrip_ttsTagMarginBottom,
                tagMarginBottom);
        custom.recycle();
        setTextSize(textSize);
        if (textColors != null) {
            setTextColor(textColors);
        } else {
            setTextColor(DEFAULT_TEXT_COLOR);
        }
        setTextScale(scale);
        setItemBackground(itemBackground);
        setGradient(gradient);
        setDivider(divider);
        setIndicator(indicator);
        setIndicatorWidthMode(indicatorWidthMode);
        setIndicatorPadding(indicatorPadding);
        setIndicatorWidth(indicatorWidth);
        setIndicatorHeight(indicatorHeight);
        setInterval(interval);
        setMinItemWidth(minItemWidth);
        setMinItemHeight(minItemHeight);
        setTagTextSize(tagTextSize);
        setTagTextColor(tagTextColor);
        setTagBackground(tagBackground);
        setTagMinSizeMode(tagMinSizeMode);
        setTagMinWidth(tagMinWidth);
        setTagMinHeight(tagMinHeight);
        setTagPadding(tagPaddingLeft, tagPaddingTop, tagPaddingRight, tagPaddingBottom);
        setTagMargin(tagMarginLeft, tagMarginTop, tagMarginRight, tagMarginBottom);
    }

    @TargetApi(5)
    private void updateTextPaintDensity() {
        mTextPaint.density = getResources().getDisplayMetrics().density;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mTextPaint.setTextSize(mTextSize);
        Paint.FontMetricsInt metrics = mTextPaint.getFontMetricsInt();
        final int textHeight = metrics.bottom - metrics.top;
        mTextDesc = metrics.bottom;
        int textWidth = 0;
        for (int i = 0; i < getItemCount(); i++) {
            if (getItemText(i) != null) {
                String text = getItemText(i).toString();
                mTextPaint.getTextBounds(text, 0, text.length(), mTextMeasureBounds);
                textWidth = Math.max(textWidth, mTextMeasureBounds.width());
            }
        }
        final int maxTextWidth = mTextScale > 1 ? (int) (Math.ceil((float) textWidth * mTextScale) + 1) : textWidth;
        final int itemBackgroundWith = getMinItemBackgroundWidth();
        final int itemWidth = Math.max(maxTextWidth, Math.max(mMinItemWidth, itemBackgroundWith));
        final int intervalWidth = getIntervalWidth();
        final int totalWidth = itemWidth * getItemCount() + intervalWidth * (getItemCount() - 1)
                + ViewCompat.getPaddingStart(this) + ViewCompat.getPaddingEnd(this);
        final int dividerWidth = mDivider == null ? 0
                : mDivider.getIntrinsicWidth() + ViewCompat.getPaddingStart(this) + ViewCompat.getPaddingEnd(this);
        final int width = Math.max(Math.max(totalWidth, dividerWidth), getSuggestedMinimumWidth());
        final int maxTextHeight = mTextScale > 1 ? (int) (Math.ceil((float) textHeight * mTextScale) + 1)
                : textHeight;
        final int itemBackgroundHeight = getMinItemBackgroundHeight();
        final int intervalHeight = mInterval == null ? 0 : mInterval.getIntrinsicHeight();
        final int dividerHeight = getDividerHeight();
        final int itemHeight = Math.max(Math.max(maxTextHeight, intervalHeight),
                Math.max(mMinItemHeight, itemBackgroundHeight));
        final int height = Math.max(dividerHeight + itemHeight + getPaddingTop() + getPaddingBottom(),
                getSuggestedMinimumHeight());
        setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));
        mTextPaint.setTextSize(mTagTextSize);
        metrics = mTextPaint.getFontMetricsInt();
        mTagTextDesc = metrics.bottom;
    }

    /**
     * ?
     *
     * @return 
     */
    protected int getIntervalWidth() {
        return mInterval == null ? 0 : mInterval.getIntrinsicWidth();
    }

    /**
     * ??
     *
     * @return ?
     */
    protected float getItemWidth() {
        float width = getWidth() - ViewCompat.getPaddingStart(this) - ViewCompat.getPaddingEnd(this)
                - getIntervalWidth() * (getItemCount() - 1);
        return width / getItemCount();
    }

    /**
     * ???
     *
     * @return ??
     */
    protected int getDrawItemWidth() {
        return Math.round(getItemWidth());
    }

    /**
     * ????
     *
     * @return ???
     */
    protected int getLastItemWidth() {
        return getWidth() - ViewCompat.getPaddingStart(this) - ViewCompat.getPaddingEnd(this)
                - getIntervalWidth() * (getItemCount() - 1) - getDrawItemWidth() * (getItemCount() - 1);
    }

    /**
     * ?Divider
     *
     * @return Divider
     */
    protected int getDividerHeight() {
        return mDivider == null ? 0 : mDivider.getIntrinsicHeight();
    }

    /**
     * ??
     *
     * @return ?
     */
    protected int getItemHeight() {
        return getHeight() - getDividerHeight() - getPaddingTop() - getPaddingBottom();
    }

    @Override
    protected void jumpTo(int current) {
        mCurrentPager = current - 1;
        mNextPager = current;
        mOffset = 1;
        invalidate();
    }

    @Override
    protected void gotoLeft(int current, int next, float offset) {
        mCurrentPager = current;
        mNextPager = next;
        mOffset = 1 - offset;
        invalidate();
    }

    @Override
    protected void gotoRight(int current, int next, float offset) {
        mCurrentPager = current;
        mNextPager = next;
        mOffset = offset;
        invalidate();
    }

    @Override
    protected int pointToPosition(float x, float y) {
        final int paddingTop = getPaddingTop();
        final int paddingBottom = getPaddingBottom();
        if (y < paddingTop || y > getWidth() - paddingBottom) {
            return -1;
        }
        int position = -1;
        final float itemWidth = getItemWidth();
        final int intervalWidth = getIntervalWidth();
        for (int i = 0; i < getItemCount(); i++) {
            float l = ViewCompat.getPaddingStart(this) + intervalWidth * i + itemWidth * i;
            float r = l + itemWidth;
            if (x >= l && x <= r) {
                position = i;
                break;
            }
        }
        return position;
    }

    @Override
    protected float getHotspotX(Drawable background, int position, float motionX, float motionY) {
        return motionX - getPaddingLeft() - getIntervalWidth() * position - getItemWidth() * position;
    }

    @Override
    protected float getHotspotY(Drawable background, int position, float motionX, float motionY) {
        return motionY;
    }

    @Override
    protected int getItemCount() {
        if (isInEditMode()) {
            return 3;
        }
        return super.getItemCount();
    }

    @Override
    protected CharSequence getItemText(int position) {
        if (isInEditMode()) {
            return "Tab" + position;
        }
        return super.getItemText(position);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawDivider(canvas);
        drawItem(canvas);
        drawIndicator(canvas);
    }

    /**
     * Divider
     *
     * @param canvas 
     */
    protected void drawDivider(Canvas canvas) {
        if (mDivider == null || mDivider.getIntrinsicHeight() <= 0)
            return;
        final int dividerWidth = mDivider.getIntrinsicWidth() > 0 ? mDivider.getIntrinsicWidth()
                : getWidth() - ViewCompat.getPaddingStart(this) - ViewCompat.getPaddingEnd(this);
        final int dividerHeight = mDivider.getIntrinsicHeight();
        mDivider.setBounds(0, 0, dividerWidth, dividerHeight);
        final float moveX = ViewCompat.getPaddingStart(this);
        final float moveY = getItemHeight() + getPaddingTop();
        canvas.save();
        canvas.translate(moveX, moveY);
        mDivider.draw(canvas);
        canvas.restore();
    }

    /**
     * ?
     *
     * @param canvas 
     */
    protected void drawItem(Canvas canvas) {
        final int itemWidth = getDrawItemWidth();
        final int itemHeight = getItemHeight();
        for (int position = 0; position < getItemCount(); position++) {
            drawItemBackground(canvas, position, itemWidth, itemHeight);
            drawItemGradient(canvas, position, itemWidth, itemHeight);
            drawInterval(canvas, position, itemWidth, itemHeight);
            drawText(canvas, position, itemWidth, itemHeight);
            drawTag(canvas, position, itemWidth, itemHeight);
        }
    }

    /**
     * 
     *
     * @param canvas     
     * @param position   ???
     * @param itemWidth  ?
     * @param itemHeight ?
     */
    protected void drawItemBackground(Canvas canvas, int position, int itemWidth, int itemHeight) {
        if (!hasItemBackgrounds())
            return;
        Drawable tag = getItemBackground(position);
        if (position == getItemCount() - 1) {
            int restWidth = getLastItemWidth();
            tag.setBounds(0, 0, restWidth, itemHeight);
        } else {
            tag.setBounds(0, 0, itemWidth, itemHeight);
        }
        final float moveX = ViewCompat.getPaddingStart(this) + (itemWidth + getIntervalWidth()) * position;
        final float moveY = getPaddingTop();
        canvas.save();
        canvas.translate(moveX, moveY);
        tag.draw(canvas);
        canvas.restore();
    }

    /**
     * ??
     *
     * @param canvas     
     * @param position   ???
     * @param itemWidth  ?
     * @param itemHeight ?
     */
    protected void drawItemGradient(Canvas canvas, int position, int itemWidth, int itemHeight) {
        if (mGradient == null || !mGradient.isStateful())
            return;
        final int normalColor = mGradient.getDefaultColor();
        final int selectedColor = mGradient.getColorForState(SELECTED_STATE_SET, normalColor);
        if (position == mNextPager) {
            mTextPaint.setColor(getColor(normalColor, selectedColor, mOffset));
        } else if (position == mCurrentPager) {
            mTextPaint.setColor(getColor(normalColor, selectedColor, 1 - mOffset));
        } else {
            mTextPaint.setColor(normalColor);
        }
        final float moveX = ViewCompat.getPaddingStart(this) + (itemWidth + getIntervalWidth()) * position;
        final float moveY = getPaddingTop();
        final int restWidth = position == getItemCount() - 1 ? getLastItemWidth() : itemWidth;
        canvas.save();
        canvas.translate(moveX, moveY);
        canvas.drawRect(0, 0, restWidth, itemHeight, mTextPaint);
        canvas.restore();
    }

    /**
     * 
     *
     * @param canvas     
     * @param position   ???
     * @param itemWidth  ?
     * @param itemHeight ?
     */
    protected void drawInterval(Canvas canvas, int position, int itemWidth, int itemHeight) {
        if (mInterval == null || mInterval.getIntrinsicWidth() <= 0 || position == getItemCount() - 1)
            return;
        final int intervalHeight = mInterval.getIntrinsicHeight() <= 0 ? itemHeight
                : mInterval.getIntrinsicHeight();
        mInterval.setBounds(0, 0, getIntervalWidth(), intervalHeight);
        final int moveX = ViewCompat.getPaddingStart(this) + itemWidth * position;
        final float moveY = getPaddingTop() + (itemHeight - intervalHeight) * 0.5f;
        canvas.save();
        canvas.translate(moveX, moveY);
        mInterval.draw(canvas);
        canvas.restore();
    }

    /**
     * 
     *
     * @param canvas     
     * @param position   ???
     * @param itemWidth  ?
     * @param itemHeight ?
     */
    protected void drawText(Canvas canvas, int position, int itemWidth, int itemHeight) {
        if (getItemText(position) == null)
            return;
        String text = getItemText(position).toString();
        if (text.length() <= 0)
            return;
        mTextPaint.setTextSize(mTextSize);
        if (mTextColor == null) {
            mTextPaint.setColor(DEFAULT_TEXT_COLOR);
        } else {
            final int normalColor = mTextColor.getDefaultColor();
            final int selectedColor = mTextColor.getColorForState(SELECTED_STATE_SET, normalColor);
            if (position == mNextPager) {
                mTextPaint.setColor(getColor(normalColor, selectedColor, mOffset));
            } else if (position == mCurrentPager) {
                mTextPaint.setColor(getColor(normalColor, selectedColor, 1 - mOffset));
            } else {
                mTextPaint.setColor(normalColor);
            }
        }
        final float centerX = ViewCompat.getPaddingStart(this)
                + (itemWidth + getIntervalWidth()) * ((float) position + 0.5f);
        final float centerY = getPaddingTop() + itemHeight * 0.5f;
        float scale;
        if (position == mNextPager) {
            scale = 1 + (mTextScale - 1) * mOffset;
        } else if (position == mCurrentPager) {
            scale = 1 + (mTextScale - 1) * (1 - mOffset);
        } else {
            scale = 1;
        }
        canvas.save();
        canvas.translate(centerX, centerY + mTextDesc);
        if (scale != 1) {
            canvas.scale(scale, scale, 0, -mTextDesc);
        }
        canvas.drawText(text, 0, 0, mTextPaint);
        canvas.restore();
    }

    /**
     * Tag
     *
     * @param canvas     
     * @param position   ???
     * @param itemWidth  ?
     * @param itemHeight ?
     */
    @SuppressWarnings("unused")
    protected void drawTag(Canvas canvas, int position, int itemWidth, int itemHeight) {
        if (mAdapter == null || !mAdapter.isTagEnable(position))
            return;
        String text = mAdapter.getTag(position) == null ? "" : mAdapter.getTag(position);
        mTextPaint.setTextSize(mTagTextSize);
        mTextPaint.setColor(mTagTextColor);
        mTextPaint.getTextBounds(text, 0, text.length(), mTextMeasureBounds);
        final int textWidth = mTextMeasureBounds.width();
        final int textHeight = mTextMeasureBounds.height();
        final int tagBackgroundWidth = mTagBackground == null ? 0 : mTagBackground.getIntrinsicWidth();
        final int tagBackgroundHeight = mTagBackground == null ? 0 : mTagBackground.getIntrinsicHeight();
        int tagWidth;
        int tagHeight;
        switch (mTagMinSizeMode) {
        default:
        case TAG_MIN_SIZE_MODE_HAS_TEXT:
            if ("".equals(text)) {
                tagWidth = Math.min(mTagMinWidth, tagBackgroundWidth);
                tagHeight = Math.min(mTagMinHeight, tagBackgroundHeight);
                break;
            }
        case TAG_MIN_SIZE_MODE_ALWAYS:
            tagWidth = Math.max(textWidth + mTagLocation.getPaddingLeft() + mTagLocation.getPaddingRight(),
                    Math.max(mTagMinWidth, tagBackgroundWidth));
            tagHeight = Math.max(textHeight + mTagLocation.getPaddingTop() + mTagLocation.getPaddingBottom(),
                    Math.max(mTagMinHeight, tagBackgroundHeight));
            break;
        }
        final int rightTabX = position == getItemCount() - 1 ? getWidth() - ViewCompat.getPaddingEnd(this)
                : ViewCompat.getPaddingStart(this) + (itemWidth + getIntervalWidth()) * position + itemWidth;
        final int rightTagX = rightTabX - mTagLocation.getMarginRight();
        final int tagY = getPaddingTop() + mTagLocation.getMarginTop();
        final int leftTagX = rightTagX - tagWidth;
        final float tagCenterX = leftTagX + tagWidth * 0.5f;
        final float tagCenterY = tagY + tagWidth * 0.5f;
        canvas.save();
        if (mTagBackground != null) {
            mTagBackground.setBounds(0, 0, tagWidth, tagHeight);
            canvas.translate(leftTagX, tagY);
            mTagBackground.draw(canvas);
            if (!"".equals(text)) {
                canvas.translate(tagWidth * 0.5f, tagHeight * 0.5f + mTagTextDesc);
                canvas.drawText(text, 0, 0, mTextPaint);
            }
        } else {
            canvas.translate(tagCenterX, tagCenterY + mTagTextDesc);
            canvas.drawText(text, 0, 0, mTextPaint);
        }
        canvas.restore();
    }

    /**
     * 
     *
     * @param canvas 
     */
    protected void drawIndicator(Canvas canvas) {
        if (mIndicator == null)
            return;
        final int indicatorWidth = getIndicatorWidth();
        final int indicatorHeight = getIndicatorHeight();
        if (indicatorWidth <= 0 || indicatorHeight <= 0)
            return;
        mIndicator.setBounds(0, 0, indicatorWidth, indicatorHeight);
        final float widthWithInterval = getItemWidth() + getIntervalWidth();
        final float currentCenter = ViewCompat.getPaddingStart(this) + mCurrentPager * widthWithInterval
                + widthWithInterval * 0.5f;
        final float nextCenter = ViewCompat.getPaddingStart(this) + mNextPager * widthWithInterval
                + widthWithInterval * 0.5f;
        final float moveCenter = currentCenter + (nextCenter - currentCenter) * mOffset;
        final float moveX = moveCenter - indicatorWidth * 0.5f;
        final float moveY = getHeight() - getPaddingBottom() - getDividerHeight() - indicatorHeight;
        canvas.save();
        canvas.translate(moveX, moveY);
        mIndicator.draw(canvas);
        canvas.restore();
    }

    /**
     * ??
     *
     * @return ?
     */
    @SuppressWarnings("unused")
    public float getTextSize() {
        return mTextSize;
    }

    /**
     * ?
     *
     * @param size ?
     */
    public void setTextSize(float size) {
        if (mTextSize != size) {
            mTextSize = size;
            requestLayout();
            invalidate();
        }
    }

    /**
     * ?
     *
     * @return 
     */
    @SuppressWarnings("unused")
    public ColorStateList getTextColor() {
        return mTextColor;
    }

    /**
     * 
     *
     * @param color 
     */
    public void setTextColor(ColorStateList color) {
        if (color != null && color != mTextColor) {
            mTextColor = color;
            invalidate();
        }
    }

    /**
     * 
     *
     * @param color 
     */
    public void setTextColor(@ColorInt int color) {
        setTextColor(ColorStateList.valueOf(color));
    }

    /**
     * ?
     *
     * @return 
     */
    @SuppressWarnings("unused")
    public float getTextScale() {
        return mTextScale;
    }

    /**
     * 
     *
     * @param scale 
     */
    public void setTextScale(float scale) {
        if (scale > 0 && mTextScale != scale) {
            mTextScale = scale;
            requestLayout();
            invalidate();
        }
    }

    /**
     * ???
     *
     * @return ??
     */
    @SuppressWarnings("unused")
    public ColorStateList getGradient() {
        return mGradient;
    }

    /**
     * ??
     *
     * @param gradient ??
     */
    public void setGradient(ColorStateList gradient) {
        if (gradient == null) {
            mGradient = null;
            invalidate();
        } else if (mGradient != gradient && gradient.isStateful()) {
            mGradient = gradient;
            invalidate();
        } else {
            setItemBackground(new ColorDrawable(gradient.getDefaultColor()));
        }
    }

    /**
     * ??
     *
     * @param gradient ??
     */
    @SuppressWarnings("unused")
    public void setGradient(@ColorRes int gradient) {
        setGradient(ContextCompat.getColorStateList(getContext(), gradient));
    }

    /**
     * ?Divider
     *
     * @return Divider
     */
    @SuppressWarnings("unused")
    public Drawable getDivider() {
        return mDivider;
    }

    /**
     * Divider
     *
     * @param divider Divider
     */
    public void setDivider(Drawable divider) {
        if (mDivider != divider) {
            mDivider = divider;
            invalidate();
        }
    }

    /**
     * Divider
     *
     * @param divider Divider
     */
    @SuppressWarnings("unused")
    public void setDivider(@DrawableRes int divider) {
        setDivider(ContextCompat.getDrawable(getContext(), divider));
    }

    /**
     * ?
     *
     * @return 
     */
    @SuppressWarnings("unused")
    public Drawable getIndicator() {
        return mIndicator;
    }

    /**
     * 
     *
     * @param indicator 
     */
    public void setIndicator(Drawable indicator) {
        if (mIndicator != indicator) {
            mIndicator = indicator;
            invalidate();
        }
    }

    /**
     * 
     *
     * @param indicator 
     */
    @SuppressWarnings("unused")
    public void setIndicator(@DrawableRes int indicator) {
        setIndicator(ContextCompat.getDrawable(getContext(), indicator));
    }

    /**
     * ??
     *
     * @return ?
     */
    @SuppressWarnings("unused")
    public int getIndicatorWidthMode() {
        return mIndicatorWidthMode;
    }

    /**
     * ??
     *
     * @param mode ??
     */
    public void setIndicatorWidthMode(int mode) {
        if (mode != INDICATOR_WIDTH_MODE_SET && mode != INDICATOR_WIDTH_MODE_TAB)
            return;
        if (mIndicatorWidthMode != mode) {
            mIndicatorWidthMode = mode;
            invalidate();
        }
    }

    /**
     * ?Padding
     *
     * @return Padding
     */
    @SuppressWarnings("unused")
    public int getIndicatorPadding() {
        return mIndicatorPadding;
    }

    /**
     * Padding
     *
     * @param padding Padding
     */
    public void setIndicatorPadding(int padding) {
        if (mIndicatorPadding != padding) {
            mIndicatorPadding = padding;
            invalidate();
        }
    }

    /**
     * ?
     *
     * @return 
     */
    public int getIndicatorWidth() {
        if (mIndicator == null)
            return 0;
        switch (mIndicatorWidthMode) {
        default:
        case INDICATOR_WIDTH_MODE_SET:
            return mIndicatorWidth == INDICATOR_WIDTH_BY_DRAWABLE ? mIndicator.getIntrinsicWidth()
                    : mIndicatorWidth;
        case INDICATOR_WIDTH_MODE_TAB:
            return getDrawItemWidth() - mIndicatorPadding * 2;
        }
    }

    /**
     * 
     *
     * @param width 
     */
    public void setIndicatorWidth(int width) {
        if (width < INDICATOR_WIDTH_BY_DRAWABLE)
            return;
        if (mIndicatorWidth != width) {
            mIndicatorWidth = width;
            invalidate();
        }
    }

    /**
     * ?
     *
     * @return 
     */
    public int getIndicatorHeight() {
        if (mIndicator == null)
            return 0;
        return mIndicatorHeight == INDICATOR_HEIGHT_BY_DRAWABLE ? mIndicator.getIntrinsicHeight()
                : mIndicatorHeight;
    }

    /**
     * 
     *
     * @param height 
     */
    public void setIndicatorHeight(int height) {
        if (height < INDICATOR_HEIGHT_BY_DRAWABLE)
            return;
        if (mIndicatorHeight != height) {
            mIndicatorHeight = height;
            invalidate();
        }
    }

    /**
     * ??
     *
     * @return ?
     */
    @SuppressWarnings("unused")
    public Drawable getInterval() {
        return mInterval;
    }

    /**
     * ?
     *
     * @param interval ?
     */
    public void setInterval(Drawable interval) {
        if (mInterval != interval) {
            mInterval = interval;
            invalidate();
        }
    }

    /**
     * ?
     *
     * @param interval ?
     */
    @SuppressWarnings("unused")
    public void setInterval(@DrawableRes int interval) {
        setInterval(ContextCompat.getDrawable(getContext(), interval));
    }

    /**
     * ???
     *
     * @return ??
     */
    @SuppressWarnings("unused")
    public int getMinItemWidth() {
        return mMinItemWidth;
    }

    /**
     * ??
     *
     * @param width ??
     */
    public void setMinItemWidth(int width) {
        if (width >= 0 && width != mMinItemWidth) {
            mMinItemWidth = width;
            requestLayout();
            invalidate();
        }
    }

    /**
     * ???
     *
     * @return ??
     */
    @SuppressWarnings("unused")
    public int getMinItemHeight() {
        return mMinItemHeight;
    }

    /**
     * ??
     *
     * @param height ??
     */
    public void setMinItemHeight(int height) {
        if (height >= 0 && height != mMinItemHeight) {
            mMinItemHeight = height;
            requestLayout();
            invalidate();
        }
    }

    /**
     * ?Adapter
     *
     * @return Adapter
     */
    @SuppressWarnings("unused")
    public ItemTabAdapter getAdapter() {
        return mAdapter;
    }

    /**
     * Adapter
     *
     * @param adapter Adapter
     */
    public void setAdapter(ItemTabAdapter adapter) {
        if (mAdapter != adapter) {
            mAdapter = adapter;
            invalidate();

        }
    }

    /**
     * ?Tag?
     *
     * @return Tag?
     */
    @SuppressWarnings("unused")
    public float getTagTextSize() {
        return mTagTextSize;
    }

    /**
     * Tag?
     *
     * @param size Tag?
     */
    public void setTagTextSize(float size) {
        if (mTagTextSize != size) {
            mTagTextSize = size;
            invalidate();
        }
    }

    /**
     * ?Tag
     *
     * @return Tag
     */
    @SuppressWarnings("unused")
    public int getTagTextColor() {
        return mTagTextColor;
    }

    /**
     * Tag
     *
     * @param color Tag
     */
    public void setTagTextColor(@ColorInt int color) {
        if (mTagTextColor != color) {
            mTagTextColor = color;
            invalidate();
        }
    }

    /**
     * ?Tag
     *
     * @return Tag
     */
    @SuppressWarnings("unused")
    public Drawable getTagBackground() {
        return mTagBackground;
    }

    /**
     * Tag
     *
     * @param background Tag
     */
    public void setTagBackground(Drawable background) {
        if (mTagBackground != background) {
            mTagBackground = background;
            invalidate();
        }
    }

    /**
     * Tag
     *
     * @param background Tag
     */
    @SuppressWarnings("unused")
    public void setTagBackground(@DrawableRes int background) {
        setTagBackground(ContextCompat.getDrawable(getContext(), background));
    }

    /**
     * ?Tag??
     *
     * @return Tag??
     */
    @SuppressWarnings("unused")
    public int getTagMinSizeMode() {
        return mTagMinSizeMode;
    }

    /**
     * Tag??
     *
     * @param mode Tag??
     */
    public void setTagMinSizeMode(int mode) {
        if (mode != TAG_MIN_SIZE_MODE_HAS_TEXT && mode != TAG_MIN_SIZE_MODE_ALWAYS)
            return;
        if (mTagMinSizeMode != mode) {
            mTagMinSizeMode = mode;
            invalidate();
        }
    }

    /**
     * ?Tag?
     *
     * @return Tag?
     */
    @SuppressWarnings("unused")
    public int getTagMinWidth() {
        return mTagMinWidth;
    }

    /**
     * Tag?
     *
     * @param width Tag?
     */
    public void setTagMinWidth(int width) {
        if (width >= 0 && mTagMinWidth != width) {
            mTagMinWidth = width;
            invalidate();
        }
    }

    /**
     * ?Tag?
     *
     * @return Tag?
     */
    @SuppressWarnings("unused")
    public int getTagMinHeight() {
        return mTagMinHeight;
    }

    /**
     * Tag?
     *
     * @param height Tag?
     */
    public void setTagMinHeight(int height) {
        if (height >= 0 && mTagMinHeight != height) {
            mTagMinHeight = height;
            invalidate();
        }
    }

    /**
     * Tag Padding
     *
     * @param left   
     * @param top    
     * @param right  ?
     * @param bottom 
     */
    public void setTagPadding(int left, int top, int right, int bottom) {
        if (mTagLocation.setPadding(left, top, right, bottom)) {
            invalidate();
        }
    }

    /**
     * Tag Margin
     *
     * @param left   
     * @param top    
     * @param right  ?
     * @param bottom 
     */
    public void setTagMargin(int left, int top, int right, int bottom) {
        if (mTagLocation.setMargin(left, top, right, bottom)) {
            invalidate();
        }
    }
}