package com.zhengsr.tablib.view.flow; import android.content.Context; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewParent; import android.widget.Scroller; /** * @author by zhengshaorui on 2019/10/8 * Describe: 滚动类,用来移动 */ class ScrollFlowLayout extends FlowLayout { private static final String TAG = "ScrollFlowLayout"; private int mTouchSlop; private float mLastPos; private float mMovePos; protected int mRightBound; protected int mBottomRound; private boolean isCanMove; private int mScreenWidth; private int mScreenHeight; private VelocityTracker mVelocityTracker; private Scroller mScroller; private int mCurScrollPos; private int mMaximumVelocity; private int mMinimumVelocity; protected boolean isMove; protected int mWidth; protected int mHeight; public ScrollFlowLayout(Context context) { this(context, null); } public ScrollFlowLayout(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public ScrollFlowLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mScreenWidth = getResources().getDisplayMetrics().widthPixels; mScreenHeight = getResources().getDisplayMetrics().heightPixels; mScroller = new Scroller(context); mMaximumVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity(); mMinimumVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity(); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); int count = getChildCount(); //拿到有边界 if (count > 0) { View child = getChildAt(count - 1); mRightBound = child.getRight() + getPaddingRight(); mBottomRound = child.getBottom() + getPaddingBottom(); } /** * TabFlowLayout 或者 LabelFlowLayout 的竖向滚动 */ //todo 这是什么魔性 if else 找机会改一下 if (isVerticalMove()){ if (mViewHeight < mScreenHeight){ if (mBottomRound > mViewHeight){ isCanMove = true; }else{ isCanMove = false; } mHeight = mViewHeight; }else{ //再确认一遍 if (mBottomRound > mScreenHeight){ isCanMove = true; } mHeight = mScreenHeight; //需要减去actionbar 和 状态栏的高度 mHeight = mHeight - getActionBarHeight(getContext()) - getStatusBarHeight(); } if (!isLabelAutoScroll() || !isTabAutoScroll()){ isCanMove = false; } }else{ //TabFlowLayout 横向布局 if (!isVertical()) { //如果是固定宽度 if (mVisualCount != -1){ if (getChildCount() > mVisualCount){ isCanMove = true; }else{ isCanMove = false; } mWidth = mViewWidth; }else { //说明控件没有满屏或者固定宽度 if (mViewWidth < mScreenWidth) { if (mRightBound > mViewWidth) { isCanMove = true; } else { isCanMove = false; } mWidth = mViewWidth; } else { //再确认一遍 if (mRightBound > mScreenWidth) { isCanMove = true; } mWidth = mScreenWidth; } } if (!isTabAutoScroll()) { isCanMove = false; } } } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (!isCanMove){ return super.onInterceptTouchEvent(ev); } final ViewParent parent = getParent(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: if (isVertical()){ mLastPos = ev.getY(); }else { mLastPos = ev.getX(); } //拿到上次的down坐标 if (isVertical()){ mMovePos = ev.getY(); }else { mMovePos = ev.getX(); } if (mScroller != null && !mScroller.isFinished()) { mScroller.abortAnimation(); } if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(ev); //如果能滚动,应该要屏蔽父控件的触摸事件 if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_MOVE: float offset; if (isVerticalMove()){ offset = ev.getY() - mLastPos; }else{ offset = ev.getX() - mLastPos; } if (Math.abs(offset) >= mTouchSlop) { if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); } if (mVelocityTracker == null){ mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); //由父控件接管触摸事件 return true; } if (isVerticalMove()){ mLastPos = ev.getY(); }else { mLastPos = ev.getX(); } break; default: break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); final ViewParent parent = getParent(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: //scroller 向右为负,向左为正 int offset; if (isVerticalMove()){ offset = (int) (mMovePos - event.getY()); }else { offset = (int) (mMovePos - event.getX()); } if (Math.abs(offset) > mTouchSlop){ if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); } } int scrollPos ; if (isVerticalMove()){ scrollPos = getScrollY(); }else{ scrollPos = getScrollX(); } /** * 判断边界 */ if (scrollPos + offset <= 0) { scrollTo(0, 0); return true; } if (isVerticalMove()){ if (scrollPos +offset >= mBottomRound - mHeight){ scrollTo(0,mBottomRound - mHeight); return true; } scrollBy(0, offset); }else{ if (scrollPos +offset >= mRightBound - mWidth) { scrollTo(mRightBound - mWidth, 0); return true; } scrollBy(offset, 0); } isMove = true; if (isVerticalMove()){ mMovePos = event.getY(); }else { mMovePos = event.getX(); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mVelocityTracker.computeCurrentVelocity(1000,mMaximumVelocity); int velocityPos ; if (isVerticalMove()){ velocityPos = (int) mVelocityTracker.getYVelocity(); }else{ velocityPos = (int) mVelocityTracker.getXVelocity(); } if (Math.abs(velocityPos) >= mMinimumVelocity) { if (isVerticalMove()){ mCurScrollPos = getScrollY(); mScroller.fling(0,mCurScrollPos,0,velocityPos, 0,0,0,getHeight()); }else { mCurScrollPos = getScrollX(); mScroller.fling(mCurScrollPos, 0, velocityPos, 0, 0, getWidth(), 0, 0); } if (mVelocityTracker != null) { mVelocityTracker.clear(); mVelocityTracker.recycle(); mVelocityTracker = null; } } break; default: break; } return super.onTouchEvent(event); } @Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()){ int offset; if (isVerticalMove()){ offset = mCurScrollPos - mScroller.getCurrY(); }else { offset = mCurScrollPos - mScroller.getCurrX(); } // 超出边界,进行修正 int scrollPos; if (isVerticalMove()){ if (getScrollY() + offset >= mBottomRound - mHeight) { offset = mBottomRound - mHeight - getScrollY(); } // 超出边界,进行修正 if (getScrollY() + offset <= 0) { offset = -getScrollY(); } scrollBy(0,offset); }else{ if (getScrollX() + offset >= mRightBound - mWidth) { offset = mRightBound - mWidth - getScrollX(); } // 超出边界,进行修正 if (getScrollX() + offset <= 0) { offset = -getScrollX(); } scrollBy(offset,0); } postInvalidate(); } } public int getViewWidth(){ return mWidth; } public boolean isCanMove() { return isCanMove; } public boolean isMove() { return isMove; } public void setMove(boolean move) { isMove = move; } /** * 获取状态栏的高度 * @return */ private int getStatusBarHeight(){ int result = 0; int resourceId = this.getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { result = this.getResources().getDimensionPixelSize(resourceId); } return result; } /** * 拿到 actionbar 的高度 * @param context * @return */ private int getActionBarHeight(Context context){ TypedValue tv = new TypedValue(); if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize,tv,true)){ return TypedValue.complexToDimensionPixelSize(tv.data, context.getResources().getDisplayMetrics()); } return 0; } public boolean isLabelAutoScroll(){ return true; } public boolean isTabAutoScroll(){ return true; } }