今天開始講RecycleView的系列教程。分割線,分組,局部刷新,動(dòng)態(tài)添加,緩存原理,抖音效果,瀑布流。嵌套,動(dòng)畫等等
支持功能
1.像qq一樣,右滑刪除
2.像支付寶一樣,拖動(dòng)排序
3.像今日頭條一樣,有些item不能拖動(dòng)
4.可以控制拖動(dòng)的方向
SnapHelper這個(gè)類:滾動(dòng)輔助類。
今天和大家一起實(shí)現(xiàn)RecyclerView可拖拽Item,主要是使用RecyclerView結(jié)合ItemTouchHelper來實(shí)現(xiàn)的


知識(shí)儲(chǔ)備:
1.Collections.swap(datas, i, i + 1);
實(shí)現(xiàn)步驟:
1.只需要給recyclerView添加一個(gè)ItemTouchHelper對(duì)象就行
實(shí)現(xiàn)側(cè)滑刪除
getMovementFlags()。確定左滑還是右滑動(dòng)
onSwiped()。? 數(shù)據(jù)移除和更新
實(shí)現(xiàn)拖動(dòng)效果:
mItemTouchHelper =?new?ItemTouchHelper(new?ItemTouchHelper.Callback() );
mItemTouchHelper.attachToRecyclerView(mRecyclerView);
2.ItemTouchHelper的callback
首先來自定義一個(gè)CallBack類,繼承與ItemTouchHepler.Callback()對(duì)象
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(),
這個(gè)方法是設(shè)置是否滑動(dòng)時(shí)間,以及拖拽的方向,所以在這里需要判斷一下是列表布局還是網(wǎng)格布局,
如果是列表布局的話則拖拽方向?yàn)镈OWN和UP,如果是網(wǎng)格布局的話則是DOWN和UP和LEFT和RIGHT,對(duì)應(yīng)這個(gè)方法的代碼如下:

