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阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容