package com.yuyh.library.view.viewpager.indicator; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.widget.LinearLayout; import android.widget.Scroller; import com.yuyh.library.view.viewpager.indicator.slidebar.ScrollBar; import java.util.LinkedList; import java.util.List; public class FixedIndicatorView extends LinearLayout implements Indicator { private IndicatorAdapter mAdapter; private OnItemSelectedListener onItemSelectedListener; private int mSelectedTabIndex = -1; public static final int SPLITMETHOD_EQUALS = 0; public static final int SPLITMETHOD_WEIGHT = 1; public static final int SPLITMETHOD_WRAP = 2; private int splitMethod = SPLITMETHOD_EQUALS; public FixedIndicatorView(Context context) { super(context); init(); } @SuppressLint("NewApi") public FixedIndicatorView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public FixedIndicatorView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { inRun = new InRun(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); inRun.stop(); } @Override public void setAdapter(IndicatorAdapter adapter) { if (this.mAdapter != null) { this.mAdapter.unRegistDataSetObserver(dataSetObserver); } this.mAdapter = adapter; adapter.registDataSetObserver(dataSetObserver); adapter.notifyDataSetChanged(); } public ScrollBar getScrollBar() { return scrollBar; } @Override public void setOnItemSelectListener(OnItemSelectedListener onItemSelectedListener) { this.onItemSelectedListener = onItemSelectedListener; } @Override public IndicatorAdapter getAdapter() { return mAdapter; } @Override public void setCurrentItem(int item) { setCurrentItem(item, true); } private int mPreSelectedTabIndex = -1; @Override public void setCurrentItem(int item, boolean anim) { int count = getCount(); if (count == 0) { return; } if (item < 0) { item = 0; } else if (item > count - 1) { item = count - 1; } if (mSelectedTabIndex != item) { mPreSelectedTabIndex = mSelectedTabIndex; mSelectedTabIndex = item; final int tabCount = mAdapter.getCount(); for (int i = 0; i < tabCount; i++) { final ViewGroup group = (ViewGroup) getChildAt(i); View child = group.getChildAt(0); final boolean isSelected = (i == item); child.setSelected(isSelected); } if (!inRun.isFinished()) { inRun.stop(); } if (positionOffset < 0.02f || positionOffset > 0.98f || !anim) { notifyPageScrolled(item, 0, 0); initNotifyOnPageScrollListener(); } if (getWidth() != 0 && anim && mPositionOffset < 0.01f && mPreSelectedTabIndex >= 0 && mPreSelectedTabIndex < getChildCount()) { int sx = getChildAt(mPreSelectedTabIndex).getLeft(); int ex = getChildAt(item).getLeft(); final float pageDelta = (float) Math.abs(ex - sx) / (getChildAt(item).getWidth()); int duration = (int) ((pageDelta + 1) * 100); duration = Math.min(duration, 600); inRun.startScroll(sx, ex, duration); } } } private void initNotifyOnPageScrollListener() { int tabCount; if (mAdapter != null && (tabCount = mAdapter.getCount()) > 1) { if (onPageScrollListener != null && tabCount > 1) { if (mPreSelectedTabIndex >= 0) { View view1 = getItemView(mPreSelectedTabIndex); if (view1 != null) { onPageScrollListener.onTransition(view1, mPreSelectedTabIndex, 0); } } if (mSelectedTabIndex >= 0) { View view1 = getItemView(mSelectedTabIndex); if (view1 != null) { onPageScrollListener.onTransition(view1, mSelectedTabIndex, 1); } } } } } @Override public int getCurrentItem() { return mSelectedTabIndex; } private List<ViewGroup> views = new LinkedList<ViewGroup>(); private DataSetObserver dataSetObserver = new DataSetObserver() { @Override public void onChange() { if (!inRun.isFinished()) { inRun.stop(); } positionOffset = 0; int count = getChildCount(); int newCount = mAdapter.getCount(); views.clear(); for (int i = 0; i < count && i < newCount; i++) { views.add((ViewGroup) getChildAt(i)); } removeAllViews(); int size = views.size(); for (int i = 0; i < newCount; i++) { LinearLayout result = new LinearLayout(getContext()); View view; if (i < size) { View temp = views.get(i).getChildAt(0); views.get(i).removeView(temp); view = mAdapter.getView(i, temp, result); } else { view = mAdapter.getView(i, null, result); } result.addView(view); result.setOnClickListener(onClickListener); result.setTag(i); addView(result, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); } mPreSelectedTabIndex = -1; setCurrentItem(mSelectedTabIndex, false); measureTabs(); } }; public void setSplitMethod(int splitMethod) { this.splitMethod = splitMethod; measureTabs(); } public int getSplitMethod() { return splitMethod; } private OnClickListener onClickListener = new OnClickListener() { @Override public void onClick(View v) { int i = (Integer) v.getTag(); ViewGroup parent = (ViewGroup) v; setCurrentItem(i); if (onItemSelectedListener != null) { onItemSelectedListener.onItemSelected(parent.getChildAt(0), i, mPreSelectedTabIndex); } } }; private ScrollBar scrollBar; @Override public void setScrollBar(ScrollBar scrollBar) { int paddingBottom = getPaddingBottom(); int paddingTop = getPaddingTop(); if (this.scrollBar != null) { switch (this.scrollBar.getGravity()) { case BOTTOM_FLOAT: paddingBottom = paddingBottom - scrollBar.getHeight(getHeight()); break; case TOP_FLOAT: paddingTop = paddingTop - scrollBar.getHeight(getHeight()); break; default: break; } } this.scrollBar = scrollBar; switch (this.scrollBar.getGravity()) { case BOTTOM_FLOAT: paddingBottom = paddingBottom + scrollBar.getHeight(getHeight()); break; case TOP_FLOAT: paddingTop = paddingTop + scrollBar.getHeight(getHeight()); break; default: break; } setPadding(getPaddingLeft(), paddingTop, getPaddingRight(), paddingBottom); // measureScrollBar(true); } private InRun inRun; private class InRun implements Runnable { private int updateTime = 20; private Scroller scroller; private final Interpolator sInterpolator = new Interpolator() { public float getInterpolation(float t) { t -= 1.0f; return t * t * t * t * t + 1.0f; } }; public InRun() { super(); scroller = new Scroller(getContext(), sInterpolator); } public void startScroll(int startX, int endX, int dration) { scroller.startScroll(startX, 0, endX - startX, 0, dration); ViewCompat.postInvalidateOnAnimation(FixedIndicatorView.this); post(this); } public boolean isFinished() { return scroller.isFinished(); } public boolean computeScrollOffset() { return scroller.computeScrollOffset(); } public int getCurrentX() { return scroller.getCurrX(); } public void stop() { if (scroller.isFinished()) { scroller.abortAnimation(); } removeCallbacks(this); } @Override public void run() { ViewCompat.postInvalidateOnAnimation(FixedIndicatorView.this); if (!scroller.isFinished()) { postDelayed(this, updateTime); } } } @Override protected void dispatchDraw(Canvas canvas) { if (scrollBar != null && scrollBar.getGravity() == ScrollBar.Gravity.CENTENT_BACKGROUND) { drawSlideBar(canvas); } super.dispatchDraw(canvas); if (scrollBar != null && scrollBar.getGravity() != ScrollBar.Gravity.CENTENT_BACKGROUND) { drawSlideBar(canvas); } } private void drawSlideBar(Canvas canvas) { if (mAdapter == null || scrollBar == null) { inRun.stop(); return; } final int count = mAdapter.getCount(); if (count == 0) { inRun.stop(); return; } if (getCurrentItem() >= count) { setCurrentItem(count - 1); inRun.stop(); return; } float offsetX = 0; int offsetY = 0; switch (this.scrollBar.getGravity()) { case CENTENT_BACKGROUND: case CENTENT: offsetY = (getHeight() - scrollBar.getHeight(getHeight())) / 2; break; case TOP: case TOP_FLOAT: offsetY = 0; break; case BOTTOM: case BOTTOM_FLOAT: default: offsetY = getHeight() - scrollBar.getHeight(getHeight()); break; } View currentView = null; if (!inRun.isFinished() && inRun.computeScrollOffset()) { offsetX = inRun.getCurrentX(); int position = 0; for (int i = 0; i < count; i++) { currentView = getChildAt(i); if (currentView.getLeft() <= offsetX && offsetX < currentView.getRight()) { position = i; break; } } int width = currentView.getWidth(); int positionOffsetPixels = (int) (offsetX - currentView.getLeft()); float positionOffset = (offsetX - currentView.getLeft()) / width; notifyPageScrolled(position, positionOffset, positionOffsetPixels); } else if (mPositionOffset > 0.001f) { currentView = getChildAt(mPosition); int width = currentView.getWidth(); offsetX = currentView.getLeft() + width * mPositionOffset; notifyPageScrolled(mPosition, mPositionOffset, mPositionOffsetPixels); } else { currentView = getChildAt(mPosition); if (currentView == null) { return; } offsetX = currentView.getLeft(); } if (inRun.isFinished()) { inRun.stop(); } int tabWidth = measureScrollBar(mPosition, mPositionOffset, true); int width = scrollBar.getSlideView().getWidth(); offsetX += (tabWidth - width) / 2; int saveCount = canvas.save(); canvas.translate(offsetX, offsetY); canvas.clipRect(0, 0, width, scrollBar.getSlideView().getHeight()); // needed scrollBar.getSlideView().draw(canvas); canvas.restoreToCount(saveCount); inRun.stop(); } private int[] prePositions = { -1, -1 }; private float positionOffset; private void notifyPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (position < 0 || position > getCount() - 1) { return; } this.positionOffset = positionOffset; if (scrollBar != null) { scrollBar.onPageScrolled(position, positionOffset, positionOffsetPixels); } if (onPageScrollListener != null) { for (int i : prePositions) { if (i != position && i != position + 1) { View view = getItemView(i); if (view != null) { onPageScrollListener.onTransition(view, i, 0); } } } prePositions[0] = position; prePositions[1] = position + 1; View view = getItemView(position); onPageScrollListener.onTransition(view, position, 1 - positionOffset); view = getItemView(position + 1); if (view != null) { onPageScrollListener.onTransition(view, position + 1, positionOffset); } } } private int measureScrollBar(int position, float selectPercent, boolean needChange) { if (scrollBar == null) return 0; View view = scrollBar.getSlideView(); if (view.isLayoutRequested() || needChange) { View selectV = getChildAt(position); View unSelectV = getChildAt(position + 1); if (selectV != null) { int tabWidth = (int) (selectV.getWidth() * (1 - selectPercent) + (unSelectV == null ? 0 : unSelectV.getWidth() * selectPercent)); int width = scrollBar.getWidth(tabWidth); int height = scrollBar.getHeight(getHeight()); view.measure(width, height); view.layout(0, 0, width, height); return tabWidth; } } return scrollBar.getSlideView().getWidth(); } private void measureTabs() { // int width = getMeasuredWidth(); int count = getChildCount(); // if (count == 0 || width == 0) { // return; // } switch (splitMethod) { case SPLITMETHOD_EQUALS: for (int i = 0; i < count; i++) { View view = getChildAt(i); LayoutParams layoutParams = (LayoutParams) view.getLayoutParams(); layoutParams.width = 0; layoutParams.weight = 1; view.setLayoutParams(layoutParams); } break; case SPLITMETHOD_WRAP: for (int i = 0; i < count; i++) { View view = getChildAt(i); LayoutParams layoutParams = (LayoutParams) view.getLayoutParams(); layoutParams.width = LayoutParams.WRAP_CONTENT; layoutParams.weight = 0; view.setLayoutParams(layoutParams); } break; case SPLITMETHOD_WEIGHT: for (int i = 0; i < count; i++) { View view = getChildAt(i); LayoutParams layoutParams = (LayoutParams) view.getLayoutParams(); layoutParams.width = LayoutParams.WRAP_CONTENT; layoutParams.weight = 1; view.setLayoutParams(layoutParams); } break; } } public int getCount() { if (mAdapter == null) { return 0; } return mAdapter.getCount(); } @Override protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { super.measureChildren(widthMeasureSpec, heightMeasureSpec); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); measureScrollBar(mSelectedTabIndex, 1, true); } private int mPosition; private int mPositionOffsetPixels; @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { this.mPosition = position; this.mPositionOffset = positionOffset; this.mPositionOffsetPixels = positionOffsetPixels; if (scrollBar != null) { ViewCompat.postInvalidateOnAnimation(this); } else { notifyPageScrolled(position, positionOffset, positionOffsetPixels); } } @Override public void onPageScrollStateChanged(int state) { if (state == ViewPager.SCROLL_STATE_IDLE) { onPageScrolled(getCurrentItem(), 0, 0); } } private float mPositionOffset; @Override public void setOnTransitionListener(OnTransitionListener onPageScrollListener) { this.onPageScrollListener = onPageScrollListener; initNotifyOnPageScrollListener(); } private OnTransitionListener onPageScrollListener; @Override public View getItemView(int position) { if (position < 0 || position > mAdapter.getCount() - 1) { return null; } final ViewGroup group = (ViewGroup) getChildAt(position); return group.getChildAt(0); } @Override public OnItemSelectedListener getOnItemSelectListener() { return onItemSelectedListener; } @Override public OnTransitionListener getOnTransitionListener() { return onPageScrollListener; } @Override public int getPreSelectItem() { return mPreSelectedTabIndex; } }