炫酷tab栏--第三方开源--NavigationTabStrip

github下载地址:https://github.com/DevLight-Mobile-Agency/NavigationTabStrip

这个开源项目很强大,只是一个自定义的控件,只有一个类

/*
 * Copyright (C) 2015 Basil Miller
 *
 * 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 testnavigationtabstrip.zzw.com.tablib;

import android.animation.Animator;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.view.ViewPager;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.widget.Scroller;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Random;

/**
 * Created by GIGAMOLE on 24.03.2016.
 */
public class NavigationTabStrip extends View implements ViewPager.OnPageChangeListener {

    // NTS constants
    private final static String PREVIEW_TITLE = "Title";
    private final static int INVALID_INDEX = -1;

    // Default variables
    private final static int DEFAULT_ANIMATION_DURATION = 350;
    private final static float DEFAULT_STRIP_FACTOR = 2.5F;
    private final static float DEFAULT_STRIP_WEIGHT = 10.0F;
    private final static float DEFAULT_CORNER_RADIUS = 5.0F;
    private final static int DEFAULT_INACTIVE_COLOR = Color.GRAY;
    private final static int DEFAULT_ACTIVE_COLOR = Color.WHITE;
    private final static int DEFAULT_STRIP_COLOR = Color.RED;
    private final static int DEFAULT_TITLE_SIZE = 0;

    // Title size offer to view height
    private final static float TITLE_SIZE_FRACTION = 0.35F;

    // Max and min fraction
    private final static float MIN_FRACTION = 0.0F;
    private final static float MAX_FRACTION = 1.0F;

    // NTS and strip bounds
    private final RectF mBounds = new RectF();
    private final RectF mStripBounds = new RectF();
    private final Rect mTitleBounds = new Rect();

