版权声明:本文为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