1. 程式人生 > >CoordinatorLayout+ViewPager+SwipeRefreshLayout滑動事件衝突的處理

CoordinatorLayout+ViewPager+SwipeRefreshLayout滑動事件衝突的處理

只是一個搬運工

連結

參考

程式碼(自己加的註釋,好多不懂,勿噴)

public class NestedScrollSwipeRefreshLayout extends SwipeRefreshLayout implements NestedScrollingChild {

    private static final int INVALID_POINTER = -1;
    //最小有效滑動
    private int mTouchSlop;
    private int mActivePointerId = INVALID_POINTER;

    private
NestedScrollingChildHelper mChildHelper; public NestedScrollSwipeRefreshLayout(Context context) { this(context, null); } public NestedScrollSwipeRefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); mChildHelper = new NestedScrollingChildHelper(this
); setNestedScrollingEnabled(true); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } @Override public void setNestedScrollingEnabled(boolean enabled) { mChildHelper.setNestedScrollingEnabled(enabled); } @Override public boolean isNestedScrollingEnabled
() { return mChildHelper.isNestedScrollingEnabled(); } /** * 實現一些跟NestedScrollingParent互動的一些方法,通知父view * @param axes * @return */ @Override public boolean startNestedScroll(int axes) { return mChildHelper.startNestedScroll(axes); } /** * 結束整個流程。 */ @Override public void stopNestedScroll() { mChildHelper.stopNestedScroll(); } @Override public boolean hasNestedScrollingParent() { return mChildHelper.hasNestedScrollingParent(); } /** * 向父view彙報滾動情況,包括子view消費的部分和子view沒有消費的部分。 * 這個函式一般在子view處理scroll後呼叫。 * @param dxConsumed * @param dyConsumed * @param dxUnconsumed * @param dyUnconsumed * @param offsetInWindow * @return 如果父view接受了它的滾動引數,進行了部分消費,則這個函式返回true,否則為false。 */ @Override public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) { return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow); } /** * 一般在MotionEvent.Move裡呼叫 通知父View滑動的距離 * * * 由於窗體進行了移動,如果你記錄了手指最後的位置,需要根據第四個引數offsetInWindow計算偏移量, * 才能保證下一次的touch事件的計算是正確的。 * 一般在子view處理scroll前呼叫 * @param dx * @param dy * @param consumed 第一個元素是父view消費的x方向的滾動距離;第二個元素是父view消費的y方向的滾動距離 * @param offsetInWindow 子View的窗體偏移量 * @return 如果父view接受了它的滾動引數,進行了部分消費,則這個函式返回true,否則為false */ @Override public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); } @Override public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); } @Override public boolean dispatchNestedPreFling(float velocityX, float velocityY) { return mChildHelper.dispatchNestedPreFling(velocityX, velocityY); } //記住最後按下時點的y值 private float mLastMotionY; private final int[] mScrollOffset = new int[2]; private final int[] mScrollConsumed = new int[2]; //標識是否已經開始拖動 private boolean mIsBeginDrag = false; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); switch (action) { case MotionEvent.ACTION_DOWN: { //獲得觸控點id.up的時候id會失效 mActivePointerId = MotionEventCompat.getPointerId(ev, 0); final float initialDownY = getMotionEventY(ev, mActivePointerId); //為-1,多點觸控,之後擡起,把事件交給父佈局處理 if (initialDownY == -1) { return false; } //儲存按下的y值,填充給最後移動的距離 mLastMotionY = initialDownY; //通知父view開始巢狀滾動 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL); super.onInterceptTouchEvent(ev); //初始化標識 mIsBeginDrag = false; break; } case MotionEvent.ACTION_MOVE: { //如果觸控點id 為 -1 把點選事件交給父佈局處理 if (mActivePointerId == INVALID_POINTER) { return false; } //標記觸控點的y值 final float y = getMotionEventY(ev, mActivePointerId); //為-1,多點觸控,之後擡起,把點選事件交給父佈局處理 if (y == -1) { return false; } //計算移動的距離 int deltaY = (int)(mLastMotionY - y); //更新 最後按下時點的y值 mLastMotionY = y; //移動的距離是否有效,更新 是否正在拖動的標識 if (Math.abs(deltaY) >= mTouchSlop) { mIsBeginDrag = true; } /** * 正在拖動,父view消耗了部分view,就交給父view處理 * mScrollOffset[1] 子view視窗y軸的偏移量 * mScrollOffset[1] 父view消費的y軸偏移量 */ if (mIsBeginDrag && dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) { mLastMotionY -= mScrollOffset[1];//修正最後按下的點的y值 deltaY -= mScrollConsumed[1];//修正被父view消耗後的移動的距離 ev.offsetLocation(0, mScrollConsumed[1]);//修正ev的位置 //向父View彙報,如果父view接受了引數,進行了部分消費,返回true,否則返回false if (dispatchNestedScroll(0, 0, 0, deltaY, mScrollOffset)) { mLastMotionY -= mScrollOffset[1];//修正最後按下的點的y值 ev.offsetLocation(0, mScrollOffset[1]);//修正點選的位置 } return false; } else { //否則正常處理 return super.onInterceptTouchEvent(ev); } } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: { //停止巢狀滑動 stopNestedScroll(); //初始化相關值 mActivePointerId = INVALID_POINTER; mIsBeginDrag = false; return super.onInterceptTouchEvent(ev); } } return super.onInterceptTouchEvent(ev); } /** * 返回 座標的y值 * 解決多點觸控的問題 * @param ev * @param activePointerId * @return */ private float getMotionEventY(MotionEvent ev, int activePointerId) { //得到觸點的索引值,範圍是 0 到 ev.getPointerCount()-1; final int index = MotionEventCompat.findPointerIndex(ev, activePointerId); if (index < 0) { return -1; } return MotionEventCompat.getY(ev, index); } }