炫酷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(...);
    }
}
时间: 2024-11-05 21:53:15

炫酷tab栏--第三方开源--NavigationTabStrip的相关文章

css3制作炫酷导航栏效果

今天主要利用hover选择器.鼠标滑过查看效果. 一.普通导航栏 Home Content Service Team Contact 对于这种普通的导航栏,只是鼠标滑过的时候颜色会变,所以思路变得很简单. (1)使用ul标签布局 (2)鼠标经过事件 <div id="demo1"> <ul> <li>Home</li> <li>Content</li> <li>Service</li> &l

CSS+JQ实现炫酷导航栏

一步一步的学习,后面再做个综合页面 1.当前页面高亮显示的导航栏 首先是HTML代码,很简单,ul+li实现菜单 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>导航栏一</title> </head> <body> <header class="header&qu

css3制作炫酷导航栏效果 &lt;转&gt;

今天主要利用hover选择器.鼠标滑过查看效果. 一.普通导航栏 Home Content Service Team Contact 对于这种普通的导航栏,只是鼠标滑过的时候颜色会变,所以思路变得很简单. (1)使用ul标签布局 (2)鼠标经过事件 <div id="demo1"> <ul> <li>Home</li> <li>Content</li> <li>Service</li> &l

炫酷的底部菜单栏BottomBar

开源项目分析BottomBar 今天分析一个炫酷的底部菜单栏开源项目,先说明下用法,再分析一下源码的实现. GitHub地址 https://github.com/roughike/BottomBar 先上个效果图 使用 添加依赖 compile 'com.roughike:bottom-bar:1.3.3' 创建menu资源文件 res/menu/bottombar_menu.xml: <menu xmlns:android="http://schemas.android.com/apk

开源分享三(炫酷的Android Loading动画)

开源分享三(炫酷的Android Loading动画) 分享GitHub上的一些Loading,为了提升产品用户体验,一个好的Loading必然是不可缺少的,对于一些耗时需要用户等待的页面来说会转移用户注意力,不会显得那么烦躁,所以你可以看到市面上一些App中的各种各样的Loading动画,从这些实现思路上可以打开你们自己的思维,没准也会有创新的Loading动画出现. android-shapeLoadingView 新版58同城加载页面动画. CircleProgress 一个效果很酷炫很创

玩转AppBarLayout,更酷炫的顶部栏

我的简书同步发布:玩转AppBarLayout,更酷炫的顶部栏 上一篇文章<CoordinateLayout的使用如此简单 >对CoordinateLayout的使用做了讲解,今天我们再讲解常常与其一起使用的几个View:AppBarLayout.CollapsingToolbarLayout以及Toolbar.一下子出现3个陌生的View,是不是觉得很慌张~,很多人都写了这几个布局的使用,但是他们却没有有针对性的单独讲解每个View的作用以及如何使用,我看的很多文章都是一上来就把AppBar

Android 实现酷炫的顶部栏

AppBarLayout 是继承LinerLayout实现的一个ViewGroup容器组件,它是为了Material Design设计的App Bar,支持手势滑动操作的,不过经常与Toolbar.CoordinatorLayout以及CollapsingToolbarLayout等一起配合使用,在说到AppBarLayout之前,我们先简单学习一下Toolbar.和往常一样,主要还是想总结一下我在学习过程中的一些笔记以及一些需要注意的地方. 一.Toolbar Toolbar是在 Androi

「珍藏」老司机为你推荐10个炫酷的开源库,看完的人都收藏了

前言 技术群里面经常有人问到一些炫酷的UI效果实现方法,有时候我都是给一个相同或者相似效果的Github链接,有同学私信给我说,大佬,怎么这些效果你都能找到?你是怎么搜索的,或者有其他什么秘方?会利用Google.百度等搜索工具搜索是一方面,另一个重要的方面是:记录搜藏,当看到一个炫酷的效果的时候,记得收藏起来,记录到自己云笔记或者收藏夹里,看得多了,印象就比较深刻,当遇到类似效果的时候,到自己记录收藏的地方找就是了.今天为大家推荐我所收藏的一些炫酷实用的效果的开源库(选择其中10个). 1.D

教你用原生CSS写炫酷页面切换效果,跟第三方组件说拜拜

因为项目需要,别人想让我给他写一个个人博客,并且给了我一个其他人的网页,可以点此查看.有的同学可能说了,第三方博客框架这么多,为什么还要去手写的,你说这个有可能是没有看到打开这个博客. 样式介绍 给大家看一下这个网页的大体样式. 这个界面可以说是非常漂亮,整体也是一个响应式布局,总体来说还算不错.但是抛开页面设计,这个网站有一个致命的缺点,就是没有做懒加载,这么多页面其实就是一个HTML文件,所有的资源图片以及文字信息等全部是一次性加载,所以你想打开这个界面还是比较困难的,需要等待一些时间. 我