前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android--ItemTouchHelper源码分析

Android--ItemTouchHelper源码分析

作者头像
aruba
发布2020-07-02 15:28:55
8110
发布2020-07-02 15:28:55
举报
文章被收录于专栏:android技术android技术
ItemTouchHelper的基本使用上次已经介绍了,今天来分析下ItemTouchHelper的源码,我们从attachToRecyclerView方法入手
代码语言:javascript
复制
    /**
     * Attaches the ItemTouchHelper to the provided RecyclerView. If TouchHelper is already
     * attached to a RecyclerView, it will first detach from the previous one. You can call this
     * method with {@code null} to detach it from the current RecyclerView.
     *
     * @param recyclerView The RecyclerView instance to which you want to add this helper or
     *                     {@code null} if you want to remove ItemTouchHelper from the current
     *                     RecyclerView.
     */
    public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {
        if (mRecyclerView == recyclerView) {
            return; // nothing to do
        }
        if (mRecyclerView != null) {
            destroyCallbacks();
        }
        mRecyclerView = recyclerView;
        if (mRecyclerView != null) {
            final Resources resources = recyclerView.getResources();
            mSwipeEscapeVelocity = resources
                    .getDimension(R.dimen.item_touch_helper_swipe_escape_velocity);
            mMaxSwipeVelocity = resources
                    .getDimension(R.dimen.item_touch_helper_swipe_escape_max_velocity);
            setupCallbacks();
        }
    }
可以看到就是将recyclerView赋值给mRecyclerView ,并调用了setupCallbacks方法,来到setupCallbacks方法:
代码语言:javascript
复制
    private void setupCallbacks() {
        ViewConfiguration vc = ViewConfiguration.get(mRecyclerView.getContext());
        mSlop = vc.getScaledTouchSlop();
        //说明ItemTouchHelper本身继承了ItemDecoration
        //ItemDecoration我们一般用于画分割线
        mRecyclerView.addItemDecoration(this);
        mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);
        mRecyclerView.addOnChildAttachStateChangeListener(this);
        initGestureDetector();
    }
其中初始化了判断移动的阀值mSlop ,然后调用mRecyclerView.addItemDecoration(this),我们看ItemTouchHelper继承ItemDecoration干了什么,发现ItemTouchHelper改写了onDraw和onDrawOver方法
代码语言:javascript
复制
    /**
     * 该方法在recyclerView的draw方法中调用,draw方法会调用onDraw方法
     *
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        float dx = 0, dy = 0;
        if (mSelected != null) {
            getSelectedDxDy(mTmpPosition);
            dx = mTmpPosition[0];
            dy = mTmpPosition[1];
        }
        mCallback.onDrawOver(c, parent, mSelected,
                mRecoverAnimations, mActionState, dx, dy);
    }

    /**
     * 该方法在recyclerView的onDraw方法中调用
     *
     * @param c
     * @param parent
     * @param state
     */
    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        // we don't know if RV changed something so we should invalidate this index.
        mOverdrawChildPosition = -1;
        float dx = 0, dy = 0;
        if (mSelected != null) {
            getSelectedDxDy(mTmpPosition);
            dx = mTmpPosition[0];
            dy = mTmpPosition[1];
        }
        mCallback.onDraw(c, parent, mSelected,
                mRecoverAnimations, mActionState, dx, dy);
    }
两个方法都差不多,先判断mSelected 是不是null,获取dx,dy,即横向的偏移量和纵向的偏移量;接下来调用mCallback的方法,mCallback就是我们使用的ItemTouchHelper.Callback,下面是Callback的onDraw方法
代码语言:javascript
复制
        void onDraw(Canvas c, RecyclerView parent, ViewHolder selected,
                    List<ItemTouchHelper.RecoverAnimation> recoverAnimationList,
                    int actionState, float dX, float dY) {
            final int recoverAnimSize = recoverAnimationList.size();
            for (int i = 0; i < recoverAnimSize; i++) {
                final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i);
                anim.update();
                final int count = c.save();
                onChildDraw(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState,
                        false);
                c.restoreToCount(count);
            }
            if (selected != null) {
                final int count = c.save();
                onChildDraw(c, parent, selected, dX, dY, actionState, true);
                c.restoreToCount(count);
            }
        }