    // Main paint
    private final Paint mStripPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG) {
        {
            setStyle(Style.FILL);
        }
    };

    // Paint for tav title
    private final Paint mTitlePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG) {
        {
            setTextAlign(Align.CENTER);
        }
    };

    // Variables for animator
    private final ValueAnimator mAnimator = new ValueAnimator();
    private final ArgbEvaluator mColorEvaluator = new ArgbEvaluator();
    private final ResizeInterpolator mResizeInterpolator = new ResizeInterpolator();
    private int mAnimationDuration;

    // NTS titles
    private String[] mTitles;

    // Variables for ViewPager
    private ViewPager mViewPager;
    private ViewPager.OnPageChangeListener mOnPageChangeListener;
    private int mScrollState;

    // Tab listener
    private OnTabStripSelectedIndexListener mOnTabStripSelectedIndexListener;
    private ValueAnimator.AnimatorListener mAnimatorListener;

    // Variables for sizes
    private float mTabSize;
    // Tab title size and margin
    private float mTitleSize;
    // Strip type and gravity
    private StripType mStripType;
    private StripGravity mStripGravity;
    // Corners radius for rect mode
    private float mStripWeight;
    private float mCornersRadius;

    // Indexes
    private int mLastIndex = INVALID_INDEX;
    private int mIndex = INVALID_INDEX;
    // General fraction value
    private float mFraction;

    // Coordinates of strip
    private float mStartStripX;
    private float mEndStripX;
    private float mStripLeft;
    private float mStripRight;

    // Detect if is bar mode or indicator pager mode
    private boolean mIsViewPagerMode;
    // Detect if we move from left to right
    private boolean mIsResizeIn;
    // Detect if we get action down event
    private boolean mIsActionDown;
    // Detect if we get action down event on strip
    private boolean mIsTabActionDown;
    // Detect when we set index from tab bar nor from ViewPager
    private boolean mIsSetIndexFromTabBar;

    // Color variables
    private int mInactiveColor;
    private int mActiveColor;

    // Custom typeface
    private Typeface mTypeface;

    public NavigationTabStrip(final Context context) {
        this(context, null);
    }

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

    public NavigationTabStrip(final Context context, final AttributeSet attrs, final int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //Init NTS

        // Always draw
        setWillNotDraw(false);
        // More speed!
        setLayerType(LAYER_TYPE_HARDWARE, null);

        final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NavigationTabStrip);
        try {
            setStripColor(
                    typedArray.getColor(R.styleable.NavigationTabStrip_nts_color, DEFAULT_STRIP_COLOR)
            );
            setTitleSize(
                    typedArray.getDimension(R.styleable.NavigationTabStrip_nts_size, DEFAULT_TITLE_SIZE)
            );
            setStripWeight(
                    typedArray.getDimension(R.styleable.NavigationTabStrip_nts_weight, DEFAULT_STRIP_WEIGHT)
            );
            setStripFactor(
                    typedArray.getFloat(R.styleable.NavigationTabStrip_nts_factor, DEFAULT_STRIP_FACTOR)
            );
            setStripType(
                    typedArray.getInt(R.styleable.NavigationTabStrip_nts_type, StripType.LINE_INDEX)
            );
            setStripGravity(
                    typedArray.getInt(R.styleable.NavigationTabStrip_nts_gravity, StripGravity.BOTTOM_INDEX)
            );

            setTypeface(typedArray.getString(R.styleable.NavigationTabStrip_nts_typeface));
            setInactiveColor(
                    typedArray.getColor(
                            R.styleable.NavigationTabStrip_nts_inactive_color, DEFAULT_INACTIVE_COLOR
                    )
            );
            setActiveColor(
                    typedArray.getColor(
                            R.styleable.NavigationTabStrip_nts_active_color, DEFAULT_ACTIVE_COLOR
                    )
            );
            setAnimationDuration(
                    typedArray.getInteger(
                            R.styleable.NavigationTabStrip_nts_animation_duration, DEFAULT_ANIMATION_DURATION
                    )
            );
            setCornersRadius(
                    typedArray.getDimension(
                            R.styleable.NavigationTabStrip_nts_corners_radius, DEFAULT_CORNER_RADIUS
                    )
            );

            // Get titles
            String[] titles = null;
            try {
                final int titlesResId = typedArray.getResourceId(
                        R.styleable.NavigationTabStrip_nts_titles, 0
                );
                titles = titlesResId == 0 ? null :
                        typedArray.getResources().getStringArray(titlesResId);
            } catch (Exception exception) {
                titles = null;
                exception.printStackTrace();
            } finally {
                if (titles == null) {
                    if (isInEditMode()) {
                        titles = new String[new Random().nextInt(5) + 1];
                        Arrays.fill(titles, PREVIEW_TITLE);
                    } else titles = new String[0];
                }

                setTitles(titles);
            }

            // Init animator
            mAnimator.setFloatValues(MIN_FRACTION, MAX_FRACTION);
            mAnimator.setInterpolator(new LinearInterpolator());
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(final ValueAnimator animation) {
                    updateIndicatorPosition((Float) animation.getAnimatedValue());
                }
            });
        } finally {
            typedArray.recycle();
        }
    }

    public int getAnimationDuration() {
        return mAnimationDuration;
    }

    public void setAnimationDuration(final int animationDuration) {
        mAnimationDuration = animationDuration;
        mAnimator.setDuration(mAnimationDuration);
        resetScroller();
    }

    public String[] getTitles() {
        return mTitles;
    }

    public void setTitles(final int... titleResIds) {
        final String[] titles = new String[titleResIds.length];
        for (int i = 0; i < titleResIds.length; i++)
            titles[i] = getResources().getString(titleResIds[i]);
        setTitles(titles);
    }

    public void setTitles(final String... titles) {
        for (int i = 0; i < titles.length; i++) titles[i] = titles[i].toUpperCase();
        mTitles = titles;
        requestLayout();
    }

    public int getStripColor() {
        return mStripPaint.getColor();
    }

    public void setStripColor(final int color) {
        mStripPaint.setColor(color);
        postInvalidate();
    }

    public void setStripWeight(final float stripWeight) {
        mStripWeight = stripWeight;
        requestLayout();
    }

    public StripGravity getStripGravity() {
        return mStripGravity;
    }

    private void setStripGravity(final int index) {
        switch (index) {
            case StripGravity.TOP_INDEX:
                setStripGravity(StripGravity.TOP);
                break;
            case StripGravity.BOTTOM_INDEX:
            default:
                setStripGravity(StripGravity.BOTTOM);
        }
    }

    public void setStripGravity(final StripGravity stripGravity) {
        mStripGravity = stripGravity;
        requestLayout();
    }

    public StripType getStripType() {
        return mStripType;
    }

    private void setStripType(final int index) {
        switch (index) {
            case StripType.POINT_INDEX:
                setStripType(StripType.POINT);
                break;
            case StripType.LINE_INDEX:
            default:
                setStripType(StripType.LINE);
        }
    }

    public void setStripType(final StripType stripType) {
        mStripType = stripType;
        requestLayout();
    }

    public float getStripFactor() {
        return mResizeInterpolator.getFactor();
    }

    public void setStripFactor(final float factor) {
        mResizeInterpolator.setFactor(factor);
    }

    public Typeface getTypeface() {
        return mTypeface;
    }

    public void setTypeface(final String typeface) {
        Typeface tempTypeface;
        try {
            tempTypeface = Typeface.createFromAsset(getContext().getAssets(), typeface);
        } catch (Exception e) {
            tempTypeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
            e.printStackTrace();
        }

        setTypeface(tempTypeface);
    }

    public void setTypeface(final Typeface typeface) {
        mTypeface = typeface;
        mTitlePaint.setTypeface(typeface);
        postInvalidate();
    }

    public int getActiveColor() {
        return mActiveColor;
    }

    public void setActiveColor(final int activeColor) {
        mActiveColor = activeColor;
        postInvalidate();
    }

    public int getInactiveColor() {
        return mInactiveColor;
    }

    public void setInactiveColor(final int inactiveColor) {
        mInactiveColor = inactiveColor;
        postInvalidate();
    }

    public float getCornersRadius() {
        return mCornersRadius;
    }

    public void setCornersRadius(final float cornersRadius) {
        mCornersRadius = cornersRadius;
        postInvalidate();
    }

    public float getTitleSize() {
        return mTitleSize;
    }

    public void setTitleSize(final float titleSize) {
        mTitleSize = titleSize;
        mTitlePaint.setTextSize(titleSize);
        postInvalidate();
    }

    public OnTabStripSelectedIndexListener getOnTabStripSelectedIndexListener() {
        return mOnTabStripSelectedIndexListener;
    }

    // Set on tab bar selected index listener where you can trigger action onStart or onEnd
    public void setOnTabStripSelectedIndexListener(final OnTabStripSelectedIndexListener onTabStripSelectedIndexListener) {
        mOnTabStripSelectedIndexListener = onTabStripSelectedIndexListener;

        if (mAnimatorListener == null)
            mAnimatorListener = new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(final Animator animation) {
                    if (mOnTabStripSelectedIndexListener != null)
                        mOnTabStripSelectedIndexListener.onStartTabSelected(mTitles[mIndex], mIndex);

                    animation.removeListener(this);
                    animation.addListener(this);
                }

                @Override
                public void onAnimationEnd(final Animator animation) {
                    if (mIsViewPagerMode) return;

                    animation.removeListener(this);
                    animation.addListener(this);

                    if (mOnTabStripSelectedIndexListener != null)
                        mOnTabStripSelectedIndexListener.onEndTabSelected(mTitles[mIndex], mIndex);
                }

                @Override
                public void onAnimationCancel(final Animator animation) {

                }

                @Override
                public void onAnimationRepeat(final Animator animation) {

                }
            };
        mAnimator.removeListener(mAnimatorListener);
        mAnimator.addListener(mAnimatorListener);
    }

    public void setViewPager(final ViewPager viewPager) {
        // Detect whether ViewPager mode
        if (viewPager == null) {
            mIsViewPagerMode = false;
            return;
        }

        if (mViewPager == viewPager) return;
        if (mViewPager != null) mViewPager.setOnPageChangeListener(null);
        if (viewPager.getAdapter() == null)
            throw new IllegalStateException("ViewPager does not provide adapter instance.");

        mIsViewPagerMode = true;
        mViewPager = viewPager;
        mViewPager.addOnPageChangeListener(this);

        resetScroller();
        postInvalidate();
    }

    public void setViewPager(final ViewPager viewPager, int index) {
        setViewPager(viewPager);

        mIndex = index;
        if (mIsViewPagerMode) mViewPager.setCurrentItem(index, true);
        postInvalidate();
    }

    // Reset scroller and reset scroll duration equals to animation duration
    private void resetScroller() {
        if (mViewPager == null) return;
        try {
            final Field scrollerField = ViewPager.class.getDeclaredField("mScroller");
            scrollerField.setAccessible(true);
            final ResizeViewPagerScroller scroller = new ResizeViewPagerScroller(getContext());
            scrollerField.set(mViewPager, scroller);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setOnPageChangeListener(final ViewPager.OnPageChangeListener listener) {
        mOnPageChangeListener = listener;
    }

    public int getTabIndex() {
        return mIndex;
    }

    public void setTabIndex(int index) {
        setTabIndex(index, false);
    }

    // Set tab index from touch or programmatically
    public void setTabIndex(int index, boolean force) {
        if (mAnimator.isRunning()) return;
        if (mTitles.length == 0) return;

        // This check gives us opportunity to have an non selected tab
        if (mIndex == INVALID_INDEX) force = true;

        // Detect if last is the same
        if (index == mIndex) return;

        // Snap index to tabs size
        index = Math.max(0, Math.min(index, mTitles.length - 1));

        mIsResizeIn = index < mIndex;
        mLastIndex = mIndex;
        mIndex = index;

        mIsSetIndexFromTabBar = true;
        if (mIsViewPagerMode) {
            if (mViewPager == null) throw new IllegalStateException("ViewPager is null.");
            mViewPager.setCurrentItem(index, true);
        }

        // Set startX and endX for animation, where we animate two sides of rect with different interpolation
        mStartStripX = mStripLeft;
        mEndStripX = (mIndex * mTabSize) + (mStripType == StripType.POINT ? mTabSize * 0.5F : 0.0F);

        // If it force, so update immediately, else animate
        // This happens if we set index onCreate or something like this
        // You can use force param or call this method in some post()
        if (force) updateIndicatorPosition(MAX_FRACTION);
        else mAnimator.start();
    }

    private void updateIndicatorPosition(final float fraction) {
        // Update general fraction
        mFraction = fraction;

        // Set the strip left side coordinate
        mStripLeft =
                mStartStripX + (mResizeInterpolator.getResizeInterpolation(fraction, mIsResizeIn) *
                        (mEndStripX - mStartStripX));
        // Set the strip right side coordinate
        mStripRight =
                (mStartStripX + (mStripType == StripType.LINE ? mTabSize : mStripWeight)) +
                        (mResizeInterpolator.getResizeInterpolation(fraction, !mIsResizeIn) *
                                (mEndStripX - mStartStripX));

        // Update NTS
        postInvalidate();
    }

    // Update NTS
    private void notifyDataSetChanged() {
        postInvalidate();
    }

    @Override
    public boolean onTouchEvent(final MotionEvent event) {
        // Return if animation is running
        if (mAnimator.isRunning()) return true;
        // If is not idle state, return
        if (mScrollState != ViewPager.SCROLL_STATE_IDLE) return true;

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // Action down touch
                mIsActionDown = true;
                if (!mIsViewPagerMode) break;
                // Detect if we touch down on tab, later to move
                mIsTabActionDown = (int) (event.getX() / mTabSize) == mIndex;
                break;
            case MotionEvent.ACTION_MOVE:
                // If tab touched, so move
                if (mIsTabActionDown) {
                    mViewPager.setCurrentItem((int) (event.getX() / mTabSize), true);
                    break;
                }
                if (mIsActionDown) break;
            case MotionEvent.ACTION_UP:
                // Press up and set tab index relative to current coordinate
                if (mIsActionDown) setTabIndex((int) (event.getX() / mTabSize));
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_OUTSIDE:
            default:
                // Reset action touch variables
                mIsTabActionDown = false;
                mIsActionDown = false;
                break;
        }

        return true;
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // Get measure size
        final float width = MeasureSpec.getSize(widthMeasureSpec);
        final float height = MeasureSpec.getSize(heightMeasureSpec);

        // Set bounds for NTS
        mBounds.set(0.0F, 0.0F, width, height);

        if (mTitles.length == 0 || width == 0 || height == 0) return;

        // Get smaller side
        mTabSize = width / (float) mTitles.length;
        if ((int) mTitleSize == DEFAULT_TITLE_SIZE)
            setTitleSize((height - mStripWeight) * TITLE_SIZE_FRACTION);

        // Set start position of strip for preview or on start
        if (isInEditMode() || !mIsViewPagerMode) {
            mIsSetIndexFromTabBar = true;

            // Set random in preview mode
            if (isInEditMode()) mIndex = new Random().nextInt(mTitles.length);

            mStartStripX =
                    (mIndex * mTabSize) + (mStripType == StripType.POINT ? mTabSize * 0.5F : 0.0F);
            mEndStripX = mStartStripX;
            updateIndicatorPosition(MAX_FRACTION);
        }
    }

    @Override
    protected void onDraw(final Canvas canvas) {
        // Set bound of strip
        mStripBounds.set(
                mStripLeft - (mStripType == StripType.POINT ? mStripWeight * 0.5F : 0.0F),
                mStripGravity == StripGravity.BOTTOM ? mBounds.height() - mStripWeight : 0.0F,
                mStripRight - (mStripType == StripType.POINT ? mStripWeight * 0.5F : 0.0F),
                mStripGravity == StripGravity.BOTTOM ? mBounds.height() : mStripWeight
        );

        // Draw strip
        if (mCornersRadius == 0) canvas.drawRect(mStripBounds, mStripPaint);
        else canvas.drawRoundRect(mStripBounds, mCornersRadius, mCornersRadius, mStripPaint);

        // Draw tab titles
        for (int i = 0; i < mTitles.length; i++) {
            final String title = mTitles[i];

            final float leftTitleOffset = (mTabSize * i) + (mTabSize * 0.5F);

            mTitlePaint.getTextBounds(title, 0, title.length(), mTitleBounds);
            final float topTitleOffset = (mBounds.height() - mStripWeight) * 0.5F +
                    mTitleBounds.height() * 0.5F - mTitleBounds.bottom;

            // Get interpolated fraction for left last and current tab
            final float interpolation = mResizeInterpolator.getResizeInterpolation(mFraction, true);
            final float lastInterpolation = mResizeInterpolator.getResizeInterpolation(mFraction, false);

            // Check if we handle tab from touch on NTS or from ViewPager
            // There is a strange logic of ViewPager onPageScrolled method, so it is
            if (mIsSetIndexFromTabBar) {
                if (mIndex == i) updateCurrentTitle(interpolation);
                else if (mLastIndex == i) updateLastTitle(lastInterpolation);
                else updateInactiveTitle();
            } else {
                if (i != mIndex && i != mIndex + 1) updateInactiveTitle();
                else if (i == mIndex + 1) updateCurrentTitle(interpolation);
                else if (i == mIndex) updateLastTitle(lastInterpolation);
            }

            canvas.drawText(
                    title, leftTitleOffset,
                    topTitleOffset + (mStripGravity == StripGravity.TOP ? mStripWeight : 0.0F),
                    mTitlePaint
            );
        }
    }

    // Method to transform current fraction of NTS and position
    private void updateCurrentTitle(final float interpolation) {
        mTitlePaint.setColor(
                (int) mColorEvaluator.evaluate(interpolation, mInactiveColor, mActiveColor)
        );
    }

    // Method to transform last fraction of NTS and position
    private void updateLastTitle(final float lastInterpolation) {
        mTitlePaint.setColor(
                (int) mColorEvaluator.evaluate(lastInterpolation, mActiveColor, mInactiveColor)
        );
    }

    // Method to transform others fraction of NTS and position
    private void updateInactiveTitle() {
        mTitlePaint.setColor(mInactiveColor);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, final int positionOffsetPixels) {
        // If we animate, don`t call this
        if (!mIsSetIndexFromTabBar) {
            mIsResizeIn = position < mIndex;
            mLastIndex = mIndex;
            mIndex = position;

            mStartStripX =
                    (position * mTabSize) + (mStripType == StripType.POINT ? mTabSize * 0.5F : 0.0F);
            mEndStripX = mStartStripX + mTabSize;
            updateIndicatorPosition(positionOffset);
        }

        if (mOnPageChangeListener != null)
            mOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
    }

    @Override
    public void onPageSelected(final int position) {
        // If VP idle, so update
        if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
            mIsResizeIn = position < mIndex;
            mLastIndex = mIndex;
            mIndex = position;
            postInvalidate();
        }
    }

    @Override
    public void onPageScrollStateChanged(final int state) {
        // If VP idle, reset to MIN_FRACTION
        if (state == ViewPager.SCROLL_STATE_IDLE) {
            mFraction = MIN_FRACTION;
            mIsSetIndexFromTabBar = false;

            if (mOnPageChangeListener != null) mOnPageChangeListener.onPageSelected(mIndex);
            else {
                if (mOnTabStripSelectedIndexListener != null)
                    mOnTabStripSelectedIndexListener.onEndTabSelected(mTitles[mIndex], mIndex);
            }
        }
        mScrollState = state;

        if (mOnPageChangeListener != null) mOnPageChangeListener.onPageScrollStateChanged(state);
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        final SavedState savedState = (SavedState) state;
        super.onRestoreInstanceState(savedState.getSuperState());
        mIndex = savedState.index;
        requestLayout();
    }

    @Override
    public Parcelable onSaveInstanceState() {
        final Parcelable superState = super.onSaveInstanceState();
        final SavedState savedState = new SavedState(superState);
        savedState.index = mIndex;
        return savedState;
    }

    // Save current index instance
    private static class SavedState extends BaseSavedState {
        int index;

        public SavedState(Parcelable superState) {
            super(superState);
        }

        private SavedState(Parcel in) {
            super(in);
            index = in.readInt();
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(index);
        }

        @SuppressWarnings("UnusedDeclaration")
        public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
            @Override
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

    @Override
    protected void onConfigurationChanged(final Configuration newConfig) {
        // Config view on rotate etc.
        super.onConfigurationChanged(newConfig);
        requestLayout();

        // Refresh strip and state after config changed to current
        final int tempIndex = mIndex;
        setTabIndex(INVALID_INDEX, true);
        post(new Runnable() {
            @Override
            public void run() {
                setTabIndex(tempIndex, true);
            }
        });
    }

    // Custom scroller with custom scroll duration
    private class ResizeViewPagerScroller extends Scroller {

        public ResizeViewPagerScroller(Context context) {
            super(context, new AccelerateDecelerateInterpolator());
        }

        @Override
        public void startScroll(int startX, int startY, int dx, int dy, int duration) {
            super.startScroll(startX, startY, dx, dy, mAnimationDuration);
        }

        @Override
        public void startScroll(int startX, int startY, int dx, int dy) {
            super.startScroll(startX, startY, dx, dy, mAnimationDuration);
        }
    }

    // Resize interpolator to create smooth effect on strip according to inspiration design
    // This is like improved accelerated and decelerated interpolator
    private static class ResizeInterpolator implements Interpolator {

        // Spring factor
        private float mFactor;
        // Check whether side we move
        private boolean mResizeIn;

        public float getFactor() {
            return mFactor;
        }

        public void setFactor(final float factor) {
            mFactor = factor;
        }

        @Override
        public float getInterpolation(final float input) {
            if (mResizeIn) return (float) (1.0F - Math.pow((1.0F - input), 2.0F * mFactor));
            else return (float) (Math.pow(input, 2.0F * mFactor));
        }

        public float getResizeInterpolation(final float input, final boolean resizeIn) {
            mResizeIn = resizeIn;
            return getInterpolation(input);
        }
    }

    // NTS strip type
    public enum StripType {
        LINE, POINT;

        private final static int LINE_INDEX = 0;
        private final static int POINT_INDEX = 1;
    }

    // NTS strip gravity
    public enum StripGravity {
        BOTTOM, TOP;

        private final static int BOTTOM_INDEX = 0;
        private final static int TOP_INDEX = 1;
    }

    // Out listener for selected index
    public interface OnTabStripSelectedIndexListener {
        void onStartTabSelected(final String title, final int index);

        void onEndTabSelected(final String title, final int index);
    }
}
NavigationTabStrip

