Android RecyclerView item 拖动、滑动删
在上一文中,介绍了 RecyclerView 的基本使用,
请参考:Android RecyclerView 的基本使用
(下面的代码都是基于上一篇,为了节省篇幅,省略了一些重复代码)。
这一篇主要介绍 item 拖拽、滑动删除等手势操作,以及对点击、滑动事件的监听等。
点击事件监听
和 ListView 通过 setOnItemClickListener 设置监听不同,RecyclerView 需要我们自己添加监听事件,监听事件需要放在自定义的 Adapter中,在其 onCreateViewHolder 或者 onBindViewHolder 中设置,其本质是一样的,都是通过 view 的 setOnClickListener 方法。为了节省篇幅,省略重复代码,如下:
/**
* Created by zhangyb on 2017/6/22.
*/
public class LinearRecycleAdapter extends RecyclerView.Adapter<LinearRecycleAdapter.MyViewHolder> {
@Override
public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = mInflater.inflate(R.layout.item, viewGroup, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(MyViewHolder viewHolder, int position) {
viewHolder.mTextView.setText(mDataList.get(position));
//在 onBindViewHolder 获取到 viewHolder
//通过 viewHolder 获取到 View 来设置监听
setClickListener(viewHolder);
}
//设置点击监听
private void setClickListener(final MyViewHolder viewHolder) {
if (mOnItemClickListener != null) {
//通过 View 的 setOnClickListener 设置监听
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(viewHolder.itemView, viewHolder.getLayoutPosition());
}
});
viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mOnItemClickListener.onItemLongClick(viewHolder.itemView, viewHolder.getLayoutPosition());
return false;
}
});
}
}
//item 点击事件接口,回调到Activity
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
private OnItemClickListener mOnItemClickListener;
//在Activity中调用
public void setOnItemClickListener(OnItemClickListener listener) {
this.mOnItemClickListener = listener;
}
}
上面代码中,也可以在 onCreateViewHolder 中设置监听,因为 onCreateViewHolder 中也可以获取到 View,但是因为获取不到 position,所以如果需要知道 item 的 position,还是需在 onBindViewHolder 中设置。
在 Activity 中 设置监听 并实现接口:
adapter.setOnItemClickListener(new LinearRecycleAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
}
@Override
public void onItemLongClick(View view, int position) {
}
});
实现 item 拖动 交换位置、滑动删除 item
拖动 item 改变位置等操作,是通过 android.support.v7.widget.helper.ItemTouchHelper 这个类来实现的,在 CallBack 回调方法中操作,然后关联到 RecyclerView。直接看代码:
private ItemTouchHelper itemTouchHelper;
itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
//用于设置拖拽和滑动的方向
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
//设置允许拖拽item的方向,线性式布局有2个方向
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
//设置侧滑方向为从两个方向都可以
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
//长摁item拖拽到和另一个item重合时,调用
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView
.ViewHolder target) {
Log.i("zyb", "onMove");
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
String strFrom = dataList.get(from);
dataList.remove(from);
dataList.add(to, strFrom);
adapter.notifyItemMoved(from, to);//更新适配器中item的位置
return true;
}
//这里处理滑动删除
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
dataList.remove(from);
adapter.notifyItemRemoved(position);
}
//按下和松开item时 调用
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
Log.i("zyb", "onSelectedChanged");
super.onSelectedChanged(viewHolder, actionState);
if (viewHolder != null) {
viewHolder.itemView.setBackgroundColor(Color.LTGRAY); //拖拽时设置背景色为灰色
}
}
//拖拽停止时 调用
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
Log.i("zyb", "clearView");
super.clearView(recyclerView, viewHolder);
}
//当item视图变化时调用
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float
dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
viewHolder.itemView.setAlpha((float) 0.5); //可以在这里设置透明度
}
});
itemTouchHelper 关联到 RecyclerView
//itemTouchHelper 关联到 RecyclerView
itemTouchHelper.attachToRecyclerView(recyclerView);
上面代码中 CallBack 回调方法,都做了注释说明,不再赘述。
item 设置滚动监听
可以 通过 Adapter 自带的 addOnScrollListener 设置滚动监听:
adapter.addOnScrollListener(new ImageAutoLoadScrollListener());
private class ImageAutoLoadScrollListener extends OnScrollListener {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case SCROLL_STATE_IDLE: //item停止滚动时
break;
case SCROLL_STATE_DRAGGING: //item正在滚动时,手指未松开
break;
case SCROLL_STATE_SETTLING: //item根据惯性滚动时,手指已松开
break;
}
}
}
代码优化
上面的代码 Adapter、RecyclerView的初始化,ItemTouchHelper、Click的回调,都是在 Activity中,这样代码看着很乱,所以能实现一个自定义类,继承自 RecyclerView,把所有和 RecyclerView 相关的都放在自定义的类中,而初始化工作只需调用一个封装好的方法即可,下面看这个类的代码:
/**
* Created by zhangyb on 2017/6/27.
*/
public class LinearRecyclerView extends RecyclerView {
private Context myContext;
private LinearRecycleAdapter adapter;
private ItemTouchHelper itemTouchHelper;
private RecyclerView.LayoutManager layoutManager;
public LinearRecyclerView(Context context) {
super(context, null, 0);
myContext = context;
}
public void initRecyclerView(List<String> dataList) {
layoutManager = new LinearLayoutManager(myContext, VERTICAL, false);
setLayoutManager(layoutManager);
adapter = new LinearRecycleAdapter(myContext, dataList);
setAdapter(adapter);
addOnScrollListener(new ImageAutoLoadScrollListener());
addItemMoveListener();
}
private void addItemMoveListener() {
itemTouchHelper = new ItemTouchHelper(new ItemMoveListener());
itemTouchHelper.attachToRecyclerView(this);
}
private class ItemMoveListener extends ItemTouchHelper.Callback {
//用于设置拖拽和滑动的方向
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
//设置允许拖拽item的方向,线性式布局有2个方向
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
//设置侧滑方向为从两个方向都可以
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
//长摁item拖拽到和另一个item重合时,调用
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView
.ViewHolder target) {
Log.i("zyb", "onMove");
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
String strFrom = adapter.getDataList().get(from);
adapter.getDataList().remove(from);
adapter.getDataList().add(to, strFrom);
adapter.notifyItemMoved(from, to);//更新适配器中item的位置
return true;
}
//这里处理滑动删除
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
adapter.notifyItemRemoved(position);
}
//按下和松开item时 调用
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
Log.i("zyb", "onSelectedChanged");
super.onSelectedChanged(viewHolder, actionState);
if (viewHolder != null) {
viewHolder.itemView.setBackgroundColor(Color.LTGRAY); //拖拽时设置背景色为灰色
}
}
//拖拽停止时 调用
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
Log.i("zyb", "clearView");
super.clearView(recyclerView, viewHolder);
}
////当item视图变化时调用
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float
dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
viewHolder.itemView.setAlpha((float) 0.5); //可以在这里设置透明度
}
}
private class ImageAutoLoadScrollListener extends OnScrollListener {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case SCROLL_STATE_IDLE: //item停止滚动时
break;
case SCROLL_STATE_DRAGGING: //item正在滚动时,手指未松开
break;
case SCROLL_STATE_SETTLING: //item通过惯性滚动时,手指已松开
break;
}
}
}
}
在 Activity 中调用:
public class MainActivity extends AppCompatActivity {
/*优化后*/
private List<String> dataList;
private LinearRecyclerView linearRecyclerView;
/*优化前*/
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
private LinearRecycleAdapter adapter;
private ItemTouchHelper itemTouchHelper;
private List<String> dataList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*优化后*/
linearRecyclerView = (LinearRecyclerView) findViewById(R.id.main_recycle_view);
linearRecyclerView.initRecyclerView(initData());
/*优化前*/
layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
recyclerView = (RecyclerView) findViewById(R.id.main_recycle_view);
recyclerView.setLayoutManager(layoutManager);
initData();
adapter = new LinearRecycleAdapter(this, dataList);
itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
..... //省略
});
itemTouchHelper.attachToRecyclerView(recyclerView);
adapter.setOnItemClickListener(new LinearRecycleAdapter.OnItemClickListener() {
..... //省略
});
recyclerView.setAdapter(adapter);
}
private List<String> initData() {
dataList = new ArrayList<>();
for (int i = 0; i < 50; i++) {
dataList.add("item : " + i);
}
return dataList;
}
}
对比发现,优化后只需要两行代码。我们理想的状态,就是只需要一行代码,创建出这个类,就完成这个类的初始化工作,最多传入一个或者多个参数,初始化的工作尽可能的交给构造函数来完成,也就是编译器帮你做,尽量不要让用户操心 或者让用户完成一系列的初始化。但在这个例子中,DataList,也就是 和 RecyclerView 绑定的数据,需要外面来传入,所以封装了一个 public void initRecyclerView(List<String> dataList) 方法,而添加的监听功能,如果用户不想使用,只需不去处理监听的回调函数即可。