recycleview ItemTouchHelper item移动神器

最近项目中有一个需求,需要有一个列表内容管理,实时显示内容的同时,可以让item进行移动,并根据内容数量不同,进行不同布局展示,这个需求,一开始是想自己用自定义布局管理及自定义view实现的,无奈自己技术不够。后来,突发奇想,能不能使用recycleview实现,网上查了一下,发现recycleview自带布局Item管理,使用ItemToucherHelper即可轻松实现,不得不说,recycleview真的是比listview强太多了

根据item数量的不同,需要实现如下的布局

下面是实现的效果图

首先,先来讲一下ItemTouchHelper的使用,惯例,先上代码

private ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
        @Override
        public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
            if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
                final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN |
                        ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
                final int swipeFlags = 0;
                return makeMovementFlags(dragFlags, swipeFlags);
            } else {
                final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
                final int swipeFlags = 0;
                return makeMovementFlags(dragFlags, swipeFlags);
            }
        }

        @Override
        public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
            //得到当拖拽的viewHolder的Position
            int fromPosition = viewHolder.getAdapterPosition();
            //拿到当前拖拽到的item的viewHolder
            int toPosition = target.getAdapterPosition();

            itemMove(fromPosition,toPosition);

            return true;
        }

        @Override
        public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {

        }
    });
itemTouchHelper.attachToRecyclerView(binding.rvControl);
private void itemMove(int fromPosition, int toPosition){
        if (fromPosition < toPosition) {
            for (int i = fromPosition; i < toPosition; i++) {
                Collections.swap(showInstances, i, i + 1);
            }
        } else {
            for (int i = fromPosition; i > toPosition; i--) {
                Collections.swap(showInstances, i, i - 1);
            }
        }
        showAdapter.notifyItemMoved(fromPosition, toPosition);
    }

这里主要是看两个方法,getMovementFlags,这个方法主要用来控制Recycleview中Item具体可以移动的方向,而onMove方法是具体的Item移动后回调的方法,而回调中主要是先更新数据列表,然后调用recycleview中adapter的notifyItemMoved方法,用来通知item移动,而调用attachToRecyclerView,即可将itemTouchHelper和RecycleView绑定起来

接下来看如何设置RecycleView的布局,使其显示为上图的样子

首先,因为前四个item是按照列表形式布局的,因此,可以确定该recycleview的基础布局管理器为gridLayoutManager,而需要让第5个item占两行,则需要调用到layoutManager的setSpanSizeLookup方法

private void notifyShowAdapter(){
        ControlLayoutManager gridLayoutManager ;
        if (showInstances.size() < 3){
            gridLayoutManager = new ControlLayoutManager(getContext(),1,showInstances.size());
        }else if (showInstances.size() == 3){
            gridLayoutManager = new ControlLayoutManager(getContext(),2,showInstances.size());
            gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    if (position == 2)//判断条件,如大号item或者小号item
                        return 2;
                    return 1;
                }
            });
        }else if (showInstances.size() == 4){
            gridLayoutManager = new ControlLayoutManager(getContext(),2,showInstances.size());
            gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    if (position == 2)//判断条件,如大号item或者小号item
                        return 2;
                    return 1;
                }
            });
        }else {
            gridLayoutManager = new ControlLayoutManager(getContext(),2,showInstances.size());
        }
        if (lastShowSize != showInstances.size()){//有增加或减少才更新
            gridLayoutManager.setOrientation(GridLayoutManager.HORIZONTAL);
            controlFragmentDB.setControlManager(gridLayoutManager);
            showAdapter.setItemInstanceListDBS(showInstances);
            showAdapter.notifyDataSetChanged();
        }
        lastShowSize = showInstances.size();

        gridLayoutManager.setItemMove(false);
    }

代码中针对3个item和5个item分别进行了处理

