您当前的位置: 首页 >  android

韩曙亮

暂无认证

  • 2浏览

    0关注

    1068博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )

韩曙亮 发布时间:2021-07-29 09:36:22 ,浏览量:2

Android 事件分发 系列文章目录

【Android 事件分发】事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) 【Android 事件分发】事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup ) 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 一 ) 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 二 ) 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 三 ) 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 四 | View 事件传递机制 ) 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 五 ) 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 六 ) 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 七 )

【Android 事件分发】ItemTouchHelper 简介 ( 拖动/滑动事件 | ItemTouchHelper.Callback 回调 ) 【Android 事件分发】ItemTouchHelper 实现侧滑删除 ( 设置滑动方向 | 启用滑动操作 | 滑动距离判定 | 滑动速度判定 | 设置动画时间 | 设置侧滑触发操作 ) 【Android 事件分发】ItemTouchHelper 实现拖动排序 ( 设置滑动方向 | 启启用长按拖动功能 | 拖动距离判定 | 设置拖动触发操作 )

【Android 事件分发】ItemTouchHelper 事件分发源码分析 ( 绑定 RecyclerView ) 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 ) 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )

文章目录
  • Android 事件分发 系列文章目录
  • 一、onTouchEvent 事件消费源码分析
    • 1、onTouchEvent 方法
    • 2、moveIfNecessary 方法
  • 二、ItemTouchHelper 涉及到的本博客相关源码
  • 三、博客资源

一、onTouchEvent 事件消费源码分析

在上一篇博客 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 ) 主要分析了 给 RecyclerView 设置的 OnItemTouchListener 监听器的 onInterceptTouchEvent 触摸事件拦截方法 , 本篇博客中主要分析另外一个 触摸事件消费方法 onTouchEvent ;

在 onTouchEvent 事件消费 中 , 只处理 MotionEvent.ACTION_MOVE 事件 , 不处理其它事件 ;

1、onTouchEvent 方法

首先要获取操作的条目组件 ,

ViewHolder viewHolder = mSelected;

其中 mSelected 是在第一次按下时进行的赋值 , 有了 mSelected 值后 , 开始处理滑动事件 ;

如果没有获取到 mSelected , 则直接返回 ;

            if (viewHolder == null) {
                return;
            }

如果当前处于拖动事件 MotionEvent.ACTION_MOVE , 则进行拖动事件处理 ;

拖动事件的核心是 moveIfNecessary 方法 , 该方法是处理滑动事件的核心方法

            switch (action) {
                case MotionEvent.ACTION_MOVE: {
                	// 检查该操作是否在拖动 
                    // Find the index of the active pointer and fetch its position
                    if (activePointerIndex >= 0) {
                    	// 记录修改偏移值 
                        updateDxDy(event, mSelectedFlags, activePointerIndex);
                        // 处理拖动事件
                        moveIfNecessary(viewHolder);
                        mRecyclerView.removeCallbacks(mScrollRunnable);
                        mScrollRunnable.run();
                        mRecyclerView.invalidate();
                    }
                    break;
                }
2、moveIfNecessary 方法

moveIfNecessary 方法中主要进行拖动事件判定 , 一般是拖动条目组件进行重新排序 ;

先获取开发者自定义的 Callback 中的 public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder)方法返回值 , 如果开发者没有设置 , 就使用默认值 ;

该值的作用是 设置 拖动幅度 , 组件在宽度 / 高度 上移动超过该比例 , 就认为拖动触发, 执行拖动相关操作 ;

		// 该方法就是 开发者 自定义 Callback 中的 
		// public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) 
		// 方法的作用是设置 拖动幅度
		// 组件在宽度 / 高度 上移动超过该比例 , 就认为拖动触发, 执行拖动相关操作
		// 拖动多少系数 , 才算完成 拖动操作 
        final float threshold = mCallback.getMoveThreshold(viewHolder);
        final int x = (int) (mSelectedStartX + mDx);
        final int y = (int) (mSelectedStartY + mDy);

