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


    版权声明:本文为HaiyuKing原创文章,转载请注明出处!

    前言

    记录使用ItemTouchHelper对Recyclerview进行拖拽排序功能的实现。

    效果图

    代码分析

    ItemTouchHelper是一个工具类,可实现侧滑删除和拖拽移动,使用这个工具类需要RecyclerView和Callback。同时根据需要重写onMove和onSwiped方法。

    使用步骤

    一、项目组织结构图

    注意事项:

    1、  导入类文件后需要change包名以及重新import R文件路径

    2、  Values目录下的文件(strings.xml、dimens.xml、colors.xml等),如果项目中存在,则复制里面的内容,不要整个覆盖

    二、导入步骤

    (1)在build.gradle中引用recyclerview【版本号和appcompat保持一致】

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 27
        defaultConfig {
            applicationId "com.why.project.recyclerviewitemtouchhelperdemo"
            minSdkVersion 16
            targetSdkVersion 27
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'com.android.support:appcompat-v7:27.1.1'
        implementation 'com.android.support.constraint:constraint-layout:1.1.2'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'com.android.support.test:runner:1.0.2'
        androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    
        //RecyclerView
        compile "com.android.support:recyclerview-v7:27.1.1"
    }

    (2)在AndroidManifest.xml中声明震动权限

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="com.why.project.recyclerviewitemtouchhelperdemo">
    
        <!-- ======================RecyclerView拖拽震动效果====================== -->
        <uses-permission android:name="android.permission.VIBRATE" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
    
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
            </activity>
        </application>
    
    </manifest>

    (3)在项目中实现Recyclerview基本数据展现

    1、创建Bean类

    package com.why.project.recyclerviewitemtouchhelperdemo.bean;
    
    /**
     * Created by HaiyuKing
     * Used 列表项的bean类
     */
    
    public class ChannelBean {
        private String channelId;//频道id值
        private String channelName;//频道名称
    
        public String getChannelId() {
            return channelId;
        }
    
        public void setChannelId(String channelId) {
            this.channelId = channelId;
        }
    
        public String getChannelName() {
            return channelName;
        }
    
        public void setChannelName(String channelName) {
            this.channelName = channelName;
        }
    }
    ChannelBean.java

    2、创建Adapter以及item的布局文件

    package com.why.project.recyclerviewitemtouchhelperdemo.adapter;
    
    import android.content.Context;
    import android.support.v7.widget.RecyclerView;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    
    import com.why.project.recyclerviewitemtouchhelperdemo.R;
    import com.why.project.recyclerviewitemtouchhelperdemo.bean.ChannelBean;
    
    import java.util.ArrayList;
    
    /**
     * Created by HaiyuKing
     * Used 频道列表适配器
     */
    
    public class ChannelAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
        /**上下文*/
        private Context myContext;
        /**频道集合*/
        private ArrayList<ChannelBean> listitemList;
    
        /**
         * 构造函数
         */
        public ChannelAdapter(Context context, ArrayList<ChannelBean> itemlist) {
            myContext = context;
            listitemList = itemlist;
        }
    
        /**
         * 获取总的条目数
         */
        @Override
        public int getItemCount() {
            return listitemList.size();
        }
    
        /**
         * 创建ViewHolder
         */
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(myContext).inflate(R.layout.channel_list_item, parent, false);
            ItemViewHolder itemViewHolder = new ItemViewHolder(view);
            return itemViewHolder;
        }
    
        /**
         * 声明grid列表项ViewHolder*/
        static class ItemViewHolder extends RecyclerView.ViewHolder
        {
            public ItemViewHolder(View view)
            {
                super(view);
    
                listItemLayout = (LinearLayout) view.findViewById(R.id.listitem_layout);
                mChannelName = (TextView) view.findViewById(R.id.tv_channelName);
            }
    
            LinearLayout listItemLayout;
            TextView mChannelName;
        }
    
        /**
         * 将数据绑定至ViewHolder
         */
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int index) {
    
            //判断属于列表项还是上拉加载区域
            if(viewHolder instanceof ItemViewHolder){
                ChannelBean channelBean = listitemList.get(index);
                final ItemViewHolder itemViewHold = ((ItemViewHolder)viewHolder);
    
                itemViewHold.mChannelName.setText(channelBean.getChannelName());
    
                //如果设置了回调,则设置点击事件
                if (mOnItemClickLitener != null)
                {
                    itemViewHold.listItemLayout.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            int position = itemViewHold.getLayoutPosition();//在增加数据或者减少数据时候,position和index就不一样了
                            mOnItemClickLitener.onItemClick(itemViewHold.listItemLayout, position);
                        }
                    });
                    //长按事件
                    itemViewHold.listItemLayout.setOnLongClickListener(new View.OnLongClickListener() {
                        @Override
                        public boolean onLongClick(View view) {
                            int position = itemViewHold.getLayoutPosition();//在增加数据或者减少数据时候,position和index就不一样了
                            mOnItemClickLitener.onItemLongClick(itemViewHold.listItemLayout, position);
                            return false;
                        }
                    });
                }
    
            }
        }
    
        /**
         * 添加Item--用于动画的展现*/
        public void addItem(int position,ChannelBean listitemBean) {
            listitemList.add(position,listitemBean);
            notifyItemInserted(position);
        }
        /**
         * 删除Item--用于动画的展现*/
        public void removeItem(int position) {
            listitemList.remove(position);
            notifyItemRemoved(position);
        }
    
        /*=====================添加OnItemClickListener回调================================*/
        public interface OnItemClickLitener
        {
            void onItemClick(View view, int position);
            void onItemLongClick(View view, int position);
        }
    
        private OnItemClickLitener mOnItemClickLitener;
    
        public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
        {
            this.mOnItemClickLitener = mOnItemClickLitener;
        }
    }
    ChannelAdapter.java
    <?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"
        android:orientation="vertical"
        android:padding="10dp"
        android:layout_margin="10dp"
        android:background="#c5c5c5">
    
        <TextView
            android:id="@+id/tv_channelName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="频道名称"
            android:textSize="18sp"
            android:layout_gravity="center"/>
    
    </LinearLayout>
    channel_list_item.xml

    3、在Activity布局文件中引用Recyclerview控件

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:cacheColorHint="#00000000"
            android:divider="@null"
            android:listSelector="#00000000"
            android:scrollbars="none"
            />
    
    </RelativeLayout>

    4、在Activity类中初始化recyclerview数据

    package com.why.project.recyclerviewitemtouchhelperdemo;
    
    import android.app.Service;
    import android.graphics.Color;
    import android.os.Bundle;
    import android.os.Vibrator;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.GridLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.support.v7.widget.helper.ItemTouchHelper;
    import android.util.Log;
    import android.view.View;
    import android.widget.Toast;
    
    import com.why.project.recyclerviewitemtouchhelperdemo.adapter.ChannelAdapter;
    import com.why.project.recyclerviewitemtouchhelperdemo.bean.ChannelBean;
    
    import java.util.ArrayList;
    import java.util.Collections;
    
    public class MainActivity extends AppCompatActivity {
    
        private RecyclerView mRecyclerView;
        private ArrayList<ChannelBean> mChannelBeanArrayList;
        private ChannelAdapter mChannelAdapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initViews();
            initDatas();
            initEvents();
    
        }
    
        private void initViews() {
            mRecyclerView = findViewById(R.id.recycler_view);
        }
    
        private void initDatas() {
            //初始化集合
            mChannelBeanArrayList = new ArrayList<ChannelBean>();
            for(int i=0; i<10;i++){
                ChannelBean channelBean = new ChannelBean();
                channelBean.setChannelId("123"+i);
                channelBean.setChannelName("频道"+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();
            }
        }

    private void initEvents() { //列表适配器的点击监听事件 mChannelAdapter.setOnItemClickLitener(new ChannelAdapter.OnItemClickLitener() { @Override public void onItemClick(View view, int position) { Toast.makeText(MainActivity.this, mChannelBeanArrayList.get(position).getChannelName(), Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClick(View view, int position) { Toast.makeText(MainActivity.this, "长按", Toast.LENGTH_SHORT).show(); } }); } }

    三、使用方法

    在基本的Recyclerview基础上添加ItemTouchHelper【注意,紫色标记的是用来演示用的,可以去掉

    package com.why.project.recyclerviewitemtouchhelperdemo;
    
    import android.app.Service;
    import android.graphics.Color;
    import android.os.Bundle;
    import android.os.Vibrator;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.GridLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.support.v7.widget.helper.ItemTouchHelper;
    import android.util.Log;
    import android.view.View;
    import android.widget.Toast;
    
    import com.why.project.recyclerviewitemtouchhelperdemo.adapter.ChannelAdapter;
    import com.why.project.recyclerviewitemtouchhelperdemo.bean.ChannelBean;
    
    import java.util.ArrayList;
    import java.util.Collections;
    
    public class MainActivity extends AppCompatActivity {
    
        private RecyclerView mRecyclerView;
        private ArrayList<ChannelBean> mChannelBeanArrayList;
        private ChannelAdapter mChannelAdapter;
    
        /**拖拽功能*/
        private ItemTouchHelper itemTouchHelper;
        private int currentPagePosition = -1;//当前拖拽的item的原始位置,从0开始【长按时赋值】,用来和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_main);
            initViews();
            initDatas();
            initEvents();
    
        }
    
        private void initViews() {
            mRecyclerView = findViewById(R.id.recycler_view);
        }
    
        private void initDatas() {
            //初始化集合
            mChannelBeanArrayList = new ArrayList<ChannelBean>();
            for(int i=0; i<10;i++){
                ChannelBean channelBean = new ChannelBean();
                channelBean.setChannelId("123"+i);
                channelBean.setChannelName("频道"+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();
            }
    
            initItemTouchHelper();
        }
    
        private void initItemTouchHelper() {
            //拖拽
            itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback(){
    
                //开启长按拖拽功能,默认为true【暂时用不到】
                //如果需要我们自定义拖拽和滑动,可以设置为false,然后调用itemTouchHelper.startDrag(ViewHolder)方法来开启!
                @Override
                public boolean isLongPressDragEnabled() {
                    return true;
                }
    
                //开始滑动功能,默认为true【暂时用不到】
                //如果需要我们自定义拖拽和滑动,可以设置为false,然后调用itemTouchHelper.startSwipe(ViewHolder)方法来开启!
                @Override
                public boolean isItemViewSwipeEnabled() {
                    return true;
                }
    
                /*用于设置是否处理拖拽事件和滑动事件,以及拖拽和滑动操作的方向
                比如如果是列表类型的RecyclerView,拖拽只有UP、DOWN两个方向
                而如果是网格类型的则有UP、DOWN、LEFT、RIGHT四个方向
                */
                @Override
                public int getMovementFlags(RecyclerView recyclerView, 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 = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
                        swipeFlags = 0;
                    }
                    Log.w("ItemTouchHelper","{getMovementFlags}dragFlags="+dragFlags+";swipeFlags="+swipeFlags);
                    return makeMovementFlags(dragFlags, swipeFlags);
                }
    
                /*如果我们设置了非0的dragFlags ,那么当我们长按item的时候就会进入拖拽并在拖拽过程中不断回调onMove()方法
                我们就在这个方法里获取当前拖拽的item和已经被拖拽到所处位置的item的ViewHolder,
                有了这2个ViewHolder,我们就可以交换他们的数据集并调用Adapter的notifyItemMoved方法来刷新item*/
                @Override
                public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
                    int fromPosition = viewHolder.getAdapterPosition();//得到拖动ViewHolder的position
                    int toPosition = target.getAdapterPosition();//得到目标ViewHolder的position
                    Log.w("ItemTouchHelper","{onMove}fromPosition="+fromPosition+";toPosition="+toPosition);
                    //这里可以添加判断,实现某一项不可交换
                    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,我们在侧滑item的时候就会回调onSwiped的方法,我们不处理这个事件,空着就行了。*/
                @Override
                public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    
                }
                //我们希望拖拽的Item在拖拽的过程中发生震动或者颜色变深,这样就需要继续重写下面两个方法
                //当长按选中item的时候(拖拽开始的时候)调用
                //ACTION_STATE_IDLE:闲置状态
                //ACTION_STATE_SWIPE:滑动状态
                //ACTION_STATE_DRAG:拖拽状态
                @Override
                public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
                    Log.w("ItemTouchHelper","{onSelectedChanged}actionState="+actionState);
                    if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
                        //获取系统震动服务
                        Vibrator vib = (Vibrator) MainActivity.this.getSystemService(Service.VIBRATOR_SERVICE);
                        //震动70毫秒
                        vib.vibrate(70);
                        viewHolder.itemView.setPressed(true);
                        viewHolder.itemView.setBackgroundColor(Color.parseColor("#ff0000"));//演示拖拽的时候item背景颜色加深(实际情况中去掉)
                    }
                    super.onSelectedChanged(viewHolder, actionState);
                }
    
                //当手指松开的时候(拖拽或滑动完成的时候)调用,这时候我们可以将item恢复为原来的状态(相对于背景颜色加深来说的)
                @Override
                public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
                    super.clearView(recyclerView, viewHolder);
                    Log.w("ItemTouchHelper","{clearView}viewHolder.getAdapterPosition="+viewHolder.getAdapterPosition());
                    viewHolder.itemView.setPressed(false);
                    currentPageNewPosition = viewHolder.getAdapterPosition();
                    Log.w("ItemTouchHelper","{clearView}currentPagePosition="+currentPagePosition);
                    Log.w("ItemTouchHelper","{clearView}currentPageNewPosition="+currentPageNewPosition);
                    if(!(currentPagePosition == currentPageNewPosition)){
                        newOrder = true;
                        //执行其他方法,比如设置拖拽后的item为选中状态
                    }
    
                    viewHolder.itemView.setBackgroundColor(Color.parseColor("#c5c5c5"));//演示拖拽的完毕后item背景颜色恢复原样(实际情况中去掉)
                    mChannelAdapter.notifyDataSetChanged();//解决重叠问题
                }
            });
            //设置是否可以排序
            itemTouchHelper.attachToRecyclerView(mRecyclerView);
        }
    
        private void initEvents() {
            //列表适配器的点击监听事件
            mChannelAdapter.setOnItemClickLitener(new ChannelAdapter.OnItemClickLitener() {
                @Override
                public void onItemClick(View view, int position) {
                    Toast.makeText(MainActivity.this, mChannelBeanArrayList.get(position).getChannelName(), Toast.LENGTH_SHORT).show();
                }
    
                @Override
                public void onItemLongClick(View view, int position) {
                    currentPagePosition = position;//拖拽用到的
                    Toast.makeText(MainActivity.this, "长按", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

    混淆配置

    参考资料

    Android使用ItemTouchHelper打造可拖拽的RecyclerView

    RecyclerView进阶:使用ItemTouchHelper实现拖拽和侧滑删除

    RecyclerView爱恨情仇之ItemTouchHelper

    项目demo下载地址

    https://github.com/haiyuKing/RecyclerViewItemTouchHelperDemo

  • 相关阅读:
    一个利用扩展方法的实例:AttachDataExtensions
    正则表达式语法
    正则表达式30分钟入门教程
    js正则验证两位小数 验证数字最简单正则表达式大全
    SQL Server DBA三十问【转】
    Vue(踩坑)vue.esm.js?efeb:628 [Vue warn]: Error in render: "TypeError: Cannot read property 'length' of undefined" found in
    vue(有必要做的项目优化)
    vue_(根据多种条件过滤评论内容)
    vue(ref父组件使用子组件中定义的方法)
    Vuex(实现加减操作,Vue.set解决自定义属性没有双向数据绑定)
  • 原文地址:https://www.cnblogs.com/whycxb/p/9314441.html
Copyright © 2020-2023  润新知