Android -- 基于RecyclerView + itemTouchHelper的应用管理功能。

本文详细介绍如何在Android项目中使用RecyclerView结合ItemTouchHelper实现拖拽排序功能,包括添加依赖、创建Bean类、设置Adapter及item布局、实现点击监听及拖拽事件。
摘要由CSDN通过智能技术生成

照例,我们感谢一下技术大牛的分享,只有他们的无私分享,才让后来者更好地学习。

RecyclerViewItemTouchHelperDemo【使用ItemTouchHelper进行拖拽排序功能】

本文基于该文章进行学习。

首先,RecyclerView在Android的support库里,所以要使用的时候需要先在build.gradle内添加依赖,如果项目中有引用appcompat的话,版本号和appcompat保持一致

dependencies {
    implementation 'com.android.support:recyclerview-v7:28.0.0'
}

然后因为我们在拖拽的时候,会有震动效果,所以在AndroidManifest.xml里添加权限

<uses-permission android:name="android.permission.VIBRATE" />

为了方便数据的存放和读取,我们需要创建一个Bean类

ChannelBean.java

public class ChannelBean {
    private String channelId;
    private String channelNamee;
    private int channelImage;

    public int getChannelImage() {
        return channelImage;
    }

    public String getChannelId() {
        return channelId;
    }

    public String getChannelNamee() {
        return channelNamee;
    }

    public void setChannelId(String channelId) {
        this.channelId = channelId;
    }

    public void setChannelImage(int channelImage) {
        this.channelImage = channelImage;
    }

    public void setChannelNamee(String channelNamee) {
        this.channelNamee = channelNamee;
    }
}

有了Bean之后,我们开始设置adapter和item布局

首先先创建item布局,因为adapter需要引用到该布局文件

channel_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/listitem_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical">
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/linear_01"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="10dp"
        android:background="@drawable/yuanjiao_10"
        android:paddingTop="10dp"
        android:paddingBottom="10dp">
        >
        <ImageView
            android:id="@+id/img_icon_1"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintWidth_default="percent"
            app:layout_constraintWidth_percent="0.3"
            app:layout_constraintDimensionRatio="h,1:1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toTopOf="@+id/text_icon_1"
            android:src="@mipmap/icon_01"/>
        <TextView
            android:id="@+id/text_icon_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="留言信息"
            app:layout_constraintTop_toBottomOf="@+id/img_icon_1"
            app:layout_constraintBottom_toBottomOf="parent"/>
    </androidx.constraintlayout.widget.ConstraintLayout>

</LinearLayout>

然后创建Adapter。在adapter中,我们需要获得上下文和项目集合(频道集合)。因为我们继承了RecyclerView.Adapter<RecyclerView.ViewHolder>,所以我们需要实现三个方法,分别为

onCreateViewHolder,onBindViewHolder和getItemCount。

首先是onCreateViewHolder,从名字可以看出是用来创建viewHolder的。所以我们先要根据item.xml确定有哪些view需要获取到,然后声明一个ItemViewHolder,然后在onCreateViewHolder内实例化。本文中有四个view可以获取,分别为LinearLayout,ConstraintLayout,ImageView和TextView。所以我们声明viewHolder继承RecyclerView.ViewHolder

//声明grid列表项ViewHolder
    private class ItemViewHolder extends RecyclerView.ViewHolder {
        public ItemViewHolder(@NonNull View itemView) {
            super(itemView);

            listitemLayout = (LinearLayout)itemView.findViewById(R.id.listitem_layout);
            linear_01 = (ConstraintLayout)itemView.findViewById(R.id.linear_01);
            img_icon_1 = (ImageView)itemView.findViewById(R.id.img_icon_1);
            text_icon_1 = (TextView)itemView.findViewById(R.id.text_icon_1);
        }
        LinearLayout listitemLayout;
        ConstraintLayout linear_01;
        ImageView img_icon_1;
        TextView text_icon_1;
    }

然后在onCreateViewHolder中,我们实例化这个viewHolder并制定布局文件

//创建ViewHolder
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(myContext).inflate(R.layout.channel_item,parent,false);
        ItemViewHolder itemViewHolder = new ItemViewHolder(view);
        return itemViewHolder;
    }

