使用CardView作为RecyclerView的Item并实现拖拽和左滑删除

2017-11-27 by 技术学习小组

引言

CardView是Android 5.0系统之后引入的众多控件之一,实现之后的效果也是比较酷的,它经常被用在RecyclerView和ListView中的Item中。今天我们就来了解一下CardView的属性,然后使用CardView和RecyclerView结合实现一个可以拖拽Item的布局。


CardView的属性

CardView继承自FrameLayout,所以子控件的布局规则和FrameLayout的一样,是按照层次堆叠的


下面是CardView的一些常用属性:



CardView的基本使用

先看一下效果:



图1.CardView的基本使用


这是一个CardView,多个罗列起来看起啦会更酷,好了,我们先看一下代码:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:paddingTop="10dp"
android:layout_width="match_parent" android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:layout_width="match_parent" android:layout_height="60dp"
android:layout_marginLeft="10dp" android:layout_marginRight="10dp"
app:contentPaddingLeft="20dp" app:cardBackgroundColor="@color/colorPrimary"
app:cardCornerRadius="5dp" app:cardElevation="5dp"
android:layout_marginBottom="10dp"
app:cardPreventCornerOverlap="true">
<LinearLayout
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="horizontal" android:gravity="center_vertical">
<!--左侧图片-->
<ImageView
android:layout_width="50dp" android:layout_height="50dp"
android:src="@mipmap/ic_launcher"/>
<LinearLayout
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:layout_marginLeft="10dp"
android:gravity="center_vertical">
<!--姓名-->
<TextView
android:id="@+id/txt_name" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:textSize="16sp"
android:textColor="@android:color/white" android:text="王二"/>
<!--描述-->
<TextView
android:id="@+id/txt_describe" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_marginTop="5dp"
android:textSize="12sp" android:textColor="@android:color/darker_gray"
android:text="一个很厉害的人"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>

看完了布局文件,是不是觉得这个布局不仅炫酷而且使用简单,下面我们把它应用到RecyclerView中,看起来会更炫酷。


CardView应用在RecyclerView中

CardView通常会应用在RecyclerView和ListView中,今天我们就讲一讲如何应用在RecyclerView中。我们现在在大多数应用或者手机系统界面中会见到这样的效果:



图2.


是不是觉得很棒,下面我们就用CardView和RecyclerView来实现一下这个效果。


布局文件

我们实现这个效果的第一步是先添加依赖库:


implementation 'com.android.support:recyclerview-v7:26.+'
implementation 'com.android.support:cardview-v7:26.+'

然后写一下布局文件,Item的布局文件我就直接采用上面的代码了,然后再写一个主界面的布局文件,比较简单,如下:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical" android:paddingTop="10dp"
tools:context="com.jyx.demo.myapplication.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

Activity内代码
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recyclerView);
//设置LayoutManager
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(linearLayoutManager);
//绑定adapter
MyAdapter myAdapter = new MyAdapter(this);
mRecyclerView.setAdapter(myAdapter);
}
//adapter
class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
LayoutInflater mInflater;
List<MData> mList = addData();
public MyAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.item_my_recyclerview,parent,false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(MyAdapter.ViewHolder holder, int position) {
holder.mName.setText(mList.get(position).getUserName());
holder.mDescribe.setText(mList.get(position).getDescription());
}
@Override
public int getItemCount() {
return mList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView mName;
private TextView mDescribe;
public ViewHolder(View itemView) {
super(itemView);
mName = itemView.findViewById(R.id.txt_name);
mDescribe = itemView.findViewById(R.id.txt_describe);
}
}
}
//制造一个数据源
private class MData{
String userName;
String description;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
private List<MData> addData(){
List<MData> list = new ArrayList();
MData mData = new MData();
mData.setUserName("王二");
mData.setDescription("一个很厉害的人");
list.add(mData);
mData = new MData();
mData.setUserName("张三");
mData.setDescription("呵呵");
list.add(mData);
mData = new MData();
mData.setUserName("李四");
mData.setDescription("嘻嘻");
list.add(mData);
mData = new MData();
mData.setUserName("赵一");
mData.setDescription("呵呵");
list.add(mData);
mData = new MData();
mData.setUserName("钱多");
mData.setDescription("地主家的傻儿子");
list.add(mData);
return list;
}
}

好了,这就是一个没有任何效果的列表界面,我一看一下效果:



图3.列表界面


ItemTouchHelper

想实现拖拽和滑动删除的效果,很可惜RecyclerView并没有提供现成的API供我们使用,但是SDK为我们提供了 ItemTouchHelper 这样一个工具类帮助我们来轻松实现这些功能,我们先来了解一下ItemTouchHelper。官方文档是这样介绍的:


