天天看點

Android recyclerview源碼分析(二)

這篇文章分析這3個類

Android recyclerview源碼分析(二)

這3個類是和清單item的drag和swipe手勢有關的類,通過這3個類可實作類似dragsortlistview的功能。

先看下用法

ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
mItemTouchHelper = new ItemTouchHelper(callback);
mItemTouchHelper.attachToRecyclerView(recyclerView);      

附上高手寫的demo連結

https://github.com/iPaulPro/Android-ItemTouchHelper-Demo

其中SimpleItemTouchHelperCallback是繼承ItemTouchHelper.Callback

那我們從attachToRecyclerView這個方法開始分析

public void attachToRecyclerView(RecyclerView recyclerView) {
    if (mRecyclerView == recyclerView) {
        return; // nothing to do
    }
    if (mRecyclerView != null) {
        destroyCallbacks();
    }
    mRecyclerView = recyclerView;
    if (mRecyclerView != null) {
        setupCallbacks();
    }
}      

最後是調用setupCallbacks進行一些初始化工作

private void setupCallbacks() {
    ViewConfiguration vc = ViewConfiguration.get(mRecyclerView.getContext());
    mSlop = vc.getScaledTouchSlop();
    mRecyclerView.addItemDecoration(this);//增加ItemDecoration監聽
    mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);//item swipe手勢監聽
    mRecyclerView.addOnChildAttachStateChangeListener(this);//子view attach和dettach的監聽
    initGestureDetector();//初始化drag手勢監聽
}      

一、初始化這些監聽和ItemTouchHelper内部的一個類息息相關,那就是ItemTouchHelper.Callback,這個Callback

1.初始化sUICallback 

static {
    if (Build.VERSION.SDK_INT >= 21) {
        sUICallback = new ItemTouchUIUtilImpl.Lollipop();
    } else if (Build.VERSION.SDK_INT >= 11) {
        sUICallback = new ItemTouchUIUtilImpl.Honeycomb();
    } else {
        sUICallback = new ItemTouchUIUtilImpl.Gingerbread();
    }
}      

ItemTouchUIUtilImpl主要有一下這些方法

1.1onDraw 設定 view 的x、y軸的偏移量

1.2onSelected  不同的系統對應不同的實作

1.3clearView 則是清除 onDraw 、onSelected  做的事情

2.設定哪些類型的手勢是enabled 

例如:

isItemViewSwipeEnabled 是否可以swipe操作

isLongPressDragEnabled 是否可以drag操作

可以繼承ItemTouchHelper.Callback複寫這兩個方法控制這兩個手勢是否enable

3.控制drag和swipe手勢的方向,例如上下的是drag行為,左右的是swipe行為

getMovementFlags 子類重寫,控制每個手勢的方向

makeMovementFlags 在一個int值上儲存兩個手勢相關的值,類似android view繪制中makemesure方法

4.當子view被drag時,有兩個方法相關

4.1會回調onMove方法,不過是個空實作

4.2當onMove傳回true時,會回調onMoved,這個方法能確定被drag的view不會離開recyclerview的邊界

5.控制哪些子view可以拖動

5.1canDropOver 空實作

5.2chooseDropTarget 這個方法主要是判斷被drag的view應不應該替換被覆寫的那個view,他們倆是否該交換位置

6.當子view處在swipe手勢之後

6.1onSwiped  是個空實作,使用者重寫可以對adapter的資料進行操作

二、mOnItemTouchListener

這個監聽是用來分發drag和swipe事件的

2.1mOnItemTouchListener中  mGestureDetector.onTouchEvent(event); 這句代碼是将事件傳遞給ItemTouchHelperGestureListener.onLongPress()方法

在初始化的時候這個方法就是drag的監聽

private void initGestureDetector() {
    if (mGestureDetector != null) {
        return;
    }
    mGestureDetector = new GestureDetectorCompat(mRecyclerView.getContext(),
            new ItemTouchHelperGestureListener());
}      

2.2

if (index >= 0) {
    checkSelectForSwipe(action, event, index);
}      

這段代碼就是分發swipe事件的

三、真正執行drag和swipe手勢的方法

其實無論drag是swipe,最後都要經過select方法,隻不過傳的參數不同

1.drag

select(viewHolder, ACTION_STATE_DRAG);      

2.swipe

select(vh, ACTION_STATE_SWIPE);      

那接下來分析下select方法裡做了啥

關鍵代碼這一句

mRecyclerView.invalidate();      

調用這句代碼後會通知RecyclerView 的OnDraw方法

@Override
public void onDraw(Canvas c) {
    super.onDraw(c);

    final int count = mItemDecorations.size();
    for (int i = 0; i < count; i++) {
        mItemDecorations.get(i).onDraw(c, this, mState);
    }
}      

最後還是會CallBack裡的onDraw方法

最後總結一下select方法的調用流程,

ItemTouchHelperGestureListener.onLongPress-->select-->Callback.onDraw-->Callback.onChildDraw-->ItemTouchUIUtilImpl.onDraw

select方法其實隻是控制了子view的一個偏移位置 ,Callback裡的其他方法也有各自調用的時機。看Callback方法說明對應的去看就能理順ItemTouchHelper裡面所做的事情

繼續閱讀