@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()方法則是我們?cè)谕蟿?dòng)的時(shí)候不斷回調(diào)的方法,在這里我們需要將正在拖拽的item和集合的item進(jìn)行交換元素,然后在通知適配器更新數(shù)據(jù),也很簡(jiǎn)單,代碼如下:
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) {
????//得到當(dāng)拖拽的viewHolder的Position
????int?fromPosition = viewHolder.getAdapterPosition();
????//拿到當(dāng)前拖拽到的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()是替換后調(diào)用的方法,可以不用管。然后我們希望在拖拽的時(shí)候?qū)⒈煌献У腎tem高亮,這樣用戶體驗(yàn)要好很多,所以我們要重寫CallBack對(duì)象中的onSelectedChanged()和clearView()方法,在選中的時(shí)候設(shè)置高亮背景色,在完成的時(shí)候移除高亮背景色,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
?* 長(zhǎng)按選中Item的時(shí)候開始調(diào)用
?*
?* @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);
}
/**
?* 手指松開的時(shí)候還原
?* @param recyclerView
?* @param viewHolder
?*/
@Override
public?void?clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
????super.clearView(recyclerView, viewHolder);
????viewHolder.itemView.setBackgroundColor(0);
}
這樣就實(shí)現(xiàn)了我們的基本要求,但是實(shí)際功能中有可能存在,排頭前兩個(gè)的不需改變它的順序,即有些item允許拖拽,有些則不允許,所以我們需要重寫isLongPressDragEnabled()設(shè)置不允許長(zhǎng)按拖拽
1
2
3
4
5
6
7
8
/**
?* 重寫拖拽不可用
?* @return
?*/
@Override
public?boolean?isLongPressDragEnabled() {
????return?false;
}
?然后在重寫RecycleView的長(zhǎng)按監(jiān)聽(這個(gè)要自己寫個(gè)接口去實(shí)現(xiàn)),在返回的長(zhǎng)按方法中判斷是否為不可拖拽的item,若不是,則調(diào)用ItemTouchHelper的startDrag()方法,邏輯出入如下:
1
2
3
4
5
6
7
8
@Override
?public?void?onItemLongClick(RecyclerView.ViewHolder vh) {
?????//判斷被拖拽的是否是前兩個(gè),如果不是則執(zhí)行拖拽
?????if?(vh.getLayoutPosition() !=?0?&& vh.getLayoutPosition() !=?1) {
?????????mItemTouchHelper.startDrag(vh);
?????}
?}
5.重要的拖動(dòng)之后,數(shù)據(jù)變了。Adapter中實(shí)現(xiàn)下面的接口
RecyclerView.Adapter
@OverridepublicvoidonMove(intfromPosition,inttoPosition){/**
? ? ? ? * 在這里進(jìn)行給原數(shù)組數(shù)據(jù)的移動(dòng)
? ? ? ? */Collections.swap(mData, fromPosition, toPosition);/**
? ? ? ? * 通知數(shù)據(jù)移動(dòng)
? ? ? ? */notifyItemMoved(fromPosition, toPosition);? ? }
源碼:
publicclassMainActivityextendsAppCompatActivity{
privateContextmContext=MainActivity.this;
privateSwipeRecyclerViewmRecyclerView;
privateString[]titles={"美食","電影","酒店住宿","休閑娛樂","外賣","自助餐","KTV","機(jī)票/火車票","周邊游","美甲美睫",
"火鍋","生日蛋糕","甜品飲品","水上樂園","汽車服務(wù)","美發(fā)","麗人","景點(diǎn)","足療按摩","運(yùn)動(dòng)健身","健身","超市","買菜",
"今日新單","小吃快餐","面膜","洗浴/汗蒸","母嬰親子","生活服務(wù)","婚紗攝影","學(xué)習(xí)培訓(xùn)","家裝","結(jié)婚","全部分配"};
privateList<Subject>datas=newArrayList<>();
privateItemTouchHelpermItemTouchHelper;
privateMyAdaptermyAdapter;
@Override
protectedvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
? ? }
privatevoidinitData() {
//初始化data
for(inti=0;i<titles.length;i++) {
//動(dòng)態(tài)獲取資源ID,第一個(gè)參數(shù)是資源名,第二個(gè)參數(shù)是資源類型例如drawable,string等,第三個(gè)參數(shù)包名
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) {
//判斷被拖拽的是否是前兩個(gè),如果不是則執(zhí)行拖拽
if(vh.getLayoutPosition()!=0&&vh.getLayoutPosition()!=1) {
mItemTouchHelper.startDrag(vh);
//獲取系統(tǒng)震動(dòng)服務(wù)
Vibratorvib=(Vibrator)getSystemService(Service.VIBRATOR_SERVICE);//震動(dòng)70毫秒
vib.vibrate(70);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? });
mItemTouchHelper=newItemTouchHelper(newItemTouchHelper.Callback() {
/**
? ? ? ? ? ? * 是否處理滑動(dòng)事件 以及拖拽和滑動(dòng)的方向 如果是列表類型的RecyclerView的只存在UP和DOWN,如果是網(wǎng)格類RecyclerView則還應(yīng)該多有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) {
//得到當(dāng)拖拽的viewHolder的Position
intfromPosition=viewHolder.getAdapterPosition();
//拿到當(dāng)前拖拽到的item的viewHolder
inttoPosition=target.getAdapterPosition();
if(fromPosition<toPosition) {
for(inti=fromPosition;i<toPosition;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;
? ? ? ? ? ? }
/**
? ? ? ? ? ? * 長(zhǎng)按選中Item的時(shí)候開始調(diào)用
? ? ? ? ? ? *
? ? ? ? ? ? * @param viewHolder
? ? ? ? ? ? * @param actionState
? ? ? ? ? ? */
@Override
publicvoidonSelectedChanged(RecyclerView.ViewHolderviewHolder,intactionState) {
if(actionState!=ItemTouchHelper.ACTION_STATE_IDLE) {
viewHolder.itemView.setBackgroundColor(Color.LTGRAY);
? ? ? ? ? ? ? ? }
super.onSelectedChanged(viewHolder,actionState);
? ? ? ? ? ? }
/**
? ? ? ? ? ? * 手指松開的時(shí)候還原
? ? ? ? ? ? * @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();
? ? ? ? ? ? }
? ? ? ? });
? ? }
}