另外,因为item数量不同,item的大小也是不同的,所以需要在layoutManger中进行处理

 public void addView(View child) {
        super.addView(child);
        logUtils.i("addView");
        rvHeight = getHeight();//记录高度,不然onItemsMoved获取错误
        LayoutParams layoutParams = (LayoutParams) child.getLayoutParams();
        switch (totalSize){
            case 1:
                layoutParams.width = getWidth() - UIUtils.dptoPx(20);
                layoutParams.height = getHeight() - UIUtils.dptoPx(20);
                layoutParams.leftMargin = UIUtils.dptoPx(10);
                layoutParams.rightMargin = UIUtils.dptoPx(10);
                layoutParams.topMargin = UIUtils.dptoPx(10);
                layoutParams.bottomMargin = UIUtils.dptoPx(10);
                child.setLayoutParams(layoutParams);
                break;
            case 2:
                layoutParams.width = (getWidth() - UIUtils.dptoPx(40)) /2 ;
                layoutParams.height = getHeight() - UIUtils.dptoPx(20);
                layoutParams.leftMargin = UIUtils.dptoPx(10);
                layoutParams.rightMargin = UIUtils.dptoPx(10);
                layoutParams.topMargin = UIUtils.dptoPx(10);
                layoutParams.bottomMargin = UIUtils.dptoPx(10);
                child.setLayoutParams(layoutParams);
                break;
            case 3:
                layoutParams.width = (getWidth() - UIUtils.dptoPx(40)) /2 ;
                if (getChildCount() == 3){
                    layoutParams.height = getHeight() - UIUtils.dptoPx(20);
                } else if (!isItemMove){//移动布局时,不能修改普通item高度
                    layoutParams.height = (getHeight() - UIUtils.dptoPx(40)) /2;
                }
                layoutParams.leftMargin = UIUtils.dptoPx(10);
                layoutParams.rightMargin = UIUtils.dptoPx(10);
                layoutParams.topMargin = UIUtils.dptoPx(10);
                layoutParams.bottomMargin = UIUtils.dptoPx(10);
                child.setLayoutParams(layoutParams);
                break;
            case 5:
                layoutParams.width = (getWidth() - UIUtils.dptoPx(30)) /3 ;
                if (getChildCount() == 5){
                    layoutParams.height = getHeight() - UIUtils.dptoPx(20);
                }else if (!isItemMove){//移动布局时,不能修改普通item高度
                    layoutParams.height = (getHeight() - UIUtils.dptoPx(40)) /2;
                }
                layoutParams.leftMargin = UIUtils.dptoPx(5);
                layoutParams.rightMargin = UIUtils.dptoPx(5);
                layoutParams.topMargin = UIUtils.dptoPx(10);
                layoutParams.bottomMargin = UIUtils.dptoPx(10);
                child.setLayoutParams(layoutParams);
                break;
            default:
                layoutParams.width = (getWidth() - UIUtils.dptoPx(40)) /2 ;
                layoutParams.height = (getHeight() - UIUtils.dptoPx(40)) /2;
                layoutParams.leftMargin = UIUtils.dptoPx(10);
                layoutParams.rightMargin = UIUtils.dptoPx(10);
                layoutParams.topMargin = UIUtils.dptoPx(10);
                layoutParams.bottomMargin = UIUtils.dptoPx(10);
                child.setLayoutParams(layoutParams);
                break;
        }
    }

在addView方法中,进行child的LayoutParams重新设置,此处需要注意,因为移动布局时,同样会调用该方法,而3个布局和5个布局的时候,最后一个item大小需要重新设置,所以需要做特殊处理

最后一步,3个Item和5个Item的布局,需要在移动布局的同时,更新布局大小,因此,需要在LayouManger中对onItemsMoved进行处理

public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
        super.onItemsMoved(recyclerView, from, to, itemCount);
        //3个布局和5个布局的时候需要做特殊处理
        isItemMove = true;//告知现在是移动布局
        if ((totalSize == 5 && to == 4) || (totalSize == 3 && to == 2)){
                View view = recyclerView.getChildAt(from);
                LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
                layoutParams.height = rvHeight - UIUtils.dptoPx(20);
                view.setLayoutParams(layoutParams);

                View view1 = recyclerView.getChildAt(to);
                LayoutParams layoutParams1 = (LayoutParams) view1.getLayoutParams();
                layoutParams1.height = (rvHeight - UIUtils.dptoPx(40)) /2;
                view1.setLayoutParams(layoutParams1);
            }else if ((totalSize == 5 && from == 4)|| (totalSize == 3 && from == 2)){
                View view = recyclerView.getChildAt(from);
                LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
                layoutParams.height = (rvHeight - UIUtils.dptoPx(40)) /2;
                view.setLayoutParams(layoutParams);
                int index;
                if (from == 4){
                    index = 3;
                }else {
                    index = 1;
                }
                View view1 = recyclerView.getChildAt(index);
                LayoutParams layoutParams1 = (LayoutParams) view1.getLayoutParams();
                layoutParams1.height = rvHeight - UIUtils.dptoPx(20);
                view1.setLayoutParams(layoutParams1);
            }
    }

这样基本就能实现上述需求了,这个需求真的是花了我好长的时间,之前一直都不知道recycleview还有itemTouchHelper这种黑科技,如果代码中有写的不对的地方,欢迎大家多多指教

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值