我们关注onChildDraw方法,我们追踪代码发现,最后调用的是ItemTouchUIUtilImpl中的onDraw和onDrawOver方法,ItemTouchUIUtilImpl是ItemTouchUIUtil的实现类。mSelected 其实就是我们选中的ViewHodler,在接下来我们的分析中会知道,这边先提一下。下面贴一下ItemTouchUIUtilImpl的代码
代码语言:javascript
复制
/**
 * Package private class to keep implementations. Putting them inside ItemTouchUIUtil makes them
 * public API, which is not desired in this case.
 */
class ItemTouchUIUtilImpl {
    static class Lollipop extends Honeycomb {
        @Override
        public void onDraw(Canvas c, RecyclerView recyclerView, View view,
                float dX, float dY, int actionState, boolean isCurrentlyActive) {
            if (isCurrentlyActive) {
                Object originalElevation = view.getTag(R.id.item_touch_helper_previous_elevation);
                if (originalElevation == null) {
                    originalElevation = ViewCompat.getElevation(view);
                    float newElevation = 1f + findMaxElevation(recyclerView, view);
                    ViewCompat.setElevation(view, newElevation);
                    view.setTag(R.id.item_touch_helper_previous_elevation, originalElevation);
                }
            }
            super.onDraw(c, recyclerView, view, dX, dY, actionState, isCurrentlyActive);
        }

        private float findMaxElevation(RecyclerView recyclerView, View itemView) {
            final int childCount = recyclerView.getChildCount();
            float max = 0;
            for (int i = 0; i < childCount; i++) {
                final View child = recyclerView.getChildAt(i);
                if (child == itemView) {
                    continue;
                }
                final float elevation = ViewCompat.getElevation(child);
                if (elevation > max) {
                    max = elevation;
                }
            }
            return max;
        }

        @Override
        public void clearView(View view) {
            final Object tag = view.getTag(R.id.item_touch_helper_previous_elevation);
            if (tag != null && tag instanceof Float) {
                ViewCompat.setElevation(view, (Float) tag);
            }
            view.setTag(R.id.item_touch_helper_previous_elevation, null);
            super.clearView(view);
        }
    }

    static class Honeycomb implements ItemTouchUIUtil {

        @Override
        public void clearView(View view) {
            ViewCompat.setTranslationX(view, 0f);
            ViewCompat.setTranslationY(view, 0f);
        }

        @Override
        public void onSelected(View view) {

        }

        @Override
        public void onDraw(Canvas c, RecyclerView recyclerView, View view,
                float dX, float dY, int actionState, boolean isCurrentlyActive) {
            ViewCompat.setTranslationX(view, dX);
            ViewCompat.setTranslationY(view, dY);
        }

        @Override
        public void onDrawOver(Canvas c, RecyclerView recyclerView,
                View view, float dX, float dY, int actionState, boolean isCurrentlyActive) {

        }
    }

    static class Gingerbread implements ItemTouchUIUtil {

        private void draw(Canvas c, RecyclerView parent, View view,
                float dX, float dY) {
            c.save();
            c.translate(dX, dY);
            parent.drawChild(c, view, 0);
            c.restore();
        }

        @Override
        public void clearView(View view) {
            view.setVisibility(View.VISIBLE);
        }

        @Override
        public void onSelected(View view) {
            view.setVisibility(View.INVISIBLE);
        }

