ItemTouchHelper 实现交互动画

01.拖拽需要实现功能

  • 需要实现拖拽的功能如下所示

长按item后拖动,与其他item交换位置
按住item右面的图标后拖动,与其他item交换位置
左滑item变透明并缩小,超出屏幕后,其他item补上
右滑item变透明并缩小,超出屏幕后,其他item补上

02.几个重要的方法说明

  • 几个重要的方法说明

需要自定义类实现ItemTouchHelper.Callback类,并重写其中几个方法

isLongPressDragEnabled                  是否可以长按拖拽排序
isItemViewSwipeEnabled                  Item是否可以被滑动
getMovementFlags                        当用户拖拽或者滑动Item的时候需要我们告诉系统滑动或者拖拽的方向
onMove                                  当Item被拖拽的时候被回调
onSwiped                                当View被滑动删除的时候
onSelectedChanged                       当item被拖拽或侧滑时触发

03.简单实现思路

  • 几个方法中代码思路

要想达到上面功能需求,在getMovementFlags方法中,当用户拖拽或者滑动Item的时候需要我们告诉系统滑动或者拖拽的方向,那我们知道支持拖拽和滑动删除的无非就是LinearLayoutManager和GridLayoutManager了,所以可以根据布局管理器的不同做了响应的区分。

在onMove方法中处理拖拽的回调逻辑,那么什么时候被调用?当Item被拖拽排序移动到另一个Item的位置的时候被调用。在onSwiped方法[当Item被滑动删除到不见]中处理被删除后的逻辑。为了降低代码耦合度,可以通过接口listener回调的方式交给外部处理。

  • 上下拖动时与其他item进行位置交换

ItemTouchHelper.Callback本身不具备将两个item互换位置的功能,但RecyclerView可以,我们可以在item拖动的时候把当前item与另一个item的数据位置交换,再调用RecyclerView的notifyItemMoved()方法刷新布局,同时,因为RecyclerView自带item动画,就可以完成上面的交互效果。

  • 左右滑出屏幕时其他item补上

只要在item滑出屏幕时,将对应的数据删掉,再调用RecyclerView的notifyItemRemoved()方法刷新布局即可。

04.拖拽效果上优化

  • 拖拽效果优化

在item被拖拽或侧滑时修改背景色,当动作结束后将背景色恢复回来,而ItemTouchHelper.Callback中正好有对应这两个状态的方法,分别是:onSelectedChanged()、clearView()。那么优化处理其实可以放到这两个方法中处理。

左右滑动使item透明度变浅且缩小该如何实现呢?让item执行了两种属性动画而已,在ItemTouchHelper.Callback中有一个方法可以拿到item被拖拽或滑动时的位移变化,那就是onChildDraw()方法,在该方法中设置item渐变和缩放属性动画。

出现问题,按照上面做法会出现删除后有空白item留出来,那么为什么会出现这种情况呢?并不是多出了两条空白数据,它们是正常的数据,只是看不到了,这是因为RecyclerView条目(itemView)覆用导致的,前面在onChildDraw()方法中对itemView设置了透明和缩小,而一个列表中固定只有几个itemView而已,当那两个透明缩小的itemView被再次使用时,之前设置的透明度和高度比例已经是0,所以就出现了这种情况,解决方法也很简单,只要在item被移除后,将itemView的透明度和高度比例设置回来即可

05.完整代码展示

/**
 * <pre>
 *     @author 杨充
 *     blog  : https://github.com/yangchong211
 *     time  : 2017/5/2
 *     desc  : 自定义ItemTouchHelper
 *     revise: 参考严正杰大神博客:https://blog.csdn.net/yanzhenjie1003/article/details/51935982
 * </pre>
 */
public class ItemTouchHelpCallback extends ItemTouchHelper.Callback {

    /**
     * Item操作的回调,去更新UI和数据源
     */
    private OnItemTouchCallbackListener onItemTouchCallbackListener;
    /**
     * 是否可以拖拽
     */
    private boolean isCanDrag = false;
    /**
     * 是否可以被滑动
     */
    private boolean isCanSwipe = false;
    /**
     * 按住拖动item的颜色
     */
    private int color = 0;

    public ItemTouchHelpCallback(OnItemTouchCallbackListener onItemTouchCallbackListener) {
        this.onItemTouchCallbackListener = onItemTouchCallbackListener;
    }

    /**
     * 设置是否可以被拖拽
     *
     * @param canDrag 是true,否false
     */
    public void setDragEnable(boolean canDrag) {
        isCanDrag = canDrag;
    }

    /**
     * 设置是否可以被滑动
     *
     * @param canSwipe 是true,否false
     */
    public void setSwipeEnable(boolean canSwipe) {
        isCanSwipe = canSwipe;
    }

    /**
     * 设置按住拖动item的颜色
     * @param color     颜色
     */
    public void setColor(@ColorInt int color){
        this.color = color;
    }

    /**
     * 当Item被长按的时候是否可以被拖拽
     *
     * @return                      true
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return isCanDrag;
    }

    /**
     * Item是否可以被滑动(H:左右滑动,V:上下滑动)
     * isItemViewSwipeEnabled()返回值是否可以拖拽排序,true可以,false不可以
     * @return                      true
     */
    @Override
    public boolean isItemViewSwipeEnabled() {
        return isCanSwipe;
    }

