【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )
Android 事件分發(fā) 系列文章目錄
【Android 事件分發(fā)】事件分發(fā)源碼分析 ( 驅(qū)動(dòng)層通過(guò)中斷傳遞事件 | WindowManagerService 向 View 層傳遞事件 )
【Android 事件分發(fā)】事件分發(fā)源碼分析 ( Activity 中各層級(jí)的事件傳遞 | Activity -> PhoneWindow -> DecorView -> ViewGroup )
【Android 事件分發(fā)】事件分發(fā)源碼分析 ( ViewGroup 事件傳遞機(jī)制 一 )
【Android 事件分發(fā)】事件分發(fā)源碼分析 ( ViewGroup 事件傳遞機(jī)制 二 )
【Android 事件分發(fā)】事件分發(fā)源碼分析 ( ViewGroup 事件傳遞機(jī)制 三 )
【Android 事件分發(fā)】事件分發(fā)源碼分析 ( ViewGroup 事件傳遞機(jī)制 四 | View 事件傳遞機(jī)制 )
【Android 事件分發(fā)】事件分發(fā)源碼分析 ( ViewGroup 事件傳遞機(jī)制 五 )
【Android 事件分發(fā)】事件分發(fā)源碼分析 ( ViewGroup 事件傳遞機(jī)制 六 )
【Android 事件分發(fā)】事件分發(fā)源碼分析 ( ViewGroup 事件傳遞機(jī)制 七 )
【Android 事件分發(fā)】ItemTouchHelper 簡(jiǎn)介 ( 拖動(dòng)/滑動(dòng)事件 | ItemTouchHelper.Callback 回調(diào) )
【Android 事件分發(fā)】ItemTouchHelper 實(shí)現(xiàn)側(cè)滑刪除 ( 設(shè)置滑動(dòng)方向 | 啟用滑動(dòng)操作 | 滑動(dòng)距離判定 | 滑動(dòng)速度判定 | 設(shè)置動(dòng)畫(huà)時(shí)間 | 設(shè)置側(cè)滑觸發(fā)操作 )
【Android 事件分發(fā)】ItemTouchHelper 實(shí)現(xiàn)拖動(dòng)排序 ( 設(shè)置滑動(dòng)方向 | 啟啟用長(zhǎng)按拖動(dòng)功能 | 拖動(dòng)距離判定 | 設(shè)置拖動(dòng)觸發(fā)操作 )
【Android 事件分發(fā)】ItemTouchHelper 事件分發(fā)源碼分析 ( 綁定 RecyclerView )
【Android 事件分發(fā)】ItemTouchHelper 源碼分析 ( OnItemTouchListener 事件監(jiān)聽(tīng)器源碼分析 )
【Android 事件分發(fā)】ItemTouchHelper 源碼分析 ( OnItemTouchListener 事件監(jiān)聽(tīng)器源碼分析 二 )
文章目錄
- Android 事件分發(fā) 系列文章目錄
- 一、onTouchEvent 事件消費(fèi)源碼分析
- 1、onTouchEvent 方法
- 2、moveIfNecessary 方法
- 二、ItemTouchHelper 涉及到的本博客相關(guān)源碼
- 三、博客資源
一、onTouchEvent 事件消費(fèi)源碼分析
在上一篇博客 【Android 事件分發(fā)】ItemTouchHelper 源碼分析 ( OnItemTouchListener 事件監(jiān)聽(tīng)器源碼分析 ) 主要分析了 給 RecyclerView 設(shè)置的 OnItemTouchListener 監(jiān)聽(tīng)器的 onInterceptTouchEvent 觸摸事件攔截方法 , 本篇博客中主要分析另外一個(gè) 觸摸事件消費(fèi)方法 onTouchEvent ;
在 onTouchEvent 事件消費(fèi) 中 , 只處理 MotionEvent.ACTION_MOVE 事件 , 不處理其它事件 ;
1、onTouchEvent 方法
首先要獲取操作的條目組件 ,
ViewHolder viewHolder = mSelected;其中 mSelected 是在第一次按下時(shí)進(jìn)行的賦值 , 有了 mSelected 值后 , 開(kāi)始處理滑動(dòng)事件 ;
如果沒(méi)有獲取到 mSelected , 則直接返回 ;
if (viewHolder == null) {return;}如果當(dāng)前處于拖動(dòng)事件 MotionEvent.ACTION_MOVE , 則進(jìn)行拖動(dòng)事件處理 ;
拖動(dòng)事件的核心是 moveIfNecessary 方法 , 該方法是處理滑動(dòng)事件的核心方法
switch (action) {case MotionEvent.ACTION_MOVE: {// 檢查該操作是否在拖動(dòng) // Find the index of the active pointer and fetch its positionif (activePointerIndex >= 0) {// 記錄修改偏移值 updateDxDy(event, mSelectedFlags, activePointerIndex);// 處理拖動(dòng)事件moveIfNecessary(viewHolder);mRecyclerView.removeCallbacks(mScrollRunnable);mScrollRunnable.run();mRecyclerView.invalidate();}break;}2、moveIfNecessary 方法
moveIfNecessary 方法中主要進(jìn)行拖動(dòng)事件判定 , 一般是拖動(dòng)條目組件進(jìn)行重新排序 ;
先獲取開(kāi)發(fā)者自定義的 Callback 中的 public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder)方法返回值 , 如果開(kāi)發(fā)者沒(méi)有設(shè)置 , 就使用默認(rèn)值 ;
該值的作用是 設(shè)置 拖動(dòng)幅度 , 組件在寬度 / 高度 上移動(dòng)超過(guò)該比例 , 就認(rèn)為拖動(dòng)觸發(fā), 執(zhí)行拖動(dòng)相關(guān)操作 ;
// 該方法就是 開(kāi)發(fā)者 自定義 Callback 中的 // public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) // 方法的作用是設(shè)置 拖動(dòng)幅度// 組件在寬度 / 高度 上移動(dòng)超過(guò)該比例 , 就認(rèn)為拖動(dòng)觸發(fā), 執(zhí)行拖動(dòng)相關(guān)操作// 拖動(dòng)多少系數(shù) , 才算完成 拖動(dòng)操作 final float threshold = mCallback.getMoveThreshold(viewHolder);final int x = (int) (mSelectedStartX + mDx);final int y = (int) (mSelectedStartY + mDy);獲取到拖動(dòng)系數(shù)后 , 使用了 threshold 系數(shù) 乘以 水平 / 垂直 方向上的條目組件 寬度 / 高度 ;
- 如果拖動(dòng)比例超過(guò)在 水平 / 垂直 方向上的條目組件 寬度 / 高度 乘以 threshold 的值 , 則拖動(dòng)判定成功 , 執(zhí)行響應(yīng)的方法 ;
- 如果拖動(dòng)比例沒(méi)有超過(guò)該值 , 說(shuō)明沒(méi)有觸發(fā)拖動(dòng)操作 , 直接返回 ;
繼續(xù)向后執(zhí)行 , 說(shuō)明拖動(dòng)動(dòng)作判定成功 , 執(zhí)行拖動(dòng)響應(yīng)方法 , 即開(kāi)發(fā)者自定義的 Callback 中的 onMove 方法 ;
public boolean onMove(@NonNull RecyclerView recyclerView,@NonNull RecyclerView.ViewHolder viewHolder,@NonNull RecyclerView.ViewHolder target)先獲取拖動(dòng)的起始位置和目標(biāo)位置 , 然后傳入 onMove 方法 , 觸發(fā)回調(diào) ;
// 獲取被拖動(dòng)的目標(biāo)位置final int toPosition = target.getAdapterPosition();// 獲取被拖動(dòng)的起始位置 final int fromPosition = viewHolder.getAdapterPosition();// 拖動(dòng)判定成功 , 調(diào)用開(kāi)發(fā)者實(shí)現(xiàn)的 Callback 的 // public boolean onMove(@NonNull RecyclerView recyclerView,// @NonNull RecyclerView.ViewHolder viewHolder,// @NonNull RecyclerView.ViewHolder target) 方法 // 一般是拖動(dòng)交換數(shù)據(jù) if (mCallback.onMove(mRecyclerView, viewHolder, target)) {// keep target visiblemCallback.onMoved(mRecyclerView, viewHolder, fromPosition,target, toPosition, x, y);}二、ItemTouchHelper 涉及到的本博客相關(guān)源碼
public class ItemTouchHelper extends RecyclerView.ItemDecorationimplements RecyclerView.OnChildAttachStateChangeListener {/*** Currently selected view holder*/@SuppressWarnings("WeakerAccess") /* synthetic access */ViewHolder mSelected = null;/*** The diff between the last event and initial touch.* 最后的觸摸事件和初始觸摸事件之間的坐標(biāo)差異 , 偏移值 .*/float mDx;float mDy;private final OnItemTouchListener mOnItemTouchListener = new OnItemTouchListener() {// 攔截觸摸事件 , 處理攔截機(jī)制 @Overridepublic boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView,@NonNull MotionEvent event) {mGestureDetector.onTouchEvent(event);if (DEBUG) {Log.d(TAG, "intercept: x:" + event.getX() + ",y:" + event.getY() + ", " + event);}final int action = event.getActionMasked();// 注意此處攔截的動(dòng)作 , 只攔截 DOWN / UP / CANCEL 三種動(dòng)作 , MOVE 動(dòng)作不攔截 // 取消操作很少遇到 // 因此 , 攔截機(jī)制中 , 只負(fù)責(zé)攔截手指按下 和 抬起 操作 // 在 ItemTouchHelper 的業(yè)務(wù)邏輯中 , 不需要處理移動(dòng)事件 if (action == MotionEvent.ACTION_DOWN) {// 按下操作 , 得到初始 XY 坐標(biāo)位置 mActivePointerId = event.getPointerId(0);mInitialTouchX = event.getX();mInitialTouchY = event.getY();// 滑動(dòng)速度檢測(cè) obtainVelocityTracker();// mSelected 是當(dāng)前正在點(diǎn)擊的條目的 ViewHolder if (mSelected == null) {// 恢復(fù)動(dòng)畫(huà) , 查找手指按下的 View 子組件 , 該子組件時(shí) RecyclerView 中的一個(gè)條目 // 用戶按下 RecyclerView 中的某個(gè)條目 // findAnimation 方法用于找到按下的條目 View , 并設(shè)置給 RecoverAnimation 恢復(fù)動(dòng)畫(huà) 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);}// 為動(dòng)畫(huà)選擇 item 項(xiàng) select(animation.mViewHolder, animation.mActionState);// 計(jì)算當(dāng)前移動(dòng)的位置 , 初始位置 與 最后一次事件的位置 偏移值 . updateDxDy(event, mSelectedFlags, 0);}}} else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {mActivePointerId = ACTIVE_POINTER_ID_NONE;// 抬起 / 取消 時(shí) , 選擇項(xiàng) 置空 . select(null, ACTION_STATE_IDLE);} else if (mActivePointerId != ACTIVE_POINTER_ID_NONE) {// 該分支表示滑動(dòng)操作完成的分支 // ACTIVE_POINTER_ID_NONE 表示是否完成了滑動(dòng) // 如果滑動(dòng)完成 , 觸發(fā)了側(cè)滑事件 , 才會(huì)進(jìn)入該分支 // 如果滑動(dòng)沒(méi)有完成 , 滑到半路 , 松開(kāi)手 , 條目組件縮回去了 , 則不會(huì)進(jìn)入該分支 // in a non scroll orientation, if distance change is above threshold, we// can select the item// 滑動(dòng)完成后 , 記錄當(dāng)前的觸摸指針?biāo)饕?/span>final int index = event.findPointerIndex(mActivePointerId);if (DEBUG) {Log.d(TAG, "pointer index " + index);}if (index >= 0) {// 檢查是否完成了滑動(dòng)操作 checkSelectForSwipe(action, event, index);}}if (mVelocityTracker != null) {mVelocityTracker.addMovement(event);}return mSelected != null;}// 處理最終的事件消費(fèi) // 只處理手指滑動(dòng)操作 MotionEvent.ACTION_MOVE @Overridepublic void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull 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 = event.getActionMasked();final int activePointerIndex = event.findPointerIndex(mActivePointerId);if (activePointerIndex >= 0) {checkSelectForSwipe(action, event, activePointerIndex);}// 按下第一次后 , mSelected 便進(jìn)行賦值// 有了 mSelected 值后 , 開(kāi)始在滑動(dòng)中處理ViewHolder viewHolder = mSelected;if (viewHolder == null) {return;}switch (action) {case MotionEvent.ACTION_MOVE: {// 檢查該操作是否在拖動(dòng) // Find the index of the active pointer and fetch its positionif (activePointerIndex >= 0) {// 記錄修改偏移值 updateDxDy(event, mSelectedFlags, activePointerIndex);// 處理拖動(dòng)事件moveIfNecessary(viewHolder);mRecyclerView.removeCallbacks(mScrollRunnable);mScrollRunnable.run();mRecyclerView.invalidate();}break;}case MotionEvent.ACTION_CANCEL:// 取消操作 , 沒(méi)有實(shí)質(zhì)的內(nèi)容 if (mVelocityTracker != null) {mVelocityTracker.clear();}// fall throughcase 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;}}}@Overridepublic void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {if (!disallowIntercept) {return;}select(null, ACTION_STATE_IDLE);}};// 該方法作用 : // 用戶按下 RecyclerView 中的某個(gè)條目 // 該方法用于找到按下的條目 View , 并設(shè)置給 RecoverAnimation 恢復(fù)動(dòng)畫(huà) @SuppressWarnings("WeakerAccess") /* synthetic access */RecoverAnimation findAnimation(MotionEvent event) {if (mRecoverAnimations.isEmpty()) {return null;}// 找到手指按下所在位置的條目的 View 組件// 查找手指按下的 View 子組件 , 該子組件時(shí) RecyclerView 中的一個(gè)條目 View target = findChildView(event);// 遍歷恢復(fù)動(dòng)畫(huà) // 動(dòng)畫(huà)中有 mViewHolder 成員 , mViewHolder 中有 itemView 成員 // 設(shè)置 anim.mViewHolder.itemView 為手指按下的子組件 // 即設(shè)置該動(dòng)畫(huà)作用于 RecyclerView 的哪個(gè)條目上 ; 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// 根據(jù)按下的 X, Y 坐標(biāo) , 查找對(duì)應(yīng)的條目 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 為空 , 則開(kāi)始遍歷進(jìn)行檢測(cè) for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) {final RecoverAnimation anim = mRecoverAnimations.get(i);final View view = anim.mViewHolder.itemView;// 根據(jù)當(dāng)前按下的坐標(biāo) , 找到列表?xiàng)l目對(duì)應(yīng)的 View 組件 if (hitTest(view, x, y, anim.mX, anim.mY)) {return view;}}return mRecyclerView.findChildViewUnder(x, y);}// 判斷刪上下左右邊距 是否在對(duì)應(yīng)子組件范圍內(nèi) private static boolean hitTest(View child, float x, float y, float left, float top) {return x >= left&& x <= left + child.getWidth()&& y >= top&& y <= top + child.getHeight();}/*** Starts dragging or swiping the given View. Call with null if you want to clear it.* 開(kāi)始拖動(dòng)/滑動(dòng)給定的 View 組件. 如果想要清除傳入 null. * 為動(dòng)畫(huà)選擇 item 項(xiàng) * 該方法中進(jìn)行一系列的計(jì)算 ** @param selected The ViewHolder to drag or swipe. Can be null if you want to cancel the* current action, but may not be null if actionState is ACTION_STATE_DRAG.* @param actionState The type of action*/@SuppressWarnings("WeakerAccess") /* synthetic access */void select(@Nullable ViewHolder selected, int actionState) {if (selected == mSelected && actionState == mActionState) {return;}mDragScrollStartTimeInMs = Long.MIN_VALUE;final int prevActionState = mActionState;// prevent duplicate animationsendRecoverAnimation(selected, true);mActionState = actionState;if (actionState == ACTION_STATE_DRAG) {if (selected == null) {throw new IllegalArgumentException("Must pass a ViewHolder when dragging");}// we remove after animation is complete. this means we only elevate the last drag// child but that should perform good enough as it is very hard to start dragging a// new child before the previous one settles.mOverdrawChild = selected.itemView;addChildDrawingOrderCallback();}int actionStateMask = (1 << (DIRECTION_FLAG_COUNT + DIRECTION_FLAG_COUNT * actionState))- 1;boolean preventLayout = false;if (mSelected != null) {final ViewHolder prevSelected = mSelected;if (prevSelected.itemView.getParent() != null) {final int swipeDir = prevActionState == ACTION_STATE_DRAG ? 0: swipeIfNecessary(prevSelected);releaseVelocityTracker();// find where we should animate tofinal float targetTranslateX, targetTranslateY;int animationType;switch (swipeDir) {case LEFT:case RIGHT:case START:case END:targetTranslateY = 0;targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth();break;case UP:case DOWN:targetTranslateX = 0;targetTranslateY = Math.signum(mDy) * mRecyclerView.getHeight();break;default:targetTranslateX = 0;targetTranslateY = 0;}if (prevActionState == ACTION_STATE_DRAG) {animationType = ANIMATION_TYPE_DRAG;} else if (swipeDir > 0) {animationType = ANIMATION_TYPE_SWIPE_SUCCESS;} else {animationType = ANIMATION_TYPE_SWIPE_CANCEL;}getSelectedDxDy(mTmpPosition);final float currentTranslateX = mTmpPosition[0];final float currentTranslateY = mTmpPosition[1];final RecoverAnimation rv = new RecoverAnimation(prevSelected, animationType,prevActionState, currentTranslateX, currentTranslateY,targetTranslateX, targetTranslateY) {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);if (this.mOverridden) {return;}if (swipeDir <= 0) {// this is a drag or failed swipe. recover immediatelymCallback.clearView(mRecyclerView, prevSelected);// full cleanup will happen on onDrawOver} else {// wait until remove animation is complete.mPendingCleanup.add(prevSelected.itemView);mIsPendingCleanup = true;if (swipeDir > 0) {// Animation might be ended by other animators during a layout.// We defer callback to avoid editing adapter during a layout.postDispatchSwipe(this, swipeDir);}}// removed from the list after it is drawn for the last timeif (mOverdrawChild == prevSelected.itemView) {removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);}}};final long duration = mCallback.getAnimationDuration(mRecyclerView, animationType,targetTranslateX - currentTranslateX, targetTranslateY - currentTranslateY);rv.setDuration(duration);mRecoverAnimations.add(rv);rv.start();preventLayout = true;} else {removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView);mCallback.clearView(mRecyclerView, prevSelected);}mSelected = null;}if (selected != null) {mSelectedFlags =(mCallback.getAbsoluteMovementFlags(mRecyclerView, selected) & actionStateMask)>> (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();}// 計(jì)算當(dāng)前移動(dòng)的位置 @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 movedmDx = 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.* 拖動(dòng)事件判定 , 一般是拖動(dòng)交換條目組件*/@SuppressWarnings("WeakerAccess") /* synthetic access */void moveIfNecessary(ViewHolder viewHolder) {if (mRecyclerView.isLayoutRequested()) {return;}if (mActionState != ACTION_STATE_DRAG) {return;}// 該方法就是 開(kāi)發(fā)者 自定義 Callback 中的 // public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) // 方法的作用是設(shè)置 拖動(dòng)幅度// 組件在寬度 / 高度 上移動(dòng)超過(guò)該比例 , 就認(rèn)為拖動(dòng)觸發(fā), 執(zhí)行拖動(dòng)相關(guān)操作// 拖動(dòng)多少系數(shù) , 才算完成 拖動(dòng)操作 final float threshold = mCallback.getMoveThreshold(viewHolder);final int x = (int) (mSelectedStartX + mDx);final int y = (int) (mSelectedStartY + mDy);// 在該判斷中 , 使用了 threshold 系數(shù) 乘以 水平 / 垂直 方向上的條目組件寬度 ; // 如果拖動(dòng)比例超過(guò)在 水平 / 垂直 方向上的條目組件 寬度 / 高度 乘以 threshold 的值 // 則拖動(dòng)判定成功 , 執(zhí)行響應(yīng)的方法 // 如果拖動(dòng)比例沒(méi)有超過(guò)該值 , 說(shuō)明沒(méi)有觸發(fā)拖動(dòng)操作 , 直接返回 if (Math.abs(y - viewHolder.itemView.getTop()) < viewHolder.itemView.getHeight() * threshold&& Math.abs(x - viewHolder.itemView.getLeft())< viewHolder.itemView.getWidth() * threshold) {return;}// 執(zhí)行到此處說(shuō)明拖動(dòng)判定成功 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;}// 獲取被拖動(dòng)的目標(biāo)位置final int toPosition = target.getAdapterPosition();// 獲取被拖動(dòng)的起始位置 final int fromPosition = viewHolder.getAdapterPosition();// 拖動(dòng)判定成功 , 調(diào)用開(kāi)發(fā)者實(shí)現(xiàn)的 Callback 的 // public boolean onMove(@NonNull RecyclerView recyclerView,// @NonNull RecyclerView.ViewHolder viewHolder,// @NonNull RecyclerView.ViewHolder target) 方法 // 一般是拖動(dòng)交換數(shù)據(jù) if (mCallback.onMove(mRecyclerView, viewHolder, target)) {// keep target visiblemCallback.onMoved(mRecyclerView, viewHolder, fromPosition,target, toPosition, x, y);}} }
三、博客資源
博客資源 :
- GitHub 地址 : https://github.com/han1202012/001_RecyclerView
總結(jié)
以上是生活随笔為你收集整理的【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【Android 事件分发】ItemTo
- 下一篇: 【错误记录】Google Play 上架