获取到拖动系数后 , 使用了 threshold 系数 乘以 水平 / 垂直 方向上的条目组件 宽度 / 高度 ;

  • 如果拖动比例超过在 水平 / 垂直 方向上的条目组件 宽度 / 高度 乘以 threshold 的值 , 则拖动判定成功 , 执行响应的方法 ;
  • 如果拖动比例没有超过该值 , 说明没有触发拖动操作 , 直接返回 ;
        // 在该判断中 , 使用了 threshold 系数 乘以 水平 / 垂直 方向上的条目组件宽度 ; 
        // 如果拖动比例超过在 水平 / 垂直 方向上的条目组件 宽度 / 高度 乘以 threshold 的值 
        // 则拖动判定成功 , 执行响应的方法 
        // 如果拖动比例没有超过该值 , 说明没有触发拖动操作 , 直接返回 
        if (Math.abs(y - viewHolder.itemView.getTop()) = 0) {
                checkSelectForSwipe(action, event, activePointerIndex);
            }
            // 按下第一次后 , mSelected 便进行赋值
            // 有了 mSelected 值后 , 开始在滑动中处理
            ViewHolder viewHolder = mSelected;
            if (viewHolder == null) {
                return;
            }
            switch (action) {
                case MotionEvent.ACTION_MOVE: {
                	// 检查该操作是否在拖动 
                    // Find the index of the active pointer and fetch its position
                    if (activePointerIndex >= 0) {
                    	// 记录修改偏移值 
                        updateDxDy(event, mSelectedFlags, activePointerIndex);
                        // 处理拖动事件
                        moveIfNecessary(viewHolder);
                        mRecyclerView.removeCallbacks(mScrollRunnable);
                        mScrollRunnable.run();
                        mRecyclerView.invalidate();
                    }
                    break;
                }
                case MotionEvent.ACTION_CANCEL:
                	// 取消操作 , 没有实质的内容 
                    if (mVelocityTracker != null) {
                        mVelocityTracker.clear();
                    }
                    // fall through
                case MotionEvent.ACTION_UP:
                	// 抬起操作
                    select(null, ACTION_STATE_IDLE);
                    mActivePointerId = ACTIVE_POINTER_ID_NONE;
                    break;
                case MotionEvent.ACTION_POINTER_UP: {
                    final int pointerIndex = event.getActionIndex();
                    final int pointerId = event.getPointerId(pointerIndex);
                    if (pointerId == mActivePointerId) {
                        // This was our active pointer going up. Choose a new
                        // active pointer and adjust accordingly.
                        final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                        mActivePointerId = event.getPointerId(newPointerIndex);
                        updateDxDy(event, mSelectedFlags, pointerIndex);
                    }
                    break;
                }
            }
        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
            if (!disallowIntercept) {
                return;
            }
            select(null, ACTION_STATE_IDLE);
        }
    };


	// 该方法作用 : 
	// 用户按下 RecyclerView 中的某个条目 
	// 该方法用于找到按下的条目 View , 并设置给 RecoverAnimation 恢复动画 
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    RecoverAnimation findAnimation(MotionEvent event) {
        if (mRecoverAnimations.isEmpty()) {
            return null;
        }
        // 找到手指按下所在位置的条目的 View 组件
        // 查找手指按下的 View 子组件 , 该子组件时 RecyclerView 中的一个条目 
        View target = findChildView(event);
        // 遍历恢复动画 
        // 动画中有 mViewHolder 成员 , mViewHolder 中有 itemView 成员 
        // 设置  anim.mViewHolder.itemView 为手指按下的子组件 
        // 即设置该动画作用于 RecyclerView 的哪个条目上 ; 
        for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {
            final RecoverAnimation anim = mRecoverAnimations.get(i);
            if (anim.mViewHolder.itemView == target) {
                return anim;
            }
        }
        return null;
    }

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    View findChildView(MotionEvent event) {
        // first check elevated views, if none, then call RV
        // 根据按下的 X, Y 坐标 , 查找对应的条目 
        final float x = event.getX();
        final float y = event.getY();
        // 如果 mSelected 成员不为空 , 则直接使用 , 分支中直接返回了 
        if (mSelected != null) {
            final View selectedView = mSelected.itemView;
            if (hitTest(selectedView, x, y, mSelectedStartX + mDx, mSelectedStartY + mDy)) {
                return selectedView;
            }
        }
        // 如果 mSelected 为空 , 则开始遍历进行检测 
        for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {
            final RecoverAnimation anim = mRecoverAnimations.get(i);
            final View view = anim.mViewHolder.itemView;
            // 根据当前按下的坐标 , 找到列表条目对应的 View 组件 
            if (hitTest(view, x, y, anim.mX, anim.mY)) {
                return view;
            }
        }
        return mRecyclerView.findChildViewUnder(x, y);
    }

	// 判断删上下左右边距 是否在对应子组件范围内 
    private static boolean hitTest(View child, float x, float y, float left, float top) {
        return x >= left
                && x = top
                && y  (mActionState * DIRECTION_FLAG_COUNT);
            mSelectedStartX = selected.itemView.getLeft();
            mSelectedStartY = selected.itemView.getTop();
            mSelected = selected;

            if (actionState == ACTION_STATE_DRAG) {
                mSelected.itemView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
            }
        }
        final ViewParent rvParent = mRecyclerView.getParent();
        if (rvParent != null) {
            rvParent.requestDisallowInterceptTouchEvent(mSelected != null);
        }
        if (!preventLayout) {
            mRecyclerView.getLayoutManager().requestSimpleAnimationsInNextLayout();
        }
        mCallback.onSelectedChanged(mSelected, mActionState);
        mRecyclerView.invalidate();
    }

	// 计算当前移动的位置 
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void updateDxDy(MotionEvent ev, int directionFlags, int pointerIndex) {
        final float x = ev.getX(pointerIndex);
        final float y = ev.getY(pointerIndex);

        // Calculate the distance moved
        mDx = x - mInitialTouchX;
        mDy = y - mInitialTouchY;
        if ((directionFlags & LEFT) == 0) {
            mDx = Math.max(0, mDx);
        }
        if ((directionFlags & RIGHT) == 0) {
            mDx = Math.min(0, mDx);
        }
        if ((directionFlags & UP) == 0) {
            mDy = Math.max(0, mDy);
        }
        if ((directionFlags & DOWN) == 0) {
            mDy = Math.min(0, mDy);
        }
    }

    /**
     * Checks if we should swap w/ another view holder.
     * 拖动事件判定 , 一般是拖动交换条目组件
     */
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void moveIfNecessary(ViewHolder viewHolder) {
        if (mRecyclerView.isLayoutRequested()) {
            return;
        }
        if (mActionState != ACTION_STATE_DRAG) {
            return;
        }

		// 该方法就是 开发者 自定义 Callback 中的 
		// public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) 
		// 方法的作用是设置 拖动幅度
		// 组件在宽度 / 高度 上移动超过该比例 , 就认为拖动触发, 执行拖动相关操作
		// 拖动多少系数 , 才算完成 拖动操作 
        final float threshold = mCallback.getMoveThreshold(viewHolder);
        final int x = (int) (mSelectedStartX + mDx);
        final int y = (int) (mSelectedStartY + mDy);
        
        // 在该判断中 , 使用了 threshold 系数 乘以 水平 / 垂直 方向上的条目组件宽度 ; 
        // 如果拖动比例超过在 水平 / 垂直 方向上的条目组件 宽度 / 高度 乘以 threshold 的值 
        // 则拖动判定成功 , 执行响应的方法 
        // 如果拖动比例没有超过该值 , 说明没有触发拖动操作 , 直接返回 
        if (Math.abs(y - viewHolder.itemView.getTop())             
关注
打赏
1663594092
查看更多评论
0.0483s