給RecyclerView封裝個(gè)Adapter吧(更優(yōu)雅的添加點(diǎn)擊事件)
RecyclerView強(qiáng)大,好用鹃唯,但是使用率很高的ItemClickListener卻沒有添加。
- 你想要控制其顯示的方式,請(qǐng)通過布局管理器LayoutManager
- 你想要控制Item間的間隔(可繪制)滑臊,請(qǐng)通過ItemDecoration
- 你想要控制Item增刪的動(dòng)畫,請(qǐng)通過ItemAnimator
- 你想要控制點(diǎn)擊箍铲、長(zhǎng)按事件雇卷,請(qǐng)自己寫(擦,這點(diǎn)尼瑪颠猴。)
無疑讓我們?cè)谛老仓卸嗔艘唤z疑慮关划。有的哥們干脆從入門到放棄了。其實(shí)這也是這個(gè)框架的強(qiáng)大之處——定制翘瓮。
其中一個(gè)原因是RecyclerView的Item支持動(dòng)畫贮折。舉個(gè)栗子。如果你開啟了RecyclerView的Item動(dòng)畫资盅,當(dāng)你刪除了position位于1的Item之后调榄, 此時(shí)位于23456...的position會(huì)向前一格變成12345,而被刪除的那個(gè)view也會(huì)留在RecycleView上繼續(xù)執(zhí)行動(dòng)畫直到結(jié)束呵扛,于是這時(shí)候RecycleView中會(huì)留有兩個(gè)position為1的View....此時(shí)點(diǎn)擊那個(gè)被刪除的view的話就蛋疼了每庆。。因?yàn)锳dapter中已經(jīng)沒有這個(gè)item了今穿,如果用position 1去adapter中取的話缤灵,得到的是原來positon為2的那個(gè)item.....
還有就是RecyclerView負(fù)責(zé)控制/框架,LayoutManager負(fù)責(zé)計(jì)算布局,假設(shè)將ItemClickListener放到RecyclerView上凤价,如果要實(shí)現(xiàn)點(diǎn)擊事件鸽斟,首先需要確定每一個(gè)item的點(diǎn)擊區(qū)域。但是RecyclerView無法知道每一個(gè)item的點(diǎn)擊區(qū)域利诺,因?yàn)長(zhǎng)ayoutManager是可以由開發(fā)者來實(shí)現(xiàn)的富蓄,也就是說兩個(gè)View的區(qū)域是允許重疊的。如果點(diǎn)了A和B重疊區(qū)域到底是觸發(fā)A還是B慢逾,又必須要由LayoutManager來決定立倍。所以還不如直接放到LayoutManager中,但如果放到LayoutManager中的話需要給RecyclerView添加OnTouchListener侣滩,看上去又很別扭(要是外部給RecyclerView設(shè)置OnTouchListener會(huì)覆蓋掉這個(gè)導(dǎo)致ItemClickListener失效)口注。所以干脆交給開發(fā)者,你長(zhǎng)按或者點(diǎn)擊君珠,自己實(shí)現(xiàn)寝志。。策添。
點(diǎn)擊事件的實(shí)現(xiàn)材部,有常見的三種方法:
通過 RecyclerView已有的方法 addOnItemTouchListener()實(shí)現(xiàn)
在創(chuàng)建 ItemView時(shí)添加點(diǎn)擊監(jiān)聽
當(dāng) ItemViewattachRecyclerView時(shí)實(shí)現(xiàn)
從以上三種方式的實(shí)現(xiàn)過程可知:
三種均可實(shí)現(xiàn) ItemView的點(diǎn)擊事件和長(zhǎng)按事件的監(jiān)聽.
第一種方式可以很方便獲取用戶點(diǎn)擊的坐標(biāo).并且節(jié)省資源
第二種和第三種方式可以很方便對(duì) ItemView中的子 View進(jìn)行監(jiān)聽
第一種方式和第三種方式可以寫在單獨(dú)的類中,相對(duì)于第二種寫在 Adapter的方式可使代碼更獨(dú)立整潔,
既然RecyclerViewrecycle這么優(yōu)雅的解耦唯竹,我們肯定要選個(gè)解耦的方案乐导。而RecycleViewd的API也提供了RecyclerView.addOnItemTouchListener方法。so浸颓,我們選擇第一種方案:
OnItemTouchListener 源碼:
···
public static interface OnItemTouchListener {
? ? ?public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);?
? ? ?public void onTouchEvent(RecyclerView rv, MotionEvent e);?
? ? ?public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept); }
···
此接口還提供了一個(gè)實(shí)現(xiàn)類,且官方推薦使用該實(shí)現(xiàn)類 SimpleOnItemTouchListener:
/**
* An implementation of {@linkRecyclerView.OnItemTouchListener} that has empty method bodies and
* default return values.
*
* You may prefer to extend this class if you don't need to override all methods. Another
* benefit of using this class is future compatibility. As the interface may change, we'll
* always provide a default implementation on this class so that your code won't break when
* you update to a new version of the support library.
*/
public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {?
? ? @Override?
? ? public
boolean onInterceptTouchEvent(RecyclerViewrv,MotionEvente){returnfalse;}
@Override
public void onTouchEvent(RecyclerViewrv,MotionEvente){}
@Override
public void onRequestDisallowInterceptTouchEvent(booleandisallowIntercept){}}
在觸摸接口中,當(dāng)觸摸時(shí)會(huì)回調(diào)一個(gè) MotionEvent對(duì)象,看到MotionEvent然后想到了什么物臂?通過使用 GestureDetectorCompat來解析用戶的操作!GestureDetectorCompat就是處理手勢(shì)的類:手勢(shì)探測(cè)器产上,它比GestureDetector能更好兼容低版本的api棵磷,但使用方法是一致的。而GestureDetectorCompat還提供了一個(gè)外部類SimpleOnGestureListener蒂秘,這個(gè)類實(shí)現(xiàn)了上面GestureDetectorCompat接口的所有方法泽本,但全都是空實(shí)現(xiàn),這樣繼承SimpleOnGestureListener的時(shí)候就不用實(shí)現(xiàn)每一個(gè)方法了姻僧,我們需要點(diǎn)擊和長(zhǎng)按,只需要實(shí)現(xiàn):
boolean onSingleTapUp(MotionEvent e)voidonLongPress(MotionEvent e)
然后根據(jù)點(diǎn)擊的坐標(biāo)得到viewhoder:
View childView=recyclerView.findChildViewUnder(e.getX(),e.getY());if(childView!=null){RecyclerView.ViewHoldervh=recyclerView.getChildViewHolder(childView);onItemClick(vh);//點(diǎn)擊回掉}
全部代碼:
public abstract class OnRecyclerItemClickListener extends RecyclerView.SimpleOnItemTouchListener{
private GestureDetectorCompatmGestureDetector;
private RecyclerViewrecyclerView;
public OnRecyclerItemClickListener(finalRecyclerViewrecyclerView){
this.recyclerView=recyclerView;
mGestureDetector=new GestureDetectorCompat(recyclerView.getContext(),newGestureDetector.SimpleOnGestureListener(){
@Override
public boolean onSingleTapUp(MotionEvente){
View childView=recyclerView.findChildViewUnder(e.getX(),e.getY());if(childView!=null){
RecyclerView.ViewHoldervh=recyclerView.getChildViewHolder(childView);
onItemClick(vh);}
return true;}
@Override
public void onLongPress(MotionEvente){
View childView=recyclerView.findChildViewUnder(e.getX(),e.getY());
if(childView!=null){
RecyclerView.ViewHoldervh=recyclerView.getChildViewHolder(childView);onItemLongClick(vh);}}});}//點(diǎn)擊事件交給mGestureDetector處理@Override public boolean onInterceptTouchEvent(RecyclerViewrv,MotionEvente){mGestureDetector.onTouchEvent(e);returnfalse;}//點(diǎn)擊回掉public abstract void onItemClick(RecyclerView.ViewHoldervh);//長(zhǎng)按監(jiān)聽public abstract void onItemLongClick(RecyclerView.ViewHoldervh);}
使用:
recyclerView.addOnItemTouchListener(newOnRecyclerItemClickListener(recyclerView){
@Override?
public void onItemClick(RecyclerView.ViewHoldervh){
intadapterPosition=vh.getAdapterPosition();//當(dāng)前item的位置Log.i(TAG,"onItemClick: "+adapterPosition);}
@Override
public void onItemLongClick(RecyclerView.ViewHoldervh){Log.i(TAG,"onItemLongClick: ");}});