        @Override
        public void onDraw(Canvas c, RecyclerView recyclerView, View view,
                float dX, float dY, int actionState, boolean isCurrentlyActive) {
            if (actionState != ItemTouchHelper.ACTION_STATE_DRAG) {
                draw(c, recyclerView, view, dX, dY);
            }
        }

        @Override
        public void onDrawOver(Canvas c, RecyclerView recyclerView,
                View view, float dX, float dY,
                int actionState, boolean isCurrentlyActive) {
            if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
                draw(c, recyclerView, view, dX, dY);
            }
        }
    }
}
到了这里,我们发现,都是canvas的矩阵变换效果(api28中是设置View的属性),也就是我们拖拽和侧滑,最终的动画效果就是利用的canvas,那么具体我们要执行到哪个ViewHodler上,是在哪里判断的呢?我们继续来到setupCallbacks方法
代码语言:javascript
复制
    private void setupCallbacks() {
        ViewConfiguration vc = ViewConfiguration.get(mRecyclerView.getContext());
        mSlop = vc.getScaledTouchSlop();
        //说明ItemTouchHelper本事实现了ItemDecoration
        //ItemDecoration我们一般用于画分割线
        mRecyclerView.addItemDecoration(this);
        //addOnItemTouchListener方法做了什么?
        mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);
        mRecyclerView.addOnChildAttachStateChangeListener(this);
        initGestureDetector();
    }
之前分析了mRecyclerView.addItemDecoration方法,知道了RecyclerView每次onDraw的时候,都会调用ItemTouchHelper.Callback的onDraw方法,我们再分析mRecyclerView.addOnItemTouchListener做了什么?来到RecyclerView的addOnItemTouchListener方法
代码语言:javascript
复制
    /**
     * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
     * to child views or this view's standard scrolling behavior.
     *
     * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
     * returns true from
     * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
     * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
     * for each incoming MotionEvent until the end of the gesture.</p>
     *
     * @param listener Listener to add
     * @see SimpleOnItemTouchListener
     */
    public void addOnItemTouchListener(OnItemTouchListener listener) {
        mOnItemTouchListeners.add(listener);
    }
添加到了mOnItemTouchListeners集合中,那么mOnItemTouchListeners用来做什么呢?我们发现在RecyclerView的dispatchOnItemTouchIntercept方法中,用到了这个mOnItemTouchListeners集合
而dispatchOnItemTouchIntercept方法是在RecyclerView的onInterceptTouchEvent事件中调用的
代码语言:javascript
复制
    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        if (mLayoutFrozen) {
            // When layout is frozen,  RV does not intercept the motion event.
            // A child view e.g. a button may still get the click.
            return false;
        }
        if (dispatchOnItemTouchIntercept(e)) {
            cancelTouch();
            //消费了事件,直接返回
            return true;
        }
...
    }
代码语言:javascript
复制
   private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
        final int action = e.getAction();
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
            mActiveOnItemTouchListener = null;
        }

        final int listenerCount = mOnItemTouchListeners.size();
        for (int i = 0; i < listenerCount; i++) {
            final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
            if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
                //赋值
                mActiveOnItemTouchListener = listener;
                return true;
            }
        }
        return false;
    }
