返回介绍

4.​2. shouldInterceptTouchEvent() 方法的实现

发布于 2024-12-23 21:11:39 字数 6668 浏览 0 评论 0 收藏 0

在这里我们假设大家都清楚了 Android 的事件分发机制,如果不清楚请看 这里 ,要想处理触摸事件,我们需要在 onInterceptTouchEvent(MotionEvent ev) 方法里判断是否需要拦截这次触摸事件,如果此方法返回 true 则触摸事件将会交给 onTouchEvent(MotionEvent event) 处理,这样我们就能处理触摸事件了,所以我们在上面的使用方法里会这样写:

  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    return mDragger.shouldInterceptTouchEvent(ev);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    mDragger.processTouchEvent(event);
    return true;
  }

这样就将是否拦截触摸事件,以及处理触摸事件委托给 ViewDragHelper 来处理了,所以我们先来看看 ViewDragHelpershouldInterceptTouchEvent(); 方法的实现:

  public boolean shouldInterceptTouchEvent(MotionEvent ev) {
    //获取 action
    final int action = MotionEventCompat.getActionMasked(ev);
    //获取 action 对应的 index
    final int actionIndex = MotionEventCompat.getActionIndex(ev);

    //如果是按下的 action 则重置一些信息,包括各种事件点的数组
    if (action == MotionEvent.ACTION_DOWN) {
      // Reset things for a new event stream, just in case we didn't get
      // the whole previous stream.
      cancel();
    }
    //初始化 mVelocityTracker
    if (mVelocityTracker == null) {
      mVelocityTracker = VelocityTracker.obtain();
    }
    mVelocityTracker.addMovement(ev);

    //根据 action 来做相应的处理
    switch (action) {
      case MotionEvent.ACTION_DOWN: {
        final float x = ev.getX();
        final float y = ev.getY();
        //获取这个事件对应的 pointerId,一般情况下只有一个手指触摸时为 0
        //两个手指触摸时第二个手指触摸返回的 pointerId 为 1,以此类推
        final int pointerId = MotionEventCompat.getPointerId(ev, 0);
        //保存点的数据
        //TODO (1)
        saveInitialMotion(x, y, pointerId);
        //获取当前触摸点下最顶层的子 View
        //TODO (2)
        final View toCapture = findTopChildUnder((int) x, (int) y);

        //如果 toCapture 是已经捕获的 View,而且正在处于被释放状态
        //那么就重新捕获
        if (toCapture == mCapturedView && mDragState == STATE_SETTLING) {
          tryCaptureViewForDrag(toCapture, pointerId);
        }

        //如果触摸了边缘,回调 callback 的 onEdgeTouched() 方法
        final int edgesTouched = mInitialEdgesTouched[pointerId];
        if ((edgesTouched & mTrackingEdges) != 0) {
          mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
        }
        break;
      }

      //当又有一个手指触摸时
      case MotionEventCompat.ACTION_POINTER_DOWN: {
        final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
        final float x = MotionEventCompat.getX(ev, actionIndex);
        final float y = MotionEventCompat.getY(ev, actionIndex);

        //保存触摸信息
        saveInitialMotion(x, y, pointerId);

        //因为同一时间 ViewDragHelper 只能操控一个 View,所以当有新的手指触摸时
        //只讨论当无触摸发生时,回调边缘触摸的 callback
        //或者正在处于释放状态时重新捕获 View
        if (mDragState == STATE_IDLE) {
          final int edgesTouched = mInitialEdgesTouched[pointerId];
          if ((edgesTouched & mTrackingEdges) != 0) {
            mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
          }
        } else if (mDragState == STATE_SETTLING) {
          // Catch a settling view if possible.
          final View toCapture = findTopChildUnder((int) x, (int) y);
          if (toCapture == mCapturedView) {
            tryCaptureViewForDrag(toCapture, pointerId);
          }
        }
        break;
      }

      //当手指移动时
      case MotionEvent.ACTION_MOVE: {
        if (mInitialMotionX == null || mInitialMotionY == null) break;

        // First to cross a touch slop over a draggable view wins. Also report edge drags.
        //得到触摸点的数量,并循环处理,只处理第一个发生了拖拽的事件
        final int pointerCount = MotionEventCompat.getPointerCount(ev);
        for (int i = 0; i < pointerCount; i++) {
          final int pointerId = MotionEventCompat.getPointerId(ev, i);
          final float x = MotionEventCompat.getX(ev, i);
          final float y = MotionEventCompat.getY(ev, i);
          //获得拖拽偏移量
          final float dx = x - mInitialMotionX[pointerId];
          final float dy = y - mInitialMotionY[pointerId];
          //获取当前触摸点下最顶层的子 View
          final View toCapture = findTopChildUnder((int) x, (int) y);
          //如果找到了最顶层 View,并且产生了拖动(checkTouchSlop() 返回 true)
          //TODO (3)
          final boolean pastSlop = toCapture != null && checkTouchSlop(toCapture, dx, dy);
          if (pastSlop) {
            //根据 callback 的四个方法 getView[Horizontal|Vertical]DragRange 和
            //clampViewPosition[Horizontal|Vertical]来检查是否可以拖动
            final int oldLeft = toCapture.getLeft();
            final int targetLeft = oldLeft + (int) dx;
            final int newLeft = mCallback.clampViewPositionHorizontal(toCapture,
                targetLeft, (int) dx);
            final int oldTop = toCapture.getTop();
            final int targetTop = oldTop + (int) dy;
            final int newTop = mCallback.clampViewPositionVertical(toCapture, targetTop,
                (int) dy);
            final int horizontalDragRange = mCallback.getViewHorizontalDragRange(
                toCapture);
            final int verticalDragRange = mCallback.getViewVerticalDragRange(toCapture);
            //如果都不允许移动则跳出循环
            if ((horizontalDragRange == 0 || horizontalDragRange > 0
                && newLeft == oldLeft) && (verticalDragRange == 0
                || verticalDragRange > 0 && newTop == oldTop)) {
              break;
            }
          }
          //记录并回调是否有边缘触摸
          reportNewEdgeDrags(dx, dy, pointerId);
          if (mDragState == STATE_DRAGGING) {
            // Callback might have started an edge drag
            break;
          }
          //如果产生了拖动则调用 tryCaptureViewForDrag()
          //TODO (4)
          if (pastSlop && tryCaptureViewForDrag(toCapture, pointerId)) {
            break;
          }
        }
        //保存触摸点的信息
        saveLastMotion(ev);
        break;
      }

      //当有一个手指抬起时,清除这个手指的触摸数据
      case MotionEventCompat.ACTION_POINTER_UP: {
        final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
        clearMotionHistory(pointerId);
        break;
      }

      //清除所有触摸数据
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL: {
        cancel();
        break;
      }
    }

    //如果 mDragState 等于正在拖拽则返回 true
    return mDragState == STATE_DRAGGING;
  }

上面就是整个 shouldInterceptTouchEvent() 的实现,上面的注释也足够清楚了,我们这里就先不分析某一种触摸事件,大家可以看到我上面留了几个 TODO,下文会一起分析,这里我假设大家都已经对触摸事件分发处理都有充分的理解了,我们下面就直接看 ViewDragHelperprocessTouchEvent() 方法的实现。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文