创建好viewHolder之后,我们需要给他绑定数据,从名字可以知道,调用onBindViewHolder

//将数据绑定在ViewHolder上
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int index) {
        //判定列表项还是上拉加载区域
        if(holder instanceof ItemViewHolder){
            ChannelBean channelBean = listitemList.get(index);
            final ItemViewHolder itemViewHolder = ((ItemViewHolder)holder);

            itemViewHolder.text_icon_1.setText(channelBean.getChannelNamee());
            itemViewHolder.img_icon_1.setImageResource(channelBean.getChannelImage());
        }
    }

绑定好数据之后,我们还需要他有点击监听的事件,所以我们需要添加OnItemClickListener回调

/*==================添加OnItemClickListener回调==============*/
    public interface OnItemClickListener{
        void onItemClick(View view,int position);
        void onItemLongClick(View view,int position);
    }

    private OnItemClickListener mOnItemClickListener;

    public void setOnItemClickListener(OnItemClickListener mOnItemClickListener){
        this.mOnItemClickListener = mOnItemClickListener;
    }

这里的回调设置为接口,在具体调用的时候实现。

添加好OnItemClickListener回调之后,我们需要在onBindViewHolder里绑定点击事件

 //将数据绑定在ViewHolder上
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int index) {
        //判定列表项还是上拉加载区域
        if(holder instanceof ItemViewHolder){
            ChannelBean channelBean = listitemList.get(index);
            final ItemViewHolder itemViewHolder = ((ItemViewHolder)holder);

            itemViewHolder.text_icon_1.setText(channelBean.getChannelNamee());
            itemViewHolder.img_icon_1.setImageResource(channelBean.getChannelImage());

            //如果设置了回调,则设置了点击事件
            if(mOnItemClickListener != null){
                itemViewHolder.listitemLayout.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        int position = itemViewHolder.getLayoutPosition();//增加或删除数据时,position和index就不一样了
                        mOnItemClickListener.onItemClick(itemViewHolder.listitemLayout,position);
                    }
                });
                itemViewHolder.listitemLayout.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        int position = itemViewHolder.getLayoutPosition();
                        mOnItemClickListener.onItemLongClick(itemViewHolder.listitemLayout,position);
                        return false;
                    }
                });
            }

        }
    }

然后在具体调用的时候实现void onItemClick(View view,int position); void onItemLongClick(View view,int position);这两个接口

因为考虑到item拖拽的时候,不能跨RecuclerView(未学习到),所以设置为点击item添加和删除,这时候我们就需要一个additem和一个removeitem方法了。

//添加item   用于动画表现
    public void addItem(int position,ChannelBean listitemBean){
        listitemList.add(position,listitemBean);
        notifyItemChanged(position);
    }

    //删除item   用于动画表现
    public void removeItem(int position){
        listitemList.remove(position);
        notifyItemChanged(position);
    }

至此,adapter已经完成。完整的adapter文件为

ChannelAdapter.java