也就是说,事件分发时,在onInterceptTouchEvent方法中又传递给了ItemTouchHelper的OnItemTouchListener的onInterceptTouchEvent方法,并且如果该方法返回了true,则消费事件,并且又把这个OnItemTouchListener 赋值给了mActiveOnItemTouchListener,我们再看跟踪这个mActiveOnItemTouchListener,最终发现了调用的地方
代码语言:javascript
复制
    @Override
    public boolean onTouchEvent(MotionEvent e) {
        if (mLayoutFrozen || mIgnoreMotionEventTillDown) {
            return false;
        }
        if (dispatchOnItemTouch(e)) {
            cancelTouch();
            return true;
        }
...
      }

    private boolean dispatchOnItemTouch(MotionEvent e) {
        final int action = e.getAction();
        if (mActiveOnItemTouchListener != null) {
            if (action == MotionEvent.ACTION_DOWN) {
                // Stale state from a previous gesture, we're starting a new one. Clear it.
                mActiveOnItemTouchListener = null;
            } else {
                mActiveOnItemTouchListener.onTouchEvent(this, e);
                if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
                    // Clean up for the next gesture.
                    mActiveOnItemTouchListener = null;
                }
                return true;
            }
        }

        // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
        // as called from onInterceptTouchEvent; skip it.
        if (action != MotionEvent.ACTION_DOWN) {
            final int listenerCount = mOnItemTouchListeners.size();
            for (int i = 0; i < listenerCount; i++) {
                final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
                if (listener.onInterceptTouchEvent(this, e)) {
                    mActiveOnItemTouchListener = listener;
                    return true;
                }
            }
        }
        return false;
    }
如果事件分发到onTouchEvent(onInterceptTouchEvent为true或者子控件没有消费事件),那么dispatchOnItemTouch方法会被调用,在不是ACTION_DOWN事件的情况下,如果mActiveOnItemTouchListener不为null,mActiveOnItemTouchListener的onTouchEvent方法会被调用,并且返回true,如果mActiveOnItemTouchListener为null,则又会调用OnItemTouchListener的onInterceptTouchEvent方法
值得注意的是onInterceptTouchEvent方法中,OnItemTouchListener是能接受到ACTION_DOWN事件的,但是onTouchEvent事件中,OnItemTouchListener不能接受到ACTION_DOWN事件
上述事件分发的方法,调用有点乱,没什么头绪,那么我们先来看OnItemTouchListener这个对象,该对象在ItemTouchHelper中
代码语言:javascript
复制
    private final OnItemTouchListener mOnItemTouchListener
            = new OnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent event) {
            mGestureDetector.onTouchEvent(event);
            if (DEBUG) {
                Log.d(TAG, "intercept: x:" + event.getX() + ",y:" + event.getY() + ", " + event);
            }
            final int action = MotionEventCompat.getActionMasked(event);
            if (action == MotionEvent.ACTION_DOWN) {
                mActivePointerId = event.getPointerId(0);
                mInitialTouchX = event.getX();
                mInitialTouchY = event.getY();
                obtainVelocityTracker();
                if (mSelected == null) {
                    final RecoverAnimation animation = findAnimation(event);
                    if (animation != null) {
                        mInitialTouchX -= animation.mX;
                        mInitialTouchY -= animation.mY;
                        endRecoverAnimation(animation.mViewHolder, true);
                        if (mPendingCleanup.remove(animation.mViewHolder.itemView)) {
                            mCallback.clearView(mRecyclerView, animation.mViewHolder);
                        }
                        select(animation.mViewHolder, animation.mActionState);
                        updateDxDy(event, mSelectedFlags, 0);
                    }
                }
            } else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
                mActivePointerId = ACTIVE_POINTER_ID_NONE;
                select(null, ACTION_STATE_IDLE);
            } else if (mActivePointerId != ACTIVE_POINTER_ID_NONE) {
                // in a non scroll orientation, if distance change is above threshold, we
                // can select the item
                final int index = event.findPointerIndex(mActivePointerId);
                if (DEBUG) {
                    Log.d(TAG, "pointer index " + index);
                }
                if (index >= 0) {
                    checkSelectForSwipe(action, event, index);
                }
            }
            if (mVelocityTracker != null) {
                mVelocityTracker.addMovement(event);
            }
            return mSelected != null;
        }

        @Override
        public void onTouchEvent(RecyclerView recyclerView, MotionEvent event) {
            mGestureDetector.onTouchEvent(event);
            if (DEBUG) {
                Log.d(TAG,
                        "on touch: x:" + mInitialTouchX + ",y:" + mInitialTouchY + ", :" + event);
            }
            if (mVelocityTracker != null) {
                mVelocityTracker.addMovement(event);
            }
            if (mActivePointerId == ACTIVE_POINTER_ID_NONE) {
                return;
            }
            final int action = MotionEventCompat.getActionMasked(event);
            final int activePointerIndex = event.findPointerIndex(mActivePointerId);
            if (activePointerIndex >= 0) {
                checkSelectForSwipe(action, event, activePointerIndex);
            }
            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 = MotionEventCompat.getActionIndex(event);
                    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);
        }
    };