相关的资源文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="NavigationTabStrip">
        <attr name="nts_color" format="color"/>
        <attr name="nts_size" format="dimension"/>
        <attr name="nts_weight" format="dimension"/>
        <attr name="nts_factor" format="float"/>
        <attr name="nts_titles" format="reference"/>

        <attr name="nts_type" format="enum">
            <enum name="line" value="0"/>
            <enum name="point" value="1"/>
        </attr>
        <attr name="nts_gravity" format="enum">
            <enum name="bottom" value="0"/>
            <enum name="top" value="1"/>
        </attr>

        <attr name="nts_typeface" format="string"/>
        <attr name="nts_corners_radius" format="dimension"/>
        <attr name="nts_animation_duration" format="integer"/>

        <attr name="nts_inactive_color" format="color"/>
        <attr name="nts_active_color" format="color"/>
    </declare-styleable>

</resources>
attrs.xml

Demo代码:

xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.view.ViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:weightSum="3">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#ffffffff"
            android:paddingEnd="30dp"
            android:paddingLeft="30dp"
            android:paddingRight="30dp"
            android:paddingStart="30dp">

            <testnavigationtabstrip.zzw.com.tablib.NavigationTabStrip
                android:id="@+id/nts_top"
                android:layout_width="match_parent"
                android:layout_height="56dp"
                android:layout_gravity="center"
                app:nts_active_color="#42a4d1"
                app:nts_color="#42a4d1"
                app:nts_corners_radius="1dp"
                app:nts_inactive_color="#ff1a1e23"
                app:nts_size="15sp"
                app:nts_titles="@array/titles"
                app:nts_weight="3dp" />

        </FrameLayout>

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#ff252e39">

            <View
                android:layout_width="match_parent"
                android:layout_height="4dp"
                android:layout_gravity="center"
                android:layout_marginTop="18dp"
                android:background="#182128" />

            <testnavigationtabstrip.zzw.com.tablib.NavigationTabStrip
                android:id="@+id/nts_center"
                android:layout_width="match_parent"
                android:layout_height="42dp"
                android:layout_gravity="center"
                android:layout_marginEnd="30dp"
                android:layout_marginLeft="30dp"
                android:layout_marginRight="30dp"
                android:layout_marginStart="30dp"
                app:nts_active_color="#fff"
                app:nts_color="#3cdedd"
                app:nts_corners_radius="3.5dp"
                app:nts_factor="0.8"
                app:nts_gravity="bottom"
                app:nts_inactive_color="#81ffffff"
                app:nts_titles="@array/titles"
                app:nts_type="point"
                app:nts_typeface="fonts/moon.otf"
                app:nts_weight="7dp" />

        </FrameLayout>

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="#ff1a1e23"
            android:paddingEnd="30dp"
            android:paddingLeft="30dp"
            android:paddingRight="30dp"
            android:paddingStart="30dp">

            <testnavigationtabstrip.zzw.com.tablib.NavigationTabStrip
                android:id="@+id/nts_bottom"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_gravity="center"
                app:nts_active_color="#fff"
                app:nts_color="#ff1748"
                app:nts_corners_radius="1.5dp"
                app:nts_factor="2.8"
                app:nts_inactive_color="#81ffffff"
                app:nts_titles="@array/titles"
                app:nts_typeface="fonts/drugs.otf"
                app:nts_weight="3dp" />

        </FrameLayout>

    </LinearLayout>

