前三篇文章
android v7兼容包RecyclerView的使用(三)——布局管理器的使用
android v7兼容包RecyclerView的使用(二)
android v7兼容包RecyclerView的使用(一)
介绍了RecyclerView的使用以及常见的相关类和布局管理器的灵活之处。写了这么多篇,还没涉及到用户交互,那么怎么处理点击事件呢。
在RecyclerView中你会惊奇的发现,该类中并没有OnItemClickListener监听器监听我们的单击事件,也没有OnItemLongClickListener监听器监听我们的长按事件。取而代之的是OnItemTouchListener监听器,那么该如何实现我们的点击事件和长按事件呢。
我们的代码是基于上篇文章布局管理器的代码,在其基础上添加事件监听。
如果让我们自己来事件点击事件,我们比如会使用ViewHolder来间接处理事件。首先在适配器中增加监听器接口。
interface OnItemClickListener { void onClick(View v); } interface OnItemLongClickListener { void onLongClick(View v); } private OnItemClickListener onClickListener; private OnItemLongClickListener onLongClickListener;
然后重载构造函数,使其能够接收监听器实例
public CardViewAdapter(String[] data) { this(data, null, null); } public CardViewAdapter(String[] data, OnItemClickListener onClickListener) { this(data, onClickListener, null); } public CardViewAdapter(String[] data, OnItemClickListener onClickListener, OnItemLongClickListener onLongClickListener) { this.data = data; this.onClickListener = onClickListener; this.onLongClickListener = onLongClickListener; }
最终会调用三参数的构造器,在该构造器内完成赋值。
将原来的ViewHolder构造函数进行改造,使其处理点击事件,当然也可以直接在onCreateViewHolder函数里处理点击事件。
public ViewHolder(View itemLayoutView, final OnItemClickListener onClickListener, final OnItemLongClickListener onLongClickListener) { super(itemLayoutView); info = (TextView) itemLayoutView.findViewById(R.id.info_text); itemLayoutView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //在监听器不为空的时候,进行回调 if (onClickListener != null) { onClickListener.onClick(v); } } }); itemLayoutView.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { //在监听器不为空的时候,进行回调 if (onLongClickListener != null) { onLongClickListener.onLongClick(v); } //返回true,消费掉该事件,阻止其继续传递 return true; } }); }
仔细一看,其实代码还是挺多的,那么让我们调用一下。
mAdapter = new CardViewAdapter(data,new OnItemClickListener() { @Override public void onClick(View v) { TextView info = (TextView) v.findViewById(R.id.info_text); Toast.makeText(getApplicationContext(), "单击"+info.getText(), Toast.LENGTH_LONG).show(); } },new OnItemLongClickListener() { @Override public void onLongClick(View v) { TextView info = (TextView) v.findViewById(R.id.info_text); Toast.makeText(getApplicationContext(), "长按"+info.getText(), Toast.LENGTH_LONG).show(); } });
后面两个参数可以传空值,代表不设置监听器。
运行效果图如下。
我们会发现,上面的代码耦合性还是有点高,事件直接与适配器发生了耦合,除此之外,我们还应该有更好的方法来处理这个点击事件。是的,不是有OnItemTouchListener监听器吗,再配合手势不就可以吗。
好了,看代码吧,详细解释在注释中。
package cn.edu.zafu.layoutmanager;import android.content.Context;import android.support.v7.widget.RecyclerView;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;/** * 监听器,实现OnItemTouchListener接口 * * @author lizhangqu * * 2015-3-12 */public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener { private OnItemClickListener mListener; private GestureDetector mGestureDetector; // 点击回调 public interface OnItemClickListener { public void onItemClick(View view, int position); public void onItemLongClick(View view, int position); } public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) { mListener = listener; // 识别并处理手势 mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { // 轻击触摸屏后,弹起,必须返回true,否则无法触发单击 return true; } @Override public void onLongPress(MotionEvent e) { // 长按 // 根据findChildViewUnder(float x, float y)来算出哪个item被选择了 View childView = recyclerView.findChildViewUnder( e.getX(), e.getY()); // 有item被选则且监听器不为空触发长按事件 if (childView != null && mListener != null) { mListener.onItemLongClick(childView, recyclerView.getChildPosition(childView)); } } }); } @Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { View childView = view.findChildViewUnder(e.getX(), e.getY()); if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) { // 触发单击事件 mListener.onItemClick(childView, view.getChildPosition(childView)); return true; } return false; } @Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }}
在activity中调用
mRecyclerView.addOnItemTouchListener(new RecyclerItemClickListener(getApplicationContext(), mRecyclerView, new OnItemClickListener() { @Override public void onItemLongClick(View view, int position) { Toast.makeText(getApplicationContext(), "长按"+data[position], Toast.LENGTH_SHORT).show(); } @Override public void onItemClick(View view, int position) { Toast.makeText(getApplicationContext(), "短按"+data[position], Toast.LENGTH_SHORT).show(); } }));
很明显,第二种方式与适配器进行了解耦。应该说优于第一种方法。
那么还有没有方法处理点击事件呢。让我们从万能的github上搜索一下。
https://github.com/lucasr/twoway-view
在该项目的sample目录下有个类里面有这样一段代码
final ItemClickSupport itemClick = ItemClickSupport.addTo(mRecyclerView); itemClick.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(RecyclerView parent, View child, int position, long id) { mToast.setText("Item clicked: " + position); mToast.show(); } }); itemClick.setOnItemLongClickListener(new OnItemLongClickListener() { @Override public boolean onItemLongClick(RecyclerView parent, View child, int position, long id) { mToast.setText("Item long pressed: " + position); mToast.show(); return true; } });
上述代码有个ItemClickSupport 类,对的,该类就是提供事件的支持。那么该类在哪里呢,其实它在该项目的core目录下。我直接将其该目录下的代码拷至我的项目的包中,删除一个不相关的类,就直接使用了,当然还需要拷一个资源文件就是ids.xml
实际运行效果呢,是跟上面两种方式是一样的,那么它的实现由什么区别呢,其实与第二种方式没什么大的区别,基本上就是OnItemTouchListener 加手势实现的,只不过其逻辑可能更加严谨,设计更加优秀罢了。除此之外,该目录下还有一个ItemSelectionSupport类,该类提供了item选择的功能,提供了单选多选方式,然而呢,在我测试时发现存在bug,所以呢,这个类的使用就跳过了。
至此,RecyclerView的点击事件就处理完了。
源码下载
http://download.csdn.net/detail/sbsujjbcy/8495337