public class ChannelAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    //上下文
    private Context myContext;
    //频道集合
    private ArrayList<ChannelBean> listitemList;

    //构造函数
    public ChannelAdapter(Context context, ArrayList<ChannelBean> itemlist){
        this.myContext = context;
        this.listitemList = itemlist;
    }

    //创建ViewHolder
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(myContext).inflate(R.layout.channel_list_item,parent,false);
        ItemViewHolder itemViewHolder = new ItemViewHolder(view);
        return itemViewHolder;
    }

    //将数据绑定在ViewHolder上
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int index) {
        //判定列表项还是上拉加载区域
        if(holder instanceof ItemViewHolder){
            ChannelBean channelBean = listitemList.get(index);
            final ItemViewHolder itemViewHolder = ((ItemViewHolder)holder);

            itemViewHolder.text_icon_1.setText(channelBean.getChannelNamee());
            itemViewHolder.img_icon_1.setImageResource(channelBean.getChannelImage());

            //如果设置了回调,则设置了点击事件
            if(mOnItemClickListener != null){
                itemViewHolder.listitemLayout.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        int position = itemViewHolder.getLayoutPosition();//增加或删除数据时,position和index就不一样了
                        mOnItemClickListener.onItemClick(itemViewHolder.listitemLayout,position);
                    }
                });
                itemViewHolder.listitemLayout.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        int position = itemViewHolder.getLayoutPosition();
                        mOnItemClickListener.onItemLongClick(itemViewHolder.listitemLayout,position);
                        return false;
                    }
                });
            }

        }
    }

    //获取总的条目数
    @Override
    public int getItemCount() {
        return listitemList.size();
    }

    //声明grid列表项ViewHolder
    private class ItemViewHolder extends RecyclerView.ViewHolder {
        public ItemViewHolder(@NonNull View itemView) {
            super(itemView);

            listitemLayout = (LinearLayout)itemView.findViewById(R.id.listitem_layout);
            linear_01 = (ConstraintLayout)itemView.findViewById(R.id.linear_01);
            img_icon_1 = (ImageView)itemView.findViewById(R.id.img_icon_1);
            text_icon_1 = (TextView)itemView.findViewById(R.id.text_icon_1);
        }
        LinearLayout listitemLayout;
        ConstraintLayout linear_01;
        ImageView img_icon_1;
        TextView text_icon_1;
    }

    //添加item   用于动画表现
    public void addItem(int position,ChannelBean listitemBean){
        listitemList.add(position,listitemBean);
        notifyItemChanged(position);
    }

    //删除item   用于动画表现
    public void removeItem(int position){
        listitemList.remove(position);
        notifyItemChanged(position);
    }

    /*==================添加OnItemClickListener回调==============*/
    public interface OnItemClickListener{
        void onItemClick(View view,int position);
        void onItemLongClick(View view,int position);
    }

    private OnItemClickListener mOnItemClickListener;

    public void setOnItemClickListener(OnItemClickListener mOnItemClickListener){
        this.mOnItemClickListener = mOnItemClickListener;
    }


}

接下来我们来看具体调用方法。

我们新建一个activity--TestRecyclerView,得到TestRecyclerView.java和activity_test_recycler_view.xml

我们先设置xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".TestRecyclerView">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:cacheColorHint="#00000000"
        android:divider="@null"
        android:listSelector="#00000000"
        android:scrollbars="none"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/recycler_view2"
        />
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view2"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:cacheColorHint="#00000000"
        android:divider="@null"
        android:listSelector="#00000000"
        android:scrollbars="none"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/recycler_view"
        />

</androidx.constraintlayout.widget.ConstraintLayout>

这里我们设置了两个recyclerview,主要是为了加上点击增加和删除的功能。

首先,我们先声明RecyclerView,ArrayList<ChannelBean>,ChannelAdapter,分别声明两个。

然后在onCreate调用三个方法,initViews,initDatas,initEvents,三个方法分别用于初始化控件view,初始化数据data,还有初始化事件event(也可以理解为绑定事件)。

initViews方法的实现只需要通过id获取两个RecyclerView就可以了

private void initViews() {
        mRecyclerView = findViewById(R.id.recycler_view);
        mRecyclerView2 = findViewById(R.id.recycler_view2);
    }

然后我们需要设置view的数据。首先声明ArrayList<ChannelBean>,然后通过for循环给list赋值(例子需要。实际中可以网络获取data然后输入。)。获得数据之后,我们还需要给recyclerview指定布局管理器。常见的布局管理器有LinearLayoutManager,GridLayoutManager和StaggeredGridLayoutManager,分别对应列表布局,表格布局和瀑布流布局(瀑布流布局是一个很好用的布局,做图片手机网站的时候可以使用这种模式。目前国内的图片网站最常用的就是瀑布流。例如花瓣。当然,花瓣是网页的,只是形式上使用的是瀑布流的布局而已。)

因为我们有两个recyclerView,所以要设置两次。这里只举例一次。

