目录

图解系列之SwipeRefreshLayout原理(二)

目录

  • 我们在上篇图解系列之SwipeRefreshLayout原理(一)
  • 中分析了SwipeRefreshLayout中的测量布局及如何确保刷新view一直处于View的最顶层一确保不被遮住等代码。
  • 本篇主要通过图解的方式将SwipeRefreshLayout进行分析,以更直观的方式进行学习
图解
  • 以touch事件触发开始进行剖析
  • 条件:
  • 条件1:
1
2
3
4
5
6
7
//判断一系列条件看是否需要进行拦截,canChildScrollUp为判断列表是否可以向上滚动(手势从上往下滑)
//mRefreshing处于刷新状态也不会拦截,mNestedScrollInProgress为嵌套滑动机制
if (!isEnabled() || mReturningToStart || canChildScrollUp()
                || mRefreshing || mNestedScrollInProgress) {
            // Fail fast if we're not in a state where a swipe is possible
            return false;
        }
graph TD; A(Start) -->|Touch事件进行分发| B[onInterceptTouchEvent]; B -->|根据条件1判断不直接return false时执行下面流程|C[根据Event的Action类型处理] C -->|ACTION_DOWN| D[记录按下的Y值且设置mIsBeingDragged=false] C -->|ACTION_MOVE|E[达到阈值是mIsBeingDragged=true] C -->|ACTION_UP|F[恢复mIsBeingDragged值为false] D-->G[return mIsBeingDragged确定是否拦截] E-->G F-->G G-->|mIsBeingDragged=false时进入|H[onTouchEvent] H -->|根据条件1判断需要消费该事件时继续执行下面流程|I[根据Event的Action类型处理] I -->|ACTION_DOWN| J[获取此时的mActivePointerId重置mIsBeingDragged为false] I-->|ACTION_MOVE|K[根据down中获取的mActivityPointerId获取y值来处理刷新View的移动操作] I -->|ACTION_UP|L[根据滑动的值来判断是否触发刷新操作]
  • onInterceptTouchEvent源码如下
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public boolean onInterceptTouchEvent(MotionEvent ev) {
    ensureTarget();
    
    final int action = ev.getActionMasked();
    int pointerIndex;
    
    //如果正在执行返回到开始位置的动画且触摸为按下时,置为false进行中断操作
    if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
        mReturningToStart = false;
    }
    
    //判断一系列条件看是否需要进行拦截,canChildScrollUp为判断列表是否可以向上滚动(手势从上往下滑)
    //mRefreshing处于刷新状态也不会拦截,mNestedScrollInProgress为嵌套滑动机制
    if (!isEnabled() || mReturningToStart || canChildScrollUp()
            || mRefreshing || mNestedScrollInProgress) {
        // Fail fast if we're not in a state where a swipe is possible
        return false;
    }

    switch (action) {
        case MotionEvent.ACTION_DOWN:
            setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCircleView.getTop());
            mActivePointerId = ev.getPointerId(0);
            mIsBeingDragged = false;

            pointerIndex = ev.findPointerIndex(mActivePointerId);
            if (pointerIndex < 0) {
                return false;
            }
            //获取刚按下的y值
            mInitialDownY = ev.getY(pointerIndex);
            break;

        case MotionEvent.ACTION_MOVE:
            if (mActivePointerId == INVALID_POINTER) {
                Log.e(LOG_TAG, "Got ACTION_MOVE event but don't have an active pointer id.");
                return false;
            }

            pointerIndex = ev.findPointerIndex(mActivePointerId);
            if (pointerIndex < 0) {
                return false;
            }
            //startDragging中判断是否达到拖拽的阈值
            final float y = ev.getY(pointerIndex);
            startDragging(y);
            break;

        case MotionEvent.ACTION_POINTER_UP:
            onSecondaryPointerUp(ev);
            break;

        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            mIsBeingDragged = false;
            mActivePointerId = INVALID_POINTER;
            break;
    }

    return mIsBeingDragged;
}
  • startDragging源码
1
2
3
4
5
6
7
8
9
private void startDragging(float y) {
        final float yDiff = y - mInitialDownY;
        if (yDiff > mTouchSlop && !mIsBeingDragged) {
            mInitialMotionY = mInitialDownY + mTouchSlop;
            //大于阈值置位true
            mIsBeingDragged = true;
            mProgress.setAlpha(STARTING_PROGRESS_ALPHA);
        }
    }
  • onTouchEvent源码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public boolean onTouchEvent(MotionEvent ev) {
        final int action = ev.getActionMasked();
        int pointerIndex = -1;

        if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
            mReturningToStart = false;
        }

        if (!isEnabled() || mReturningToStart || canChildScrollUp()
                || mRefreshing || mNestedScrollInProgress) {
            // Fail fast if we're not in a state where a swipe is possible
            return false;
        }

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mActivePointerId = ev.getPointerId(0);
                mIsBeingDragged = false;
                break;

            case MotionEvent.ACTION_MOVE: {
                pointerIndex = ev.findPointerIndex(mActivePointerId);
                if (pointerIndex < 0) {
                    Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id.");
                    return false;
                }

                final float y = ev.getY(pointerIndex);
                startDragging(y);

                if (mIsBeingDragged) {
                    final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
                    if (overscrollTop > 0) {
                        moveSpinner(overscrollTop);
                    } else {
                        return false;
                    }
                }
                break;
            }
            case MotionEvent.ACTION_POINTER_DOWN: {
                pointerIndex = ev.getActionIndex();
                if (pointerIndex < 0) {
                    Log.e(LOG_TAG,
                            "Got ACTION_POINTER_DOWN event but have an invalid action index.");
                    return false;
                }
                mActivePointerId = ev.getPointerId(pointerIndex);
                break;
            }

            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(ev);
                break;

            case MotionEvent.ACTION_UP: {
                pointerIndex = ev.findPointerIndex(mActivePointerId);
                if (pointerIndex < 0) {
                    Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id.");
                    return false;
                }

                if (mIsBeingDragged) {
                    final float y = ev.getY(pointerIndex);
                    final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
                    mIsBeingDragged = false;
                    finishSpinner(overscrollTop);
                }
                mActivePointerId = INVALID_POINTER;
                return false;
            }
            case MotionEvent.ACTION_CANCEL:
                return false;
        }

        return true;
    } 
            
  • finishSpinner 源码如下,该方法在up时触发,
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 private void finishSpinner(float overscrollTop) {
        if (overscrollTop > mTotalDragDistance) {
               //此时滑动距离大于阈值,触发刷新操作
            setRefreshing(true, true /* notify */);
        } else {
            // cancel refresh
            mRefreshing = false;
            mProgress.setStartEndTrim(0f, 0f);
            Animation.AnimationListener listener = null;
            if (!mScale) {
                listener = new Animation.AnimationListener() {

                    @Override
                    public void onAnimationStart(Animation animation) {
                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {
                        if (!mScale) {
                            startScaleDownAnimation(null);
                        }
                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {
                    }

                };
            }
            animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener);
            mProgress.setArrowEnabled(false);
        }
    }
总结
  • 可结合流程图来阅读代码,可形象化得理解,一开始阅读不必拘泥于个别细节,
  • 先把大体流程弄懂。本人的阅读思路,小伙伴有其他的方法也可在下面分享出来哦