首先,我们先看onInterceptTouchEvent的ACTION_DOWN,其中findAnimation方法(源码有点多,就不贴出来了)会根据手指按下的位置,找到选中的子控件,然后通过select方法赋值给mSelected,最后return mSelected != null;而ACTION_UP,则会通过select方法将mSelected置空,并且判断是否需要执行侧滑动画,并最终根据判断是否要调用Callback的onSwiped方法,所以ItemTouchHelper的OnItemTouchListener的onInterceptTouchEvent方法只是找到mSelected和释放mSelected
之前了解到onInterceptTouchEvent的onTouchEvent方法中是不会有ACTION_DOWN的,我们先来看ACTION_MOVE,首先调用了updateDxDy方法
代码语言:javascript
复制
   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);
        }
    }
就是根据标志位得出相应的偏移,接下来调用moveIfNecessary方法
代码语言:javascript
复制
    /**
     * Checks if we should swap w/ another view holder.
     */
    void moveIfNecessary(ViewHolder viewHolder) {
        if (mRecyclerView.isLayoutRequested()) {
            return;
        }
        if (mActionState != ACTION_STATE_DRAG) {
            return;
        }

        final float threshold = mCallback.getMoveThreshold(viewHolder);
        final int x = (int) (mSelectedStartX + mDx);
        final int y = (int) (mSelectedStartY + mDy);
        if (Math.abs(y - viewHolder.itemView.getTop()) < viewHolder.itemView.getHeight() * threshold
                && Math.abs(x - viewHolder.itemView.getLeft())
                < viewHolder.itemView.getWidth() * threshold) {
            return;
        }
        List<ViewHolder> swapTargets = findSwapTargets(viewHolder);
        if (swapTargets.size() == 0) {
            return;
        }
        // may swap.
        ViewHolder target = mCallback.chooseDropTarget(viewHolder, swapTargets, x, y);
        if (target == null) {
            mSwapTargets.clear();
            mDistances.clear();
            return;
        }
        final int toPosition = target.getAdapterPosition();
        final int fromPosition = viewHolder.getAdapterPosition();
        if (mCallback.onMove(mRecyclerView, viewHolder, target)) {
            // keep target visible
            mCallback.onMoved(mRecyclerView, viewHolder, fromPosition,
                    target, toPosition, x, y);
        }
    }
