package com.yuyh.library.view.viewpager.indicator; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.HorizontalScrollView; import com.yuyh.library.view.viewpager.indicator.slidebar.ScrollBar; /** * * @author试着飞 * @date 2014年11月1日 * @version 1.0 主要用于多个tab可以进行滑动 */ public class ScrollIndicatorView extends HorizontalScrollView implements Indicator { private SFixedIndicatorView fixedIndicatorView; private boolean isPinnedTabView = false; private Paint defaultShadowPaint = null; private Drawable customShadowDrawable; private int shadowWidth; @TargetApi(Build.VERSION_CODES.HONEYCOMB) public ScrollIndicatorView(Context context, AttributeSet attrs) { super(context, attrs); fixedIndicatorView = new SFixedIndicatorView(context); addView(fixedIndicatorView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); setHorizontalScrollBarEnabled(false); setSplitAuto(true); defaultShadowPaint = new Paint(); defaultShadowPaint.setAntiAlias(true); defaultShadowPaint.setColor(0x33AAAAAA); shadowWidth = dipToPix(3); defaultShadowPaint.setShadowLayer(shadowWidth, 0, 0, 0xFF000000); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { setLayerType(View.LAYER_TYPE_SOFTWARE, null); } } public void setSplitAuto(boolean splitAuto) { setFillViewport(splitAuto); fixedIndicatorView.setSplitAuto(splitAuto); } public boolean isSplitAuto() { return fixedIndicatorView.isSplitAuto(); } @Override public void setAdapter(IndicatorAdapter adapter) { if (getAdapter() != null) { getAdapter().unRegistDataSetObserver(dataSetObserver); } fixedIndicatorView.setAdapter(adapter); adapter.registDataSetObserver(dataSetObserver); dataSetObserver.onChange(); } @Override public void setOnItemSelectListener(OnItemSelectedListener onItemSelectedListener) { fixedIndicatorView.setOnItemSelectListener(onItemSelectedListener); } @Override public IndicatorAdapter getAdapter() { return fixedIndicatorView.getAdapter(); } public void setPinnedTabView(boolean isPinnedTabView) { this.isPinnedTabView = isPinnedTabView; if (isPinnedTabView) { if (fixedIndicatorView.getChildCount() > 0) { pinnedTabView = fixedIndicatorView.getChildAt(0); } } ViewCompat.postInvalidateOnAnimation(this); } public void setPinnedShadow(Drawable shadowDrawable, int shadowWidth) { this.customShadowDrawable = shadowDrawable; this.shadowWidth = shadowWidth; ViewCompat.postInvalidateOnAnimation(this); } public void setPinnedTabBg(Drawable pinnedTabBgDrawable) { this.pinnedTabBgDrawable = pinnedTabBgDrawable; ViewCompat.postInvalidateOnAnimation(this); } public void setPinnedTabBgColor(int color) { setPinnedTabBg(new ColorDrawable(color)); } public void setPinnedTabBgId(int pinnedTabBgDrawableId) { setPinnedTabBg(ContextCompat.getDrawable(getContext(), pinnedTabBgDrawableId)); } private Drawable pinnedTabBgDrawable; public void setPinnedShadow(int shadowDrawableId, int shadowWidth) { setPinnedShadow(ContextCompat.getDrawable(getContext(), shadowDrawableId), shadowWidth); } private DataSetObserver dataSetObserver = new DataSetObserver() { @Override public void onChange() { if (mTabSelector != null) { removeCallbacks(mTabSelector); } positionOffset = 0; setCurrentItem(fixedIndicatorView.getCurrentItem(), false); if (isPinnedTabView) { if (fixedIndicatorView.getChildCount() > 0) { pinnedTabView = fixedIndicatorView.getChildAt(0); } } } }; @Override public void onAttachedToWindow() { super.onAttachedToWindow(); if (mTabSelector != null) { post(mTabSelector); } } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mTabSelector != null) { removeCallbacks(mTabSelector); } } private Runnable mTabSelector; private View pinnedTabView; private boolean mActionDownHappened; @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (fixedIndicatorView.getCount() > 0) { animateToTab(fixedIndicatorView.getCurrentItem()); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (unScrollPosition != -1) { final View tabView = fixedIndicatorView.getChildAt(unScrollPosition); if (tabView != null) { final int scrollPos = tabView.getLeft() - (getMeasuredWidth() - tabView.getMeasuredWidth()) / 2; if (scrollPos >= 0) { smoothScrollTo(scrollPos, 0); unScrollPosition = -1; } } } } private void animateToTab(final int position) { if (position < 0 || position > fixedIndicatorView.getCount() - 1) { return; } final View tabView = fixedIndicatorView.getChildAt(position); if (mTabSelector != null) { removeCallbacks(mTabSelector); } mTabSelector = new Runnable() { public void run() { final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2; smoothScrollTo(scrollPos, 0); mTabSelector = null; } }; post(mTabSelector); } @Override public void setCurrentItem(int item) { setCurrentItem(item, true); } @Override public void setCurrentItem(int item, boolean anim) { int count = fixedIndicatorView.getCount(); if (count == 0) { return; } if (item < 0) { item = 0; } else if (item > count - 1) { item = count - 1; } unScrollPosition = -1; if (positionOffset < 0.02f || positionOffset > 0.98f) { if (anim) { animateToTab(item); } else { final View tabView = fixedIndicatorView.getChildAt(item); final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2; if (scrollPos >= 0) { scrollTo(scrollPos, 0); } else { unScrollPosition = item; } } } fixedIndicatorView.setCurrentItem(item, anim); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (isPinnedTabView) { float x = ev.getX(); float y = ev.getY(); if (pinnedTabView != null && y >= pinnedTabView.getTop() && y <= pinnedTabView.getBottom() && x > pinnedTabView.getLeft() && x < pinnedTabView.getRight()) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { mActionDownHappened = true; } else if (ev.getAction() == MotionEvent.ACTION_UP) { if (mActionDownHappened) { pinnedTabView.performClick(); invalidate(new Rect(0, 0, pinnedTabView.getMeasuredWidth(), pinnedTabView.getMeasuredHeight())); mActionDownHappened = false; } } return true; } } return super.dispatchTouchEvent(ev); } private int unScrollPosition = -1; @Override public int getCurrentItem() { return fixedIndicatorView.getCurrentItem(); } @Override public OnItemSelectedListener getOnItemSelectListener() { return fixedIndicatorView.getOnItemSelectListener(); } @Override public void setOnTransitionListener(OnTransitionListener onPageScrollListener) { fixedIndicatorView.setOnTransitionListener(onPageScrollListener); } @Override public OnTransitionListener getOnTransitionListener() { return fixedIndicatorView.getOnTransitionListener(); } @Override public void setScrollBar(ScrollBar scrollBar) { fixedIndicatorView.setScrollBar(scrollBar); } private float positionOffset; @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { this.positionOffset = positionOffset; final View tabView = fixedIndicatorView.getChildAt(position); if (tabView == null) { return; } final View tabView2 = fixedIndicatorView.getChildAt(position + 1); float offset = (tabView.getWidth() + (tabView2 == null ? tabView.getWidth() : tabView2.getWidth())) / 2 * positionOffset; final int scrollPos = (int) (tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2 + offset); scrollTo(scrollPos, 0); fixedIndicatorView.onPageScrolled(position, positionOffset, positionOffsetPixels); } @Override public void onPageScrollStateChanged(int state) { if (state == ViewPager.SCROLL_STATE_IDLE) { onPageScrolled(getCurrentItem(), 0, 0); } } @Override public int getPreSelectItem() { return fixedIndicatorView.getPreSelectItem(); } @Override public View getItemView(int item) { return fixedIndicatorView.getItemView(item); } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (isPinnedTabView) { int scrollX = getScrollX(); if (pinnedTabView != null && scrollX > 0) { int saveCount = canvas.save(); // 绘制固定在开始位置的pinnedTabView canvas.translate(scrollX + getPaddingLeft(), getPaddingTop()); if (pinnedTabBgDrawable != null) { pinnedTabBgDrawable.setBounds(0, 0, pinnedTabView.getWidth(), pinnedTabView.getHeight()); pinnedTabBgDrawable.draw(canvas); } ScrollBar scrollBar = fixedIndicatorView.getScrollBar(); if (scrollBar != null && scrollBar.getGravity() == ScrollBar.Gravity.CENTENT_BACKGROUND) { drawScrollBar(canvas); } pinnedTabView.draw(canvas); if (scrollBar != null && scrollBar.getGravity() != ScrollBar.Gravity.CENTENT_BACKGROUND) { drawScrollBar(canvas); } int x = pinnedTabView.getWidth(); // pinnedTabView的分割绘制阴影 canvas.translate(x, 0); int shadowHeight = getHeight() - getPaddingTop() - getPaddingBottom(); if (customShadowDrawable != null) { customShadowDrawable.setBounds(0, 0, shadowWidth, shadowHeight); customShadowDrawable.draw(canvas); } else { canvas.clipRect(0, 0, shadowWidth + dipToPix(1), shadowHeight); canvas.drawRect(0, 0, dipToPix(1), shadowHeight, defaultShadowPaint); } canvas.restoreToCount(saveCount); } } } private void drawScrollBar(Canvas canvas) { ScrollBar scrollBar = fixedIndicatorView.getScrollBar(); // 如果scrollBar不为空,且刚好选中的是第一个的时候需要在这里重新绘制scrollBar,因为原先fixedIndicatorView回执的scrollBar被遮挡了 if (scrollBar != null && fixedIndicatorView.getCurrentItem() == 0) { int drawScrollBarCount = canvas.save(); int offsetY = 0; switch (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; } int scrollBarWidth = scrollBar.getWidth(pinnedTabView.getWidth()); int scrollBarHeight = scrollBar.getHeight(pinnedTabView.getHeight()); scrollBar.getSlideView().measure(scrollBarWidth, scrollBarHeight); scrollBar.getSlideView().layout(0, 0, scrollBarWidth, scrollBarHeight); int offsetX = (pinnedTabView.getWidth() - scrollBarWidth) / 2; canvas.translate(offsetX, offsetY); canvas.clipRect(0, 0, scrollBarWidth, scrollBarHeight); // needed scrollBar.getSlideView().draw(canvas); canvas.restoreToCount(drawScrollBarCount); } } private static class SFixedIndicatorView extends FixedIndicatorView { private boolean isAutoSplit; public SFixedIndicatorView(Context context) { super(context); } public boolean isSplitAuto() { return isAutoSplit; } public void setSplitAuto(boolean isAutoSplit) { if (this.isAutoSplit != isAutoSplit) { this.isAutoSplit = isAutoSplit; if (!isAutoSplit) { setSplitMethod(SPLITMETHOD_WRAP); } requestLayout(); invalidate(); } } @Override protected void onMeasure(int widthSpec, int heightSpec) { if (isAutoSplit) { ScrollIndicatorView group = (ScrollIndicatorView) getParent(); int layoutWidth = group.getMeasuredWidth(); if (layoutWidth != 0) { int totalWidth = 0; int count = getChildCount(); int maxCellWidth = 0; for (int i = 0; i < count; i++) { int width = measureChildWidth(getChildAt(i), widthSpec, heightSpec); maxCellWidth = maxCellWidth < width ? width : maxCellWidth; totalWidth += width; } if (totalWidth > layoutWidth) { group.setFillViewport(false); setSplitMethod(SPLITMETHOD_WRAP); } else if (maxCellWidth * count > layoutWidth) { group.setFillViewport(true); setSplitMethod(SPLITMETHOD_WEIGHT); } else { group.setFillViewport(true); setSplitMethod(SPLITMETHOD_EQUALS); } } } super.onMeasure(widthSpec, heightSpec); } private int measureChildWidth(View view, int widthSpec, int heightSpec) { LayoutParams p = (LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), LayoutParams.WRAP_CONTENT); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); view.measure(childWidthSpec, childHeightSpec); return view.getMeasuredWidth() + p.leftMargin + p.rightMargin; } } /** * 根据dip值转化成px值 * * @param dip * @return */ private int dipToPix(float dip) { int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics()); return size; } }