private void initDatas() {
        //初始化集合
        mChannelBeanArrayList = new ArrayList<ChannelBean>();
        for (int i = 0;i<5;i++){
            ChannelBean channelBean = new ChannelBean();
            channelBean.setChannelId("id:"+i);
            channelBean.setChannelImage(getResources().getIdentifier("icon_0"+(i+1),"mipmap",getPackageName()));
            channelBean.setChannelNamee("图标"+i);

            mChannelBeanArrayList.add(channelBean);
        }

        //设置布局管理器
        GridLayoutManager gridLayoutManager = new GridLayoutManager(this,3);
        mRecyclerView.setLayoutManager(gridLayoutManager);


        //设置适配器
        if (mChannelAdapter == null){
            mChannelAdapter = new ChannelAdapter(this,mChannelBeanArrayList);
            mRecyclerView.setAdapter(mChannelAdapter);
            //添加分割线
            //设置添加删除动画
            //调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局
            mRecyclerView.setSelected(true);
        }else {
            mChannelAdapter.notifyDataSetChanged();
        }
    }

隐藏的知识点:当我们的drawable文件的命名是符合规律的,然后我们需要循环调用的时候该怎么办,可以通过

getResources().getIdentifier("icon_0"+(i+1),"mipmap",getPackageName())

来循环获取图片id。第一个参数为图片命名的规律,第二个为R文件的类型。可以是“mipmap”,“drawable”,“id”等。

接下来,我们要设置点击事件。也就是初始化events了。

逻辑也简单,当我们点击recyclerview1的item时,先往recyclerview2添加该item,然后删除recyclerview1里的item

private void initEvents() {
        //列表适配器的点击监听事件
        mChannelAdapter.setOnItemClickListener(new ChannelAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(TestRecyclerView.this,mChannelBeanArrayList.get(position).getChannelNamee(),Toast.LENGTH_SHORT).show();
                mChannelAdapter2.addItem(mChannelBeanArrayList2.size(),mChannelBeanArrayList.get(position));
                mChannelAdapter2.notifyDataSetChanged();
                mChannelAdapter.removeItem(position);
                mChannelAdapter.notifyDataSetChanged();
            }

            @Override
            public void onItemLongClick(View view, int position) {
                currentPagePosition = position;//拖拽时给它赋值。
                Toast.makeText(TestRecyclerView.this,mChannelBeanArrayList.get(position).getChannelNamee()+"长按",Toast.LENGTH_SHORT).show();
            }
        });
        mChannelAdapter2.setOnItemClickListener(new ChannelAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(TestRecyclerView.this,mChannelBeanArrayList2.get(position).getChannelNamee(),Toast.LENGTH_SHORT).show();
                if (mChannelBeanArrayList.size()<5){
                    mChannelAdapter.addItem(mChannelBeanArrayList.size(),mChannelBeanArrayList2.get(position));
                    mChannelAdapter.notifyDataSetChanged();
                    mChannelAdapter2.removeItem(position);
                    mChannelAdapter2.notifyDataSetChanged();
                }

            }

            @Override
            public void onItemLongClick(View view, int position) {

            }
        });
    }

这样,我们运行的时候,就可以看到点击删除item和增加item了。

什么,拖拽呢?不要急,接下来就是拖拽功能的集成。

我们还是在initEvent方法内调用一个新的方法。initItemTouchHelper();

调用之后,我们来实现这个方法。

首先,实现itemTouchHelper方法之前,我们需要在声明几个变量。

分别是ItemTouchHelper,currentPagePosition,currentPageNewPosition和newOrder四个参数。

private ItemTouchHelper itemTouchHelper;
    private int currentPagePosition = -1;//当前拖拽的item的初始位置,从零开始【长按时赋值】,用来和currentPageNewPosition对比进行判断是否执行排序接口
    private int currentPageNewPosition = -1;//当前item拖拽后的位置,从0开始
    private boolean newOrder = false;//标记是否拖拽排序过,默认是false

然后我们在initItemTouchHelper里,我们必须实现的有三个方法,onMovementFlags,onMove,onSwiped,onMovementFlags函数主要用于设置是否处理拖拽和滑动事件,并返回拖拽和滑动的操作方向。

@Override
            public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
                int dragFlags = 0;//dragFlags 是拖拽标志
                int swipeFlags = 0;//swipeFlags 是侧滑标志,我们把swipeFlags设置为0,表示不处理侧滑
                if (recyclerView.getLayoutManager() instanceof GridLayoutManager){
                    dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;

                    swipeFlags = 0;
                }else {
                    dragFlags = 0;
                    swipeFlags = 0;
                }
                return makeMovementFlags(dragFlags,swipeFlags);
            }

