AndroidUI初探④RecyclerView之ItemTouchHelper

0x000 前言

現在幾乎養成了一種固定的寫作模式 , 在開始正文之前 ,總想嘮嗑幾句 , 或技術簡介亦或是近來的所思所想 , 都希望有個記錄 , 與人分享交流 。這是一個開放的時代 , 我們很幸運的生活在這個時代 , 這么一個開放的時代 , 互聯網將世界各地的知識信息傳輸到我們面前 , 幾乎零距離 。然而 , 信息爆炸也成其了我們的不幸 , 每天被各種信息淹沒 , 我們如一葉扁舟 , 在信息的大海里 , 如履薄冰 。

0x001 ItemTouchHelper

AndroidUI初探③RecyclerView之ItemDecoration中 , 介紹了RecyclerView Item之間怎樣處理Style , ItemDecoration給了我們更多的想象 , 我們可以通過getItemOffsets來設置偏移量 , 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關聯之后 , CallBack可以接收到用戶操作RecyclerView的事件 , 這樣我們就可以做一些交互處理 。

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

0x002 ItemTouchHelper的使用

一 , 創建ItemHelper對象

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

二 , 實現CallBack

class ItemTouchHelperCallback extends ItemTouchHelper.Callback {

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

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

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

    }
}

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

0x003 ItemTouchHelper源碼簡析

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

首先 , 從ItemTouchHelper關聯RecyclerView方法看起 , attachToRecyclerView(@Nullable RecyclerView recyclerView) 這樣方法就是我們ItemTouchHelper關聯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中設置的了RecyclerView的addOnItemTouchListener并且@OverrideonInterceptTouchEvent(RecyclerView recyclerView, MotionEvent event) and onTouchEvent(RecyclerView recyclerView, MotionEvent event) and onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) .

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

事件傳遞代碼塊

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狀態
     * @param viewHolder
     * @param actionState Item狀態
     */
    @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復位回調
     * @param recyclerView
     * @param viewHolder
     */
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        viewHolder.itemView.setBackgroundColor(Color.WHITE);
        // Item會復用 , 所有操作ItemView的狀態之后一定要復原
        viewHolder.itemView.setAlpha(1);
        viewHolder.itemView.setScaleX(1);
        viewHolder.itemView.setScaleY(1);
    }

    /**
     * 操作Item的時候會不斷調用繪制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

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

源碼 : AdvancedUI

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,908評論 2 372

推薦閱讀更多精彩內容