This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.Depending on which functionality you support, you should override onMove(RecyclerView, ViewHolder, ViewHolder) and / or onSwiped(ViewHolder, int).


大致意思就是,这是个工具类,可以实拖拽移动和策划删除,使用这个工具需要RecyclerView和Callback。同时需要重写onMove()和onSwiped()方法。接下来就讲讲如何使用ItemTouchHlper。


1.新建一个接口,并且让Adapter实现


我们选择使用一个接口来实现Adapter和ItemTouchHelper之间涉及数据的操作,因为ItemTouchHelper完成触摸的各种动画以后,就要对Adapter的数据进行操作,比如我们在侧滑删除以后,最后需要调用Adapter的notifyItemRemove()方法来移除该数据。所以我们可以把数据操作的部分抽象成一个接口方法,让Callbac调用它即可。具体如下:


新建一个接口:


ItemTouchHelperAdapter


public interface ItemTouchHelperAdapter {
//移动item
public void onItemMove(int fromPosition,int toPosition);
//删除item
public void onItemDelete(int position);
}

之后让Adapter实现这个接口


class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implementsItemTouchHelperAdapter{
LayoutInflater mInflater;
List<MData> mList = addData();
public MyAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.item_my_recyclerview,parent,false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(MyAdapter.ViewHolder holder, int position) {
holder.mName.setText(mList.get(position).getUserName());
holder.mDescribe.setText(mList.get(position).getDescription());
}
@Override
public int getItemCount() {
return mList.size();
}
@Override
public void onItemMove(int fromPosition, int toPosition) {
//交换位置
Collections.swap(mList,fromPosition,toPosition);
notifyItemMoved(fromPosition,toPosition);
}
@Override
public void onItemDelete(int position) {
//移除数据
mList.remove(position);
notifyItemRemoved(position);
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView mName;
private TextView mDescribe;
public ViewHolder(View itemView) {
super(itemView);
mName = itemView.findViewById(R.id.txt_name);
mDescribe = itemView.findViewById(R.id.txt_describe);
}
}
}

接下来我们直接在Callback中直接调用接口里的方法就可以了。


2.新建Callback方法,继承ItemTouchHelper.Callback


官方文档已经告诉我们,使用ItemTouchHelper需要一个Callback,这个Callback是ItemTouchHelper.Callback的子类,我们需要新建一个类来继承ItemTouchHelper.Callback,然后重写一些方法来实现我们的需求。代码如下:


public class myItemTouchHelperCallBack extends ItemTouchHelper.Callback{
private ItemTouchHelperAdapter itemTouchHelperAdapter;
public myItemTouchHelperCallBack(ItemTouchHelperAdapter itemTouchHelperAdapter) {
this.itemTouchHelperAdapter = itemTouchHelperAdapter;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
//允许上下拖动
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
//允许从右向左滑动
int swipeFlags = ItemTouchHelper.LEFT;
return makeMovementFlags(dragFlags,swipeFlags);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//onItemMove接口里的方法
itemTouchHelperAdapter.onItemMove(viewHolder.getAdapterPosition(),target.getAdapterPosition());
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
//onItemDelete接口里的方法
itemTouchHelperAdapter.onItemDelete(viewHolder.getAdapterPosition());
}
@Override
public boolean isLongPressDragEnabled() {
//该方法返回值为true时,表示支持长按ItemView拖动
return true;
}
@Override
public boolean isItemViewSwipeEnabled() {
//该方法返回true时,表示如果用户触摸并且左滑了view,那么可以执行滑动删除操作,就是可以调用onSwiped()方法
return true;
}
}

ItemTouchHelper.Callback还有其他几个常用方法:


public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) :从静止状态变为拖拽或者滑动的时候会调用该方法,参数actionState表示当前状态。
public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) :当用户操作完某个item并且动画也结束后会调用该方法,一般我们在该方法内恢复ItemView的初始状态,防止由于复用而产生的错乱问题。
public void onChildDraw(…) :我们可以在这个方法内实现我们自定义的交互规则或者自定义动画。

这样下来我们就只剩下一步了。


3.为RecyclerView添加ItemTouchHelper


代码如下:


ItemTouchHelper.Callback callback = new myItemTouchHelperCallBack(myAdapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(mRecyclerView);

这样,我们就实现了我们的需求,我们一起来看看效果:



图4.拖拽和左滑删除


好了,我们的需求完成了,效果是不是很炫酷,当然大家可以根据自己的需求做出更炫酷的效果,到时候别忘了与大家一起分享。最后欢迎指正,感谢阅读。


第七城市

栏目导航(关闭)