图解系列之SwipeRefreshLayout原理(二)

  发布日期:   2018-08-27
  最新修改:   2020-11-27
  阅读次数:   180 次

  • 我们在上篇图解系列之SwipeRefreshLayout原理(一)
  • 中分析了SwipeRefreshLayout中的测量布局及如何确保刷新view一直处于View的最顶层一确保不被遮住等代码。
  • 本篇主要通过图解的方式将SwipeRefreshLayout进行分析,以更直观的方式进行学习
图解
  • 以touch事件触发开始进行剖析

  • 条件:

    • 条件1:

       //判断一系列条件看是否需要进行拦截,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源码如下

      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源码

      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源码

      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时触发, 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);
      }

    }

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

   转载规则

《图解系列之SwipeRefreshLayout原理(二)字》GajAngels 采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。