简单实用的ViewPager指示器NavigationTabStrip
简单实用的ViewPager指示器NavigationTabStrip
最低支持版本11,特效很足嗷,各位老板搬好板凳把!!
-
考入工具类
-
布局中引用
-
代码实现
代码块
- 布局实现:
<com.***.***.widget.NavigationTabStrip
android:id="@+id/act_menu_nts"
app:nts_active_color="@color/main_bg"
app:nts_color="@color/main_bg"
app:nts_corners_radius="3dp"
app:nts_gravity="top"
app:nts_inactive_color="#ff1a1e23"
app:nts_size="14sp"
app:nts_weight="3dp"
android:layout_width="match_parent"
android:layout_height="@dimen/vp_tab_height"
android:layout_gravity="center"
android:background="@color/white" />
- 代码实现:
1.注解Id
@BindView(R.id.act_menu_nts)
NavigationTabStrip mVpNts;
2.设置item显示
mVpNts.setTitles("测试一", "测试二", "测试三");
3.设置viewPage,将viewPage放入mVpNts
TodoFrag todoFrag = new TodoFrag();
DoneFrag doneFrag = new DoneFrag();
QueryFrag queryFrag = new QueryFrag();
List<BaseFragment> mVpListData = new ArrayList<>();
mVpListData.add(todoFrag);
mVpListData.add(doneFrag);
mVpListData.add(queryFrag);
FragPagAdapter mPagAdapter = new FragPagAdapter(getSupportFragmentManager(), mVpListData);
viewPager.setAdapter(mPagAdapter);
mVpNts.setViewPager(viewPager);
4.设置滑动监听
mVpNts.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
switch (position) {
case 0:
mRightBtn.setText("新增");
mRightBtn.setVisibility(View.VISIBLE);
break;
case 1:
mRightBtn.setVisibility(View.GONE);
break;
case 2:
mRightBtn.setText("查询");
mRightBtn.setVisibility(View.VISIBLE);
break;
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
- 工具类:
public class NavigationTabStrip extends View implements ViewPager.OnPageChangeListener {
// NTS constants
private final static int HIGH_QUALITY_FLAGS = Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG;
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(HIGH_QUALITY_FLAGS) {
{
setStyle(Style.FILL);
}
};
// Paint for tav title
private final Paint mTitlePaint = new TextPaint(HIGH_QUALITY_FLAGS) {
{
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);
// Speed and fix for pre 17 API
ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, null);
setLayerType(LAYER_TYPE_SOFTWARE, 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);
break;
}
}
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);
break;
}
}
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) {
if (TextUtils.isEmpty(typeface)) return;
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 AnimatorListenerAdapter() {
@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);
}
};
mAnimator.removeListener(mAnimatorListener);
mAnimator.addListener(mAnimatorListener);
}
public void setViewPager(final ViewPager viewPager) {
// Detect whether ViewPager mode
if (viewPager == null) {
mIsViewPagerMode = false;
return;
}
if (viewPager.equals(mViewPager)) return;
if (mViewPager != null) //noinspection deprecation
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 tabIndex, boolean isForce) {
if (mAnimator.isRunning()) return;
if (mTitles.length == 0) return;
int index = tabIndex;
boolean force = isForce;
// 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, !force);
}
// 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);
// Force onPageScrolled listener and refresh VP
if (mIsViewPagerMode) {
if (!mViewPager.isFakeDragging()) mViewPager.beginFakeDrag();
if (mViewPager.isFakeDragging()) {
mViewPager.fakeDragBy(0.0F);
mViewPager.endFakeDrag();
}
}
} else mAnimator.start();
}
// Deselect active index and reset pointer
public void deselect() {
mLastIndex = INVALID_INDEX;
mIndex = INVALID_INDEX;
mStartStripX = INVALID_INDEX * mTabSize;
mEndStripX = mStartStripX;
updateIndicatorPosition(MIN_FRACTION);
}
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() {
requestLayout();
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;
}
@SuppressLint("DrawAllocation")
@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(
(Integer) mColorEvaluator.evaluate(interpolation, mInactiveColor, mActiveColor)
);
}
// Method to transform last fraction of NTS and position
private void updateLastTitle(final float lastInterpolation) {
mTitlePaint.setColor(
(Integer) 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 (mOnPageChangeListener != null)
mOnPageChangeListener.onPageScrolled(position, positionOffset, 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);
}
// Stop scrolling on animation end and reset values
if (!mAnimator.isRunning() && mIsSetIndexFromTabBar) {
mFraction = MIN_FRACTION;
mIsSetIndexFromTabBar = false;
}
}
@Override
public void onPageSelected(final int position) {
// 自定义 - 绑定Title View 随着position 改变设置text
if (mTitleTv != null) {
mTitleTv.setText(mTitles[position]);
}
// This method is empty, because we call onPageSelected() when scroll state is idle
}
@Override
public void onPageScrollStateChanged(final int state) {
// If VP idle, reset to MIN_FRACTION
mScrollState = state;
if (state == ViewPager.SCROLL_STATE_IDLE) {
if (mOnPageChangeListener != null) mOnPageChangeListener.onPageSelected(mIndex);
if (mIsViewPagerMode && mOnTabStripSelectedIndexListener != null)
mOnTabStripSelectedIndexListener.onEndTabSelected(mTitles[mIndex], mIndex);
}
if (mOnPageChangeListener != null) mOnPageChangeListener.onPageScrollStateChanged(state);
}
TextView mTitleTv;
/**
* 设置 title tv 随着 position 改变 设置 content
* @param mTitleTv title
*/
public void setTitleTextView(TextView mTitleTv){
this.mTitleTv = mTitleTv;
}
@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 {
private 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;
deselect();
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);
}
}
- **GitHub地址**
https://github.com/wp521