Android RecyclerView的Item点击事件实现整理
自从开始使用recyclerview代替listview,会发现有很多地方需要学习。前一段时间的学习记录有:
recyclerview的滚动事件研究 - devwiki
recyclerview的viewholder和adapter的封装优化 - devwiki
recyclerview问题记录 - devwiki
实现 recyclerview的item的点击事件有三种方式:
- 在创建 itemview时添加点击监听
- 当 itemview attach recyclerview时实现
- 通过recyclerview已有的方法addonitemtouchlistener()实现
1.在创建itemview时添加点击监听
思路是:因为viewholder我们可以拿到每个item的根布局,所以如果我们为根布局设置单独的onclick监听并将其开放给adapter,那不就可以在组装recyclerview时就能够设置itemclicklistener,只不过这个listener不是设置到recyclerview上而是设置到adapter。具体实现代码如下:
public class sampleadapter extends recyclerview.adapter<sampleadapter.sampleviewholder> { private list<databean> mdatas; private onitemclicklistener mlistener; // item点击事件 public databean getitem(int position) { return mdatas == null ? null : mdatas.get(position); } @override public sampleviewholder oncreateviewholder(viewgroup parent, int viewtype) { view itemview = layoutinflater.from(parent.getcontext()).inflate(r.layout.item, parent,false); return new sampleviewholder(itemview); } @override public void onbindviewholder(sampleviewholder holder, int position) { } @override public int getitemcount() { return mdatas == null ? 0 : mdatas.size(); } class sampleviewholder extends recyclerview.viewholder implements view.onclicklistener, view.onlongclicklistener { public sampleviewholder(view itemview) { super(itemview); // todo:初始化view ... itemview.setonclicklistener(this); itemview.setonlongclicklistener(this); } @override public void onclick(view v) { if (mlistener != null) { mlistener.onitemclick(sampleadapter.this, v, getlayoutposition()); } } @override public boolean onlongclick(view v) { if (mlistener != null) { mlistener.onitemlongclick(sampleadapter.this, v, getlayoutposition()); return true; } return false; } } }
2.当itemview attach recyclerview时实现
该实现方法是在阅读国外的一篇博客时发现的,原文链接如下:getting your clicks on recyclerview
实现的代码如下:
public class itemclicksupport { private static final int key = 0x99999999; private final recyclerview mrecyclerview; private onitemclicklistener monitemclicklistener; private onitemlongclicklistener monitemlongclicklistener; private view.onclicklistener monclicklistener = new view.onclicklistener() { @override public void onclick(view v) { if (monitemclicklistener != null) { recyclerview.viewholder holder = mrecyclerview.getchildviewholder(v); monitemclicklistener.onitemclicked(mrecyclerview, v, holder.getadapterposition()); } } }; private view.onlongclicklistener monlongclicklistener = new view.onlongclicklistener() { @override public boolean onlongclick(view v) { if (monitemlongclicklistener != null) { recyclerview.viewholder holder = mrecyclerview.getchildviewholder(v); return monitemlongclicklistener.onitemlongclicked(mrecyclerview, v, holder.getadapterposition()); } return false; } }; private recyclerview.onchildattachstatechangelistener mattachlistener = new recyclerview.onchildattachstatechangelistener() { @override public void onchildviewattachedtowindow(view view) { if (monitemclicklistener != null) { view.setonclicklistener(monclicklistener); } if (monitemlongclicklistener != null) { view.setonlongclicklistener(monlongclicklistener); } } @override public void onchildviewdetachedfromwindow(view view) { } }; /** * itemclicksupport的私有构造方法 */ private itemclicksupport(recyclerview recyclerview) { mrecyclerview = recyclerview; mrecyclerview.settag(key, this); // 为recyclerview设置onchildattachstatechangelistener事件监听 mrecyclerview.addonchildattachstatechangelistener(mattachlistener); } /** * 为recyclerview设置itemclicksupport */ public static itemclicksupport addto(recyclerview view) { itemclicksupport support = (itemclicksupport) view.gettag(key); if (support == null) { support = new itemclicksupport(view); } return support; } /** * 为recyclerview移除itemclicksupport */ public static itemclicksupport removefrom(recyclerview view) { itemclicksupport support = (itemclicksupport) view.gettag(key); if (support != null) { support.detach(view); } return support; } /** * 为recyclerview设置点击事件监听 */ public itemclicksupport setonitemclicklistener(onitemclicklistener listener) { monitemclicklistener = listener; return this; } /** * 为recyclerview设置长按事件监听 */ public itemclicksupport setonitemlongclicklistener(onitemlongclicklistener listener) { monitemlongclicklistener = listener; return this; } /** * 为recyclerview移除onchildattachstatechangelistener事件监听 */ private void detach(recyclerview view) { view.removeonchildattachstatechangelistener(mattachlistener); view.settag(key, null); } /** * recyclerview的点击事件监听接口 */ public interface onitemclicklistener { void onitemclicked(recyclerview recyclerview, view itemview, int position); } /** * recyclerview的长按事件监听接口 */ public interface onitemlongclicklistener { boolean onitemlongclicked(recyclerview recyclerview, view itemview, int position); } }
上面的代码中给recyclerview设置了onchildattachstatechangelistener事件监听,当子view attach recyclerview时设置事件监听。
private recyclerview.onchildattachstatechangelistener mattachlistener = new recyclerview.onchildattachstatechangelistener() { @override public void onchildviewattachedtowindow(view view) { if (monitemclicklistener != null) { view.setonclicklistener(monclicklistener); } if (monitemlongclicklistener != null) { view.setonlongclicklistener(monlongclicklistener); } } @override public void onchildviewdetachedfromwindow(view view) {} };
使用时只需要调用addto(recycleview view)方法得到itemclicksupport对象,然后调用setonitemclicklistener()方法和setonitemlongclicklistener()方法设置itemview的点击事件和长按事件监听即可。
3.通过recyclerview已有的方法addonitemtouchlistener()实现
3.1、查看源码
查看recyclerview源码可以看到,recyclerview预留了一个item的触摸事件方法:
/** * add an {@link onitemtouchlistener} to intercept touch events before they are dispatched * to child views or this view's standard scrolling behavior. * * <p>client code may use listeners to implement item manipulation behavior. once a listener * returns true from * {@link onitemtouchlistener#onintercepttouchevent(recyclerview, motionevent)} its * {@link onitemtouchlistener#ontouchevent(recyclerview, motionevent)} method will be called * for each incoming motionevent until the end of the gesture.</p> * * @param listener listener to add * @see simpleonitemtouchlistener */ public void addonitemtouchlistener(onitemtouchlistener listener) { monitemtouchlisteners.add(listener); }
通过注释我们可知,此方法是在滚动事件之前调用,需要传入一个onitemtouchlistener对象。onitemtouchlistener的代码如下:
public static interface onitemtouchlistener { public boolean onintercepttouchevent(recyclerview rv, motionevent e); public void ontouchevent(recyclerview rv, motionevent e); public void onrequestdisallowintercepttouchevent(boolean disallowintercept); }
此接口还提供了一个实现类,且官方推荐使用该实现类simpleonitemtouchlistener:
/** * an implementation of {@link recyclerview.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 { <span style="font-family:'microsoft yahei';"> </span> @override public boolean onintercepttouchevent(recyclerview rv, motionevent e) { return false; } @override public void ontouchevent(recyclerview rv, motionevent e) { } @override public void onrequestdisallowintercepttouchevent(boolean disallowintercept) { } }
在触摸接口中,当触摸时会回调一个motionevent对象,通过使用gesturedetectorcompat来解析用户的操作。
3.2、了解gesturedetector的工作原理
对于触摸屏,其原生的消息无非按下、抬起、移动这几种,我们只需要简单重载ontouch或者设置触摸侦听器setontouchlistener即可进行处理。不过,为了提高我们的app的用户体验,有时候我们需要识别用户的手势,android给我们提供的手势识别工具gesturedetector就可以帮上大忙了。
gesturedetector的工作原理是,当我们接收到用户触摸消息时,将这个消息交给gesturedetector去加工,我们通过设置侦听器获得gesturedetector处理后的手势。
gesturedetector提供了两个侦听器接口,ongesturelistener处理单击类消息,ondoubletaplistener处理双击类消息。
ongesturelistener的接口有这几个:
// 单击,触摸屏按下时立刻触发 abstract boolean ondown(motionevent e); // 抬起,手指离开触摸屏时触发(长按、滚动、滑动时,不会触发这个手势) abstract boolean onsingletapup(motionevent e); // 短按,触摸屏按下后片刻后抬起,会触发这个手势,如果迅速抬起则不会 abstract void onshowpress(motionevent e); // 长按,触摸屏按下后既不抬起也不移动,过一段时间后触发 abstract void onlongpress(motionevent e); // 滚动,触摸屏按下后移动 abstract boolean onscroll(motionevent e1, motionevent e2, float distancex, float distancey); // 滑动,触摸屏按下后快速移动并抬起,会先触发滚动手势,跟着触发一个滑动手势 abstract boolean onfling(motionevent e1, motionevent e2, float velocityx, float velocityy); ondoubletaplistener的接口有这几个:
// 双击,手指在触摸屏上迅速点击第二下时触发 abstract boolean ondoubletap(motionevent e); // 双击的按下跟抬起各触发一次 abstract boolean ondoubletapevent(motionevent e); // 单击确认,即很快的按下并抬起,但并不连续点击第二下 abstract boolean onsingletapconfirmed(motionevent e);
有时候我们并不需要处理上面所有手势,方便起见,android提供了另外一个类simpleongesturelistener实现了如上接口,我们只需要继承simpleongesturelistener然后重载需要的手势即可。
3.3、实现点击事件监听
了解了gesturedetector的工作原理之后,便开始实现recycleview的item的点击事件。首先写一个simplerecycleviewitemclicklistener类继承simpleonitemtouchlistener,构造时传入item点击回调onitemclicklistener,并覆写父类的boolean onintercepttouchevent(recyclerview rv, motionevent e)方法,具体代码如下:
/** * recyclerview的item点击事件监听 * * @author liyunlong * @date 2016/11/21 9:42 */ public class simplerecycleviewitemclicklistener extends recyclerview.simpleonitemtouchlistener { private onitemclicklistener mlistener; private gesturedetectorcompat mgesturedetector; public simplerecycleviewitemclicklistener(onitemclicklistener listener) { this.mlistener = listener; } @override public boolean onintercepttouchevent(recyclerview rv, motionevent e) { if (mgesturedetector == null) { initgesturedetector(rv); } if (mgesturedetector.ontouchevent(e)) { // 把事件交给gesturedetector处理 return true; } else { return false; } } /** * 初始化gesturedetector */ private void initgesturedetector(final recyclerview recyclerview) { mgesturedetector = new gesturedetectorcompat(recyclerview.getcontext(), new gesturedetector.simpleongesturelistener() { // 这里选择simpleongesturelistener实现类,可以根据需要选择重写的方法 /** * 单击事件 */ @override public boolean onsingletapup(motionevent e) { view childview = recyclerview.findchildviewunder(e.getx(), e.gety()); if (childview != null && mlistener != null) { mlistener.onitemclick(childview, recyclerview.getchildlayoutposition(childview)); return true; } return false; } /** * 长按事件 */ @override public void onlongpress(motionevent e) { view childview = recyclerview.findchildviewunder(e.getx(), e.gety()); if (childview != null && mlistener != null) { mlistener.onitemlongclick(childview, recyclerview.getchildlayoutposition(childview)); } } /** * 双击事件 */ @override public boolean ondoubletapevent(motionevent e) { int action = e.getaction(); if (action == motionevent.action_up) { view childview = recyclerview.findchildviewunder(e.getx(), e.gety()); if (childview != null && mlistener != null) { mlistener.onitemdoubleclick(childview, recyclerview.getchildlayoutposition(childview)); return true; } } return false; } }); } /** * recyclerview的item点击事件监听接口 * * @author liyunlong * @date 2016/11/21 9:43 */ public interface onitemclicklistener { /** * 当itemview的单击事件触发时调用 */ void onitemclick(view view, int position); /** * 当itemview的长按事件触发时调用 */ void onitemlongclick(view view, int position); /** * 当itemview的双击事件触发时调用 */ void onitemdoubleclick(view view, int position); } /** * recyclerview的item点击事件监听实现 * * @author liyunlong * @date 2016/11/21 10:05 */ public class simpleonitemclicklistener implements onitemclicklistener { @override public void onitemclick(view view, int position) { } @override public void onitemlongclick(view view, int position) { } @override public void onitemdoubleclick(view view, int position) { } } }
在gesturedetectorcompat的手势回调中我们覆写:
- boolean onsingletapup(motionevent e):单击事件回调
- void onlongpress(motionevent e):长按事件回调
- boolean ondoubletapevent(motionevent e):双击事件回调
如果我们只需要监听单击事件,而不需要监听长按事件和双击事件,构造simplerecycleviewitemclicklistener时只需要传入simpleonitemclicklistener即可,如果需要处理其它的手势监听,也可以覆写对应的手势回调方法。
4.三种方法对比
以上三种方式分别是:
- 在创建itemview时添加点击监听
- 当itemview attach recyclerview时实现
- 通过recyclerview已有的方法addonitemtouchlistener()实现
从以上三种方式的实现过程可知:
三种均可实现itemview的点击事件和长按事件的监听。
第一种和第二种方式可以很方便对itemview中的子view进行监听。
第三种方式可以很方便获取用户点击的坐标。
第二种方式和第三种方式可以写在单独的类中,相对于第一种写在adapter的方式可使代码更独立整洁。
综上所述:
如果你只想监听itemview的点击事件或长按事件,三种方式均可。
如果你想监听itemview中每个子view的点击事件,采用第一种或者第二种比较方便。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
推荐阅读
-
Android RecyclerView的Item点击事件实现整理
-
Android使用CardView作为RecyclerView的Item并实现拖拽和左滑删除
-
Android 自定义view模板并实现点击事件的回调
-
Android RecyclerView选择多个item的实现代码
-
Android中RecyclerView实现Item添加和删除的代码示例
-
Android RecyclerView选择多个item的实现代码
-
Android中RecyclerView实现Item添加和删除的代码示例
-
Android 中RecycleView实现item的点击事件
-
用Kotlin实现Android点击事件的方法
-
recyclerview item点击事件无效(recyclerview实现双击)