发现最后调用了Callback的onMove方法,这也是我们需要改写的拖拽方法,而ACTION_UP和onInterceptTouchEvent中的差不多
最后总结一下,ItemTouchHelper是通过OnItemTouchListener获取到选中的ViewHolder,并通过它的内部类Callback,调用ItemTouchUIUtilImpl中的方法进行绘制工作
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ItemTouchHelper的基本使用上次已经介绍了,今天来分析下ItemTouchHelper的源码,我们从attachToRecyclerView方法入手
  • 可以看到就是将recyclerView赋值给mRecyclerView ,并调用了setupCallbacks方法,来到setupCallbacks方法:
  • 其中初始化了判断移动的阀值mSlop ,然后调用mRecyclerView.addItemDecoration(this),我们看ItemTouchHelper继承ItemDecoration干了什么,发现ItemTouchHelper改写了onDraw和onDrawOver方法
  • 两个方法都差不多,先判断mSelected 是不是null,获取dx,dy,即横向的偏移量和纵向的偏移量;接下来调用mCallback的方法,mCallback就是我们使用的ItemTouchHelper.Callback,下面是Callback的onDraw方法
  • 我们关注onChildDraw方法,我们追踪代码发现,最后调用的是ItemTouchUIUtilImpl中的onDraw和onDrawOver方法,ItemTouchUIUtilImpl是ItemTouchUIUtil的实现类。mSelected 其实就是我们选中的ViewHodler,在接下来我们的分析中会知道,这边先提一下。下面贴一下ItemTouchUIUtilImpl的代码
  • 到了这里,我们发现,都是canvas的矩阵变换效果(api28中是设置View的属性),也就是我们拖拽和侧滑,最终的动画效果就是利用的canvas,那么具体我们要执行到哪个ViewHodler上,是在哪里判断的呢?我们继续来到setupCallbacks方法
  • 之前分析了mRecyclerView.addItemDecoration方法,知道了RecyclerView每次onDraw的时候,都会调用ItemTouchHelper.Callback的onDraw方法,我们再分析mRecyclerView.addOnItemTouchListener做了什么?来到RecyclerView的addOnItemTouchListener方法
  • 添加到了mOnItemTouchListeners集合中,那么mOnItemTouchListeners用来做什么呢?我们发现在RecyclerView的dispatchOnItemTouchIntercept方法中,用到了这个mOnItemTouchListeners集合
  • 而dispatchOnItemTouchIntercept方法是在RecyclerView的onInterceptTouchEvent事件中调用的
  • 也就是说,事件分发时,在onInterceptTouchEvent方法中又传递给了ItemTouchHelper的OnItemTouchListener的onInterceptTouchEvent方法,并且如果该方法返回了true,则消费事件,并且又把这个OnItemTouchListener 赋值给了mActiveOnItemTouchListener,我们再看跟踪这个mActiveOnItemTouchListener,最终发现了调用的地方
  • 如果事件分发到onTouchEvent(onInterceptTouchEvent为true或者子控件没有消费事件),那么dispatchOnItemTouch方法会被调用,在不是ACTION_DOWN事件的情况下,如果mActiveOnItemTouchListener不为null,mActiveOnItemTouchListener的onTouchEvent方法会被调用,并且返回true,如果mActiveOnItemTouchListener为null,则又会调用OnItemTouchListener的onInterceptTouchEvent方法
    • 值得注意的是onInterceptTouchEvent方法中,OnItemTouchListener是能接受到ACTION_DOWN事件的,但是onTouchEvent事件中,OnItemTouchListener不能接受到ACTION_DOWN事件
    • 上述事件分发的方法,调用有点乱,没什么头绪,那么我们先来看OnItemTouchListener这个对象,该对象在ItemTouchHelper中
    • 首先,我们先看onInterceptTouchEvent的ACTION_DOWN,其中findAnimation方法(源码有点多,就不贴出来了)会根据手指按下的位置,找到选中的子控件,然后通过select方法赋值给mSelected,最后return mSelected != null;而ACTION_UP,则会通过select方法将mSelected置空,并且判断是否需要执行侧滑动画,并最终根据判断是否要调用Callback的onSwiped方法,所以ItemTouchHelper的OnItemTouchListener的onInterceptTouchEvent方法只是找到mSelected和释放mSelected
    • 之前了解到onInterceptTouchEvent的onTouchEvent方法中是不会有ACTION_DOWN的,我们先来看ACTION_MOVE,首先调用了updateDxDy方法
    • 就是根据标志位得出相应的偏移,接下来调用moveIfNecessary方法
    • 发现最后调用了Callback的onMove方法,这也是我们需要改写的拖拽方法,而ACTION_UP和onInterceptTouchEvent中的差不多
    • 最后总结一下,ItemTouchHelper是通过OnItemTouchListener获取到选中的ViewHolder,并通过它的内部类Callback,调用ItemTouchUIUtilImpl中的方法进行绘制工作
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档