今天开始讲RecycleView的系列教程。分割线,分组,局部刷新,动态添加,缓存原理,抖音效果,瀑布流。嵌套,动画等等
支持功能
1.像qq一样,右滑删除
2.像支付宝一样,拖动排序
3.像今日头条一样,有些item不能拖动
4.可以控制拖动的方向
SnapHelper这个类:滚动辅助类。
今天和大家一起实现RecyclerView可拖拽Item,主要是使用RecyclerView结合ItemTouchHelper来实现的
知识储备:
1.Collections.swap(datas, i, i + 1);
实现步骤:
1.只需要给recyclerView添加一个ItemTouchHelper对象就行
实现侧滑删除
getMovementFlags()。确定左滑还是右滑动
onSwiped()。 数据移除和更新
实现拖动效果:
mItemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() );
mItemTouchHelper.attachToRecyclerView(mRecyclerView);
2.ItemTouchHelper的callback
首先来自定义一个CallBack类,继承与ItemTouchHepler.Callback()对象
1
2
3
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
1).首先说一下getMovementFlags(),
这个方法是设置是否滑动时间,以及拖拽的方向,所以在这里需要判断一下是列表布局还是网格布局,
如果是列表布局的话则拖拽方向为DOWN和UP,如果是网格布局的话则是DOWN和UP和LEFT和RIGHT,对应这个方法的代码如下:
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN |
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
} else {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
}
2).而onMove()方法则是我们在拖动的时候不断回调的方法,在这里我们需要将正在拖拽的item和集合的item进行交换元素,然后在通知适配器更新数据,也很简单,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//得到当拖拽的viewHolder的Position
int fromPosition = viewHolder.getAdapterPosition();
//拿到当前拖拽到的item的viewHolder
int toPosition = target.getAdapterPosition();
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(datas, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(datas, i, i - 1);
}
}
myAdapter.notifyItemMoved(fromPosition, toPosition);
return true;
}
3).onSwiped()是替换后调用的方法,可以不用管。然后我们希望在拖拽的时候将被拖拽的Item高亮,这样用户体验要好很多,所以我们要重写CallBack对象中的onSelectedChanged()和clearView()方法,在选中的时候设置高亮背景色,在完成的时候移除高亮背景色,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 长按选中Item的时候开始调用
*
* @param viewHolder
* @param actionState
*/
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
viewHolder.itemView.setBackgroundColor(Color.LTGRAY);
}
super.onSelectedChanged(viewHolder, actionState);
}
/**
* 手指松开的时候还原
* @param recyclerView
* @param viewHolder
*/
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setBackgroundColor(0);
}
这样就实现了我们的基本要求,但是实际功能中有可能存在,排头前两个的不需改变它的顺序,即有些item允许拖拽,有些则不允许,所以我们需要重写isLongPressDragEnabled()设置不允许长按拖拽
1
2
3
4
5
6
7
8
/**
* 重写拖拽不可用
* @return
*/
@Override
public boolean isLongPressDragEnabled() {
return false;
}
然后在重写RecycleView的长按监听(这个要自己写个接口去实现),在返回的长按方法中判断是否为不可拖拽的item,若不是,则调用ItemTouchHelper的startDrag()方法,逻辑出入如下:
1
2
3
4
5
6
7
8
@Override
public void onItemLongClick(RecyclerView.ViewHolder vh) {
//判断被拖拽的是否是前两个,如果不是则执行拖拽
if (vh.getLayoutPosition() != 0 && vh.getLayoutPosition() != 1) {
mItemTouchHelper.startDrag(vh);
}
}
5.重要的拖动之后,数据变了。Adapter中实现下面的接口
RecyclerView.Adapter
@OverridepublicvoidonMove(intfromPosition,inttoPosition){/**
* 在这里进行给原数组数据的移动
*/Collections.swap(mData, fromPosition, toPosition);/**
* 通知数据移动
*/notifyItemMoved(fromPosition, toPosition); }
源码:
publicclassMainActivityextendsAppCompatActivity{
privateContextmContext=MainActivity.this;
privateSwipeRecyclerViewmRecyclerView;
privateString[]titles={"美食","电影","酒店住宿","休闲娱乐","外卖","自助餐","KTV","机票/火车票","周边游","美甲美睫",
"火锅","生日蛋糕","甜品饮品","水上乐园","汽车服务","美发","丽人","景点","足疗按摩","运动健身","健身","超市","买菜",
"今日新单","小吃快餐","面膜","洗浴/汗蒸","母婴亲子","生活服务","婚纱摄影","学习培训","家装","结婚","全部分配"};
privateList
privateItemTouchHelpermItemTouchHelper;
privateMyAdaptermyAdapter;
@Override
protectedvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
}
privatevoidinitData() {
//初始化data
for(inti=0;i //动态获取资源ID,第一个参数是资源名,第二个参数是资源类型例如drawable,string等,第三个参数包名 intimageId=getResources().getIdentifier("ic_category_"+i,"mipmap",getPackageName()); datas.add(newSubject(titles[i],imageId)); } } privatevoidinitView() { mRecyclerView=(SwipeRecyclerView)findViewById(recyclerView); mRecyclerView.setLayoutManager(newLinearLayoutManager(this)); mRecyclerView.addItemDecoration(newDividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST)); // mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4)); // mRecyclerView.addItemDecoration(new DividerGridItemDecoration(this)); myAdapter=newMyAdapter(datas,mContext); mRecyclerView.setAdapter(myAdapter); mRecyclerView.addOnItemTouchListener(newOnRecyclerItemClickListener(mRecyclerView) { @Override publicvoidonItemClick(RecyclerView.ViewHoldervh) { Toast.makeText(mContext,datas.get(vh.getLayoutPosition()).getTitle(),Toast.LENGTH_SHORT).show(); } @Override publicvoidonItemLongClick(RecyclerView.ViewHoldervh) { //判断被拖拽的是否是前两个,如果不是则执行拖拽 if(vh.getLayoutPosition()!=0&&vh.getLayoutPosition()!=1) { mItemTouchHelper.startDrag(vh); //获取系统震动服务 Vibratorvib=(Vibrator)getSystemService(Service.VIBRATOR_SERVICE);//震动70毫秒 vib.vibrate(70); } } }); mItemTouchHelper=newItemTouchHelper(newItemTouchHelper.Callback() { /** * 是否处理滑动事件 以及拖拽和滑动的方向 如果是列表类型的RecyclerView的只存在UP和DOWN,如果是网格类RecyclerView则还应该多有LEFT和RIGHT * @param recyclerView * @param viewHolder * @return */ @Override publicintgetMovementFlags(RecyclerViewrecyclerView,RecyclerView.ViewHolderviewHolder) { if(recyclerView.getLayoutManager()instanceofGridLayoutManager) { finalintdragFlags=ItemTouchHelper.UP|ItemTouchHelper.DOWN| ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT; finalintswipeFlags=0; returnmakeMovementFlags(dragFlags,swipeFlags); }else{ finalintdragFlags=ItemTouchHelper.UP|ItemTouchHelper.DOWN; finalintswipeFlags=0; returnmakeMovementFlags(dragFlags,swipeFlags); } } @Override publicbooleanonMove(RecyclerViewrecyclerView,RecyclerView.ViewHolderviewHolder,RecyclerView.ViewHoldertarget) { //得到当拖拽的viewHolder的Position intfromPosition=viewHolder.getAdapterPosition(); //拿到当前拖拽到的item的viewHolder inttoPosition=target.getAdapterPosition(); if(fromPosition for(inti=fromPosition;i Collections.swap(datas,i,i+1); } }else{ for(inti=fromPosition;i>toPosition;i--) { Collections.swap(datas,i,i-1); } } myAdapter.notifyItemMoved(fromPosition,toPosition); returntrue; } @Override publicvoidonSwiped(RecyclerView.ViewHolderviewHolder,intdirection) { // int position = viewHolder.getAdapterPosition(); // myAdapter.notifyItemRemoved(position); // datas.remove(position); } /** * 重写拖拽可用 * @return */ @Override publicbooleanisLongPressDragEnabled() { returnfalse; } /** * 长按选中Item的时候开始调用 * * @param viewHolder * @param actionState */ @Override publicvoidonSelectedChanged(RecyclerView.ViewHolderviewHolder,intactionState) { if(actionState!=ItemTouchHelper.ACTION_STATE_IDLE) { viewHolder.itemView.setBackgroundColor(Color.LTGRAY); } super.onSelectedChanged(viewHolder,actionState); } /** * 手指松开的时候还原 * @param recyclerView * @param viewHolder */ @Override publicvoidclearView(RecyclerViewrecyclerView,RecyclerView.ViewHolderviewHolder) { super.clearView(recyclerView,viewHolder); viewHolder.itemView.setBackgroundColor(0); } }); mItemTouchHelper.attachToRecyclerView(mRecyclerView); mRecyclerView.setRightClickListener(newSwipeRecyclerView.OnRightClickListener() { @Override publicvoidonRightClick(intposition,Stringid) { datas.remove(position); // myAdapter.notifyItemRemoved(position); myAdapter.notifyDataSetChanged(); Toast.makeText(mContext," position = "+position,Toast.LENGTH_SHORT).show(); } }); } } demo地址:https://github.com/pengcaihua123456/shennandadao