</FrameLayout>

相关资源文件:

arrays.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string-array name="titles">
        <item>Nav</item>
        <item>Tab</item>
        <item>Strip</item>
    </string-array>

</resources>

java代码:

package com.zzw.TestNavigationTabStrip;

import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;

import testnavigationtabstrip.zzw.com.tablib.NavigationTabStrip;

public class MainActivity extends AppCompatActivity {
    private ViewPager mViewPager;

    private NavigationTabStrip mTopNavigationTabStrip;
    private NavigationTabStrip mCenterNavigationTabStrip;
    private NavigationTabStrip mBottomNavigationTabStrip;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initUI();
        setUI();
    }

    private void initUI() {
        mViewPager = (ViewPager) findViewById(R.id.vp);
        mTopNavigationTabStrip = (NavigationTabStrip) findViewById(R.id.nts_top);
        mCenterNavigationTabStrip = (NavigationTabStrip) findViewById(R.id.nts_center);
        mBottomNavigationTabStrip = (NavigationTabStrip) findViewById(R.id.nts_bottom);
    }

    private void setUI() {
        mViewPager.setAdapter(new PagerAdapter() {
            @Override
            public int getCount() {
                return 3;
            }

            @Override
            public boolean isViewFromObject(final View view, final Object object) {
                return view.equals(object);
            }

            @Override
            public void destroyItem(final View container, final int position, final Object object) {
                ((ViewPager) container).removeView((View) object);
            }

            @Override
            public Object instantiateItem(final ViewGroup container, final int position) {
                final View view = new View(getBaseContext());
                container.addView(view);
                return view;
            }
        });

        mTopNavigationTabStrip.setTabIndex(1, true);
        mBottomNavigationTabStrip.setViewPager(mViewPager, 1);
        mCenterNavigationTabStrip.setTabIndex(1, true);

//        final NavigationTabStrip navigationTabStrip = (NavigationTabStrip) findViewById(R.id.nts);
//        navigationTabStrip.setTitles("Nav", "Tab", "Strip");
//        navigationTabStrip.setTabIndex(0, true);
//        navigationTabStrip.setTitleSize(15);
//        navigationTabStrip.setStripColor(Color.RED);
//        navigationTabStrip.setStripWeight(6);
//        navigationTabStrip.setStripFactor(2);
//        navigationTabStrip.setStripType(NavigationTabStrip.StripType.LINE);
//        navigationTabStrip.setStripGravity(NavigationTabStrip.StripGravity.BOTTOM);
//        navigationTabStrip.setTypeface("fonts/typeface.ttf");
//        navigationTabStrip.setCornersRadius(3);
//        navigationTabStrip.setAnimationDuration(300);
//        navigationTabStrip.setInactiveColor(Color.GRAY);
//        navigationTabStrip.setActiveColor(Color.WHITE);
//        navigationTabStrip.setOnPageChangeListener(...);
//        navigationTabStrip.setOnTabStripSelectedIndexListener(...);
    }
}
原文地址:https://www.cnblogs.com/zzw1994/p/5553609.html