AndroidUI初探④RecyclerView之ItemTouchHelper

0x000 前言

現(xiàn)在幾乎養(yǎng)成了一種固定的寫作模式 , 在開始正文之前 ,總想嘮嗑幾句 , 或技術(shù)簡介亦或是近來的所思所想 , 都希望有個記錄 , 與人分享交流 。這是一個開放的時代 , 我們很幸運的生活在這個時代 , 這么一個開放的時代 , 互聯(lián)網(wǎng)將世界各地的知識信息傳輸?shù)轿覀兠媲?, 幾乎零距離 。然而 , 信息爆炸也成其了我們的不幸 , 每天被各種信息淹沒 , 我們?nèi)缫蝗~扁舟 , 在信息的大海里 , 如履薄冰 。

0x001 ItemTouchHelper

AndroidUI初探③RecyclerView之ItemDecoration中 , 介紹了RecyclerView Item之間怎樣處理Style , ItemDecoration給了我們更多的想象 , 我們可以通過getItemOffsets來設(shè)置偏移量 , onDraw來繪制各種圖形 , 可以通過Drawable來打造漂亮的ItemDecoration 。

如果說ItemDecoration是裝飾Item的 , 那么ItemTouchHelper就是Item與用戶交互的利器 。那么ItemTouchHelper是個什么樣的類呢?他會為我們做些什么樣的事情 。

ItemTouchHelper簡介

源碼中的解釋是這樣的 :

This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
大致說的是:這是一個RecyclerView的工具,提供了drag & swipe 的功能,也就是說 , 這個類可以幫助我們處理RecyclerView中Item的Drag和Swipe

It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.
大致說的是:他需要RecyclerView 還有ItemTouchHelper.CallBack配合使用,可以配置交互類型 , 并可以接收用戶操作的事件 。也就是說 , 將ItemTouchHelper與RecyclerView關(guān)聯(lián)之后 , CallBack可以接收到用戶操作RecyclerView的事件 , 這樣我們就可以做一些交互處理 。

英語渣渣 , 英語好的請忽略翻譯

0x002 ItemTouchHelper的使用

一 , 創(chuàng)建ItemHelper對象

// 創(chuàng)建ItemTouchHelper的時候 , 就會要求我們傳入CallBack , 用作RecyclerView事件的回調(diào) , 并做出處理 。
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callBack);
// 關(guān)聯(lián)RecylerView
itemTouchHelper.attachToRecyclerView(rvSimpleList);

二 , 實現(xiàn)CallBack

class ItemTouchHelperCallback extends ItemTouchHelper.Callback {

    /**
     * 需要處理的方向 , 如上下移動 , 左右滑動等
     * @param recyclerView
     * @param viewHolder
     * @return
     */
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        return 0;
    }

    /**
     * 移動時會回調(diào)
     * @param recyclerView
     * @param viewHolder
     * @param target
     * @return
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        return false;
    }

    /**
     * 滑動時會回調(diào)
     * @param viewHolder
     * @param direction
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {

    }
}

只需兩步 , 我們就可以將RecyclerView的事件接收掉 。那么他是怎么接收的呢 ? 我們深入源碼看一看 。

0x003 ItemTouchHelper源碼簡析

在分析源碼的時候 , 通常源碼都會比較多 , 所以我們要找準主旨 , 我們是要分析ItemHelper是怎樣將RecyclerView的事件接收起來的,那么我們主要看RecyclerView的事件處理,在ItemTouchHelper中是怎樣體現(xiàn)的。

首先 , 從ItemTouchHelper關(guān)聯(lián)RecyclerView方法看起 , attachToRecyclerView(@Nullable RecyclerView recyclerView) 這樣方法就是我們ItemTouchHelper關(guān)聯(lián)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對象 , 接著就是將RecyclerView的TouchListener給搶過來 。進入setupCallbacks()我們可以看到 , 在ItemTouchHelper中設(shè)置的了RecyclerView的addOnItemTouchListener并且@OverrideonInterceptTouchEvent(RecyclerView recyclerView, MotionEvent event) and onTouchEvent(RecyclerView recyclerView, MotionEvent event) and onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) .

onInterceptTouchEventonTouchEvent主要是將RecyclerView的TouchListener事件傳遞給ItemTouchHelper.Callback并做了相應(yīng)的處理, 這樣我們就可以在ItemTouchHelper的CallBack只關(guān)心我們想要處理的 。

事件傳遞代碼塊

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);
    }
}

0x004 ItemTouchHelper簡單示例

public class DragItemTouchHelperCallback extends ItemTouchHelper.Callback {

    private final ViewHolderDragSwipeItemListener dragSwipeItemListener;

    public DragItemTouchHelperCallback(ViewHolderDragSwipeItemListener dragSwipeItemListener) {
        this.dragSwipeItemListener = dragSwipeItemListener;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {

        /*拖拽方向*/
        int dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;

        /*滑動方向*/
        int swipeFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;

        int flags = makeMovementFlags(dragFlag, swipeFlag);

        return flags;
    }

    /**
     * 是否開啟長按拖拽 ,必須開啟
     * @return
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }


    /**
     * 上下移動交換Item
     * @param recyclerView
     * @param viewHolder
     * @param target
     * @return
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        if (dragSwipeItemListener != null) {
            dragSwipeItemListener.onDragMoveSwap(viewHolder.getAdapterPosition(),target.getAdapterPosition());
        }
        return true;
    }

    /**
     * 左右滑動刪除
     * @param viewHolder
     * @param direction
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
         if (dragSwipeItemListener != null) {
             dragSwipeItemListener.onSwipeDelete(viewHolder.getAdapterPosition());
         }
    }

    /**
     * 選中Item狀態(tài)
     * @param viewHolder
     * @param actionState Item狀態(tài)
     */
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (actionState == ItemTouchHelper.ACTION_STATE_IDLE) {
            return;
        }

        viewHolder.itemView.setBackgroundColor(Color.GRAY);

        super.onSelectedChanged(viewHolder, actionState);
    }

    /**
     * ItemView復(fù)位回調(diào)
     * @param recyclerView
     * @param viewHolder
     */
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        viewHolder.itemView.setBackgroundColor(Color.WHITE);
        // Item會復(fù)用 , 所有操作ItemView的狀態(tài)之后一定要復(fù)原
        viewHolder.itemView.setAlpha(1);
        viewHolder.itemView.setScaleX(1);
        viewHolder.itemView.setScaleY(1);
    }

    /**
     * 操作Item的時候會不斷調(diào)用繪制Item , 我們可以在這里做很多事情。
     * @param c
     * @param recyclerView
     * @param viewHolder
     * @param dX
     * @param dY
     * @param actionState
     * @param isCurrentlyActive
     */
    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);

        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
            float value = 1 - Math.abs(dX) / viewHolder.itemView.getWidth();
            viewHolder.itemView.setAlpha(value);
            viewHolder.itemView.setScaleX(value);
            viewHolder.itemView.setScaleY(value);
        }
    }
}

0x005 效果圖

recyclerview_itemtouch_helper

0x006 The end

高手 , 都是通過不斷的練習 。雖然有些事物看起簡單 , 但深入其中 ,自會發(fā)現(xiàn) , 每一件事都不是獨立而存在 , 都是一環(huán)扣著一環(huán) , 不要被表象迷惑 , 要敢于深究其理 。

源碼 : AdvancedUI

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容