因为我们不处理滑动事件,所以不处理onSwiped方法,接下来我们实现onMove方法

首先我们先获取item原先的position和移动之后所处位置的position。然后当移动后的position大于原先的position,我们将这两个中间的所有item的position都减1.如果移动后的position小于原先的position,我们将这两个item中间的所有item的position都加1。最后我们需要调用一个方法mChannelAdapter.notifyItemMoved(fromPosition,toPosition);

@Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
                int fromPosition = viewHolder.getAdapterPosition();
                int toPosition = target.getAdapterPosition();
                //添加判断,实现某一项不可交换
                if(fromPosition<toPosition){
                    for (int i = fromPosition; i<toPosition;i++){
                        Collections.swap(mChannelBeanArrayList,i,i+1);
                    }
                }else {
                    for (int i = fromPosition; i>toPosition;i--){
                        Collections.swap(mChannelBeanArrayList,i,i-1);
                    }
                }
                mChannelAdapter.notifyItemMoved(fromPosition,toPosition);
                return true;
            }

这时候我们已经实现了拖拽方法了,但是为了我们拖拽项目的时候更加明显,所以我们添加一个效果,当我们长按item进行拖拽的时候,产生震动,并让背景颜色发生改变。当我们松开手指停止拖拽的时候,我们将样式还原。

/**
             * 当我们希望拖拽的item在拖拽过程中发生震动或者颜色变深,需要重写下面两个方法
             * 当长按item的时候(拖拽开始的时候)调用
             * ACTION_STATE_IDLE; 闲置状态
             * ACTION_STATE_SWIPE; 滑动状态
             * ACTION_STATE_DRAG; 拖拽状态
             */
            @Override
            public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
                if (actionState == ItemTouchHelper.ACTION_STATE_DRAG){
                    //获取系统震动服务
                    Vibrator vib = (Vibrator) TestRecyclerView.this.getSystemService(Service.VIBRATOR_SERVICE);
                    //震动70毫秒
                    vib.vibrate(70);
                    viewHolder.itemView.setPressed(true);
                    //设置拖动时的背景颜色
                    viewHolder.itemView.setBackgroundColor(Color.parseColor("#FF0000"));
                }
                super.onSelectedChanged(viewHolder, actionState);
            }

            //当手指松开的时候(拖拽或滑动完成时)调用,这时候我们将item恢复为原来的状态。
            @Override
            public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
                super.clearView(recyclerView, viewHolder);
                viewHolder.itemView.setPressed(false);
                currentPageNewPosition = viewHolder.getAdapterPosition();
                if (!(currentPagePosition == currentPageNewPosition)){
                    newOrder = true;
                    //执行其他方法,比如设置拖拽后的item为选中状态等,暂时没有想到其他有用的效果
                }
                viewHolder.itemView.setBackground(null);
                //解决重叠问题
                mChannelAdapter.notifyDataSetChanged();
            }

这样我们就设置好了拖拽和滑动的动作,然后我们要将这个itemTouchHelper添加到recyclerview里,

 //设置是否可以排序
        itemTouchHelper.attachToRecyclerView(mRecyclerView);

其中,我们可以看到我们只对recyclerview1进行排序,并且在onMove里只对ArrayList1和adapter1进行更改,所以我们的拖拽功能只对recyclerview1起效。

完整的TestRecyclerView.java为