    /**
     * 当用户拖拽或者滑动Item的时候需要我们告诉系统滑动或者拖拽的方向
     * 动作标识分:dragFlags和swipeFlags
     * dragFlags:列表滚动方向的动作标识(如竖直列表就是上和下,水平列表就是左和右)
     * wipeFlags:与列表滚动方向垂直的动作标识(如竖直列表就是左和右,水平列表就是上和下)
     *
     * 思路:如果你不想上下拖动,可以将 dragFlags = 0
     *      如果你不想左右滑动,可以将 swipeFlags = 0
     *      最终的动作标识(flags)必须要用makeMovementFlags()方法生成
     */
    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView,
                                @NonNull RecyclerView.ViewHolder viewHolder) {
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            // flag如果值是0,相当于这个功能被关闭
            int dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT
                    | ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            int swipeFlag = 0;
            // create make
            return makeMovementFlags(dragFlag, swipeFlag);
        } else if (layoutManager instanceof LinearLayoutManager) {
            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
            int orientation = linearLayoutManager.getOrientation();

            int dragFlag = 0;
            int swipeFlag = 0;

            // 为了方便理解,相当于分为横着的ListView和竖着的ListView
            // 如果是横向的布局
            if (orientation == LinearLayoutManager.HORIZONTAL) {
                swipeFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
                dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            } else if (orientation == LinearLayoutManager.VERTICAL) {
                // 如果是竖向的布局,相当于ListView
                dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
                swipeFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            }
            //第一个参数是拖拽flag,第二个是滑动的flag
            return makeMovementFlags(dragFlag, swipeFlag);
        }
        return 0;
    }


    /**
     * 当Item被拖拽的时候被回调
     *
     * @param recyclerView          recyclerView
     * @param srcViewHolder         当前被拖拽的item的viewHolder
     * @param targetViewHolder      当前被拖拽的item下方的另一个item的viewHolder
     * @return                      是否被移动
     */
    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView,
                          @NonNull RecyclerView.ViewHolder srcViewHolder,
                          @NonNull RecyclerView.ViewHolder targetViewHolder) {
        if (onItemTouchCallbackListener != null) {
            int srcPosition = srcViewHolder.getAdapterPosition();
            int targetPosition = targetViewHolder.getAdapterPosition();
            return onItemTouchCallbackListener.onMove(srcPosition, targetPosition);
        }
        return false;
    }


    /**
     * 当item侧滑出去时触发(竖直列表是侧滑,水平列表是竖滑)
     *
     * @param viewHolder            viewHolder
     * @param direction             滑动的方向
     */
    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
        if (onItemTouchCallbackListener != null) {
            onItemTouchCallbackListener.onSwiped(viewHolder.getAdapterPosition());
        }
    }

    /**
     * 当item被拖拽或侧滑时触发
     *
     * @param viewHolder            viewHolder
     * @param actionState           当前item的状态
     */
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        super.onSelectedChanged(viewHolder, actionState);
        //不管是拖拽或是侧滑,背景色都要变化
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            if (color==0){
                viewHolder.itemView.setBackgroundColor(viewHolder.itemView.getContext()
                        .getResources().getColor(android.R.color.darker_gray));
            }else {
                viewHolder.itemView.setBackgroundColor(color);
            }
        }
    }


    /**
     * 当item的交互动画结束时触发
     *
     * @param recyclerView          recyclerView
     * @param viewHolder            viewHolder
     */
    @Override
    public void clearView(@NonNull RecyclerView recyclerView,
                          @NonNull RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        viewHolder.itemView.setBackgroundColor(viewHolder.itemView.getContext().getResources()
                .getColor(android.R.color.white));
        viewHolder.itemView.setAlpha(1);
        viewHolder.itemView.setScaleY(1);
    }


    @Override
    public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView,
                            @NonNull 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.setScaleY(value);
        }
    }


    public interface OnItemTouchCallbackListener {
        /**
         * 当某个Item被滑动删除的时候
         *
         * @param adapterPosition   item的position
         */
        void onSwiped(int adapterPosition);

        /**
         * 当两个Item位置互换的时候被回调
         *
         * @param srcPosition       拖拽的item的position
         * @param targetPosition    目的地的Item的position
         * @return                  开发者处理了操作应该返回true,开发者没有处理就返回false
         */
        boolean onMove(int srcPosition, int targetPosition);
    }
}

  • 如何使用,代码如下所示
ItemTouchHelpCallback callback = new ItemTouchHelpCallback((srcPosition, targetPosition) -> {
        if (imageBeans != null) {
            try {
                // 更换数据源中的数据Item的位置。更改list中开始和结尾position的位置
                Collections.swap(imageBeans, srcPosition, targetPosition);
                // 更新UI中的Item的位置,主要是给用户看到交互效果
                mAdapter.notifyItemMoved(srcPosition, targetPosition);
            } catch (Exception e){
                e.printStackTrace();
            }
            return true;
        }
        return true;
    });
callback.setDragEnable(true);
callback.setSwipeEnable(true);
callback.setColor(this.getResources().getColor(R.color.base_background_block));
//创建helper对象,callback监听recyclerView item 的各种状态
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
try{
    //关联recyclerView,一个helper对象只能对应一个recyclerView
    itemTouchHelper.attachToRecyclerView(recyclerView);
}catch (Exception e){
    e.printStackTrace();
}

在这分享一份整理了2个月的Android进阶面试解析笔记文档,包括了知识点笔记和高频面试问题解析及部分知识点视频讲解给大家!为了不影响阅读,在这以图片展示部分内容于目录截图,有需要的朋友麻烦点赞后点击下面在线链接获取免费领取方式吧!
阿里P6P7【安卓】进阶资料分享+加薪跳槽必备面试题

发布了488 篇原创文章 · 获赞 85 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/Coo123_/article/details/104336763
今日推荐