public class TestRecyclerView extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private ArrayList<ChannelBean> mChannelBeanArrayList;
    private ChannelAdapter mChannelAdapter;

    private RecyclerView mRecyclerView2;
    private ArrayList<ChannelBean> mChannelBeanArrayList2;
    private ChannelAdapter mChannelAdapter2;

    /**
     * 拖拽功能
     * @param savedInstanceState
     */
    private ItemTouchHelper itemTouchHelper;
    private int currentPagePosition = -1;//当前拖拽的item的初始位置,从零开始【长按时赋值】,用来和currentPageNewPosition对比进行判断是否执行排序接口
    private int currentPageNewPosition = -1;//当前item拖拽后的位置,从0开始
    private boolean newOrder = false;//标记是否拖拽排序过,默认是false

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_recycler_view);

        //分别为初始化控件,初始化数据和初始化事件
        initViews();
        initDatas();
        initEvents();
    }

    private void initEvents() {
        //列表适配器的点击监听事件
        mChannelAdapter.setOnItemClickListener(new ChannelAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(TestRecyclerView.this,mChannelBeanArrayList.get(position).getChannelNamee(),Toast.LENGTH_SHORT).show();
                mChannelAdapter2.addItem(mChannelBeanArrayList2.size(),mChannelBeanArrayList.get(position));
                mChannelAdapter2.notifyDataSetChanged();
                mChannelAdapter.removeItem(position);
                mChannelAdapter.notifyDataSetChanged();
            }

            @Override
            public void onItemLongClick(View view, int position) {
                currentPagePosition = position;//拖拽时给它赋值。
                Toast.makeText(TestRecyclerView.this,mChannelBeanArrayList.get(position).getChannelNamee()+"长按",Toast.LENGTH_SHORT).show();
            }
        });
        mChannelAdapter2.setOnItemClickListener(new ChannelAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(TestRecyclerView.this,mChannelBeanArrayList2.get(position).getChannelNamee(),Toast.LENGTH_SHORT).show();
                if (mChannelBeanArrayList.size()<5){
                    mChannelAdapter.addItem(mChannelBeanArrayList.size(),mChannelBeanArrayList2.get(position));
                    mChannelAdapter.notifyDataSetChanged();
                    mChannelAdapter2.removeItem(position);
                    mChannelAdapter2.notifyDataSetChanged();
                }

            }

            @Override
            public void onItemLongClick(View view, int position) {

            }
        });

        //设置完数据之后,开始初始化拖拽事件
        initItemTouchHelper();
    }

    private void initDatas() {
        //初始化集合
        mChannelBeanArrayList = new ArrayList<ChannelBean>();
        mChannelBeanArrayList2 = new ArrayList<ChannelBean>();
        for (int i = 0;i<5;i++){
            ChannelBean channelBean = new ChannelBean();
            ChannelBean channelBean2 = new ChannelBean();
            channelBean.setChannelId("id:"+i);
            channelBean.setChannelImage(getResources().getIdentifier("icon_0"+(i+1),"mipmap",getPackageName()));
//            channelBean.setChannelImage(R.mipmap.icon_01);
            channelBean.setChannelNamee("图标"+i);


            channelBean2.setChannelId("id2:"+i);
            channelBean2.setChannelImage(getResources().getIdentifier("icon_0"+(i+1),"mipmap",getPackageName()));
//            channelBean.setChannelImage(R.mipmap.icon_01);
            channelBean2.setChannelNamee("图标22"+i);

            mChannelBeanArrayList.add(channelBean);
            mChannelBeanArrayList2.add(channelBean2);
        }

        //设置布局管理器
        GridLayoutManager gridLayoutManager = new GridLayoutManager(this,3);
        mRecyclerView.setLayoutManager(gridLayoutManager);

        GridLayoutManager gridLayoutManager2 = new GridLayoutManager(this,3);
        mRecyclerView2.setLayoutManager(gridLayoutManager2);

        //设置适配器
        if (mChannelAdapter == null){
            mChannelAdapter = new ChannelAdapter(this,mChannelBeanArrayList);
            mRecyclerView.setAdapter(mChannelAdapter);
            //添加分割线
            //设置添加删除动画
            //调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局
            mRecyclerView.setSelected(true);
        }else {
            mChannelAdapter.notifyDataSetChanged();
        }

        if (mChannelAdapter2 == null){
            mChannelAdapter2 = new ChannelAdapter(this,mChannelBeanArrayList2);
            mRecyclerView2.setAdapter(mChannelAdapter2);
            //添加分割线
            //设置添加删除动画
            //调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局
            mRecyclerView2.setSelected(true);
        }else {
            mChannelAdapter2.notifyDataSetChanged();
        }

    }

    private void initItemTouchHelper() {
        //拖拽
        itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
            //开启长按拖拽功能,默认为true【暂时用不到】
            //如果需要我们开始自定义拖拽和滑动,可以设置为false,然后调用itemTouchHelper.startDrag(ViewHolder)方法来开启
            @Override
            public boolean isLongPressDragEnabled() {
                return true;
            }

            //开启滑动功能,默认为true【暂时用不到】
            //如果需要我们自定义拖拽和滑动,可以设置为false,然后调用itemTouchHlper.startDrag(ViewHolder)方法来开启
            @Override
            public boolean isItemViewSwipeEnabled() {
                return true;
            }


            /**
             * 用于设置是否处理拖拽事件和滑动事件,以及拖拽和滑动的操作方向。
             * 如果是列表的recyclerview,则拖拽事件只有up和down两个方向。
             * 如果是网格类型的则有up,down,left,right四个方向
             * @param recyclerView
             * @param viewHolder
             * @return
             */
            @Override
            public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
                int dragFlags = 0;//dragFlags 是拖拽标志
                int swipeFlags = 0;//swipeFlags 是侧滑标志,我们把swipeFlags设置为0,表示不处理侧滑
                if (recyclerView.getLayoutManager() instanceof GridLayoutManager){
                    dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;

                    swipeFlags = 0;
                }else {
                    dragFlags = 0;
                    swipeFlags = 0;
                }
                return makeMovementFlags(dragFlags,swipeFlags);
            }

            /**
             * 如果设置了非 0 的dragFlags,当我们长按item的时候就会进入拖拽并在拖拽过程中不断回调onMove方法
             * 我们在这个方法里获取当前拖拽的item和已经被拖拽到所处位置的item的viewholder,
             * 有了这2个viewhol,我们就可以交换他们的数据集并调用Adapter的notifyItemMoved方法来刷新item
             * @param recyclerView
             * @param viewHolder
             * @param target
             * @return
             */
            @Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
                int fromPosition = viewHolder.getAdapterPosition();
                int toPosition = target.getAdapterPosition();
                //添加判断,实现某一项不可交换
                if(fromPosition<toPosition){
                    for (int i = fromPosition; i<toPosition;i++){
                        Collections.swap(mChannelBeanArrayList,i,i+1);
                    }
                }else {
                    for (int i = fromPosition; i>toPosition;i--){
                        Collections.swap(mChannelBeanArrayList,i,i-1);
                    }
                }
                mChannelAdapter.notifyItemMoved(fromPosition,toPosition);
                return true;
            }

            //如果我们设置了非 0 的swipeFlags,我们在侧滑的时候就会回调onswiped方法,我们不处理这个方法的话,就相当于没有侧滑事件
            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {

            }

            /**
             * 当我们希望拖拽的item在拖拽过程中发生震动或者颜色变深,需要重写下面两个方法
             * 当长按item的时候(拖拽开始的时候)调用
             * ACTION_STATE_IDLE; 闲置状态
             * ACTION_STATE_SWIPE; 滑动状态
             * ACTION_STATE_DRAG; 拖拽状态
             */
            @Override
            public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
                if (actionState == ItemTouchHelper.ACTION_STATE_DRAG){
                    //获取系统震动服务
                    Vibrator vib = (Vibrator) TestRecyclerView.this.getSystemService(Service.VIBRATOR_SERVICE);
                    //震动70毫秒
                    vib.vibrate(70);
                    viewHolder.itemView.setPressed(true);
                    //设置拖动时的背景颜色
                    viewHolder.itemView.setBackgroundColor(Color.parseColor("#FF0000"));
                }
                super.onSelectedChanged(viewHolder, actionState);
            }

            //当手指松开的时候(拖拽或滑动完成时)调用,这时候我们将item恢复为原来的状态。
            @Override
            public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
                super.clearView(recyclerView, viewHolder);
                viewHolder.itemView.setPressed(false);
                currentPageNewPosition = viewHolder.getAdapterPosition();
                if (!(currentPagePosition == currentPageNewPosition)){
                    newOrder = true;
                    //执行其他方法,比如设置拖拽后的item为选中状态等,暂时没有想到其他有用的效果
                }
                viewHolder.itemView.setBackground(null);
                //解决重叠问题
                mChannelAdapter.notifyDataSetChanged();
            }
        });
        //设置是否可以排序
        itemTouchHelper.attachToRecyclerView(mRecyclerView);
    }

    private void initViews() {
        mRecyclerView = findViewById(R.id.recycler_view);
        mRecyclerView2 = findViewById(R.id.recycler_view2);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>