服务器之家

服务器之家 > 正文

Android中解决RecyclerView各种点击事件的方法

时间:2022-02-23 15:14     来源/作者:小小程序员Eric

完美解决RecyclerView点击事件、长按事件、子项点击事件

自从Google推出了RecyclerView之后,便可以完全取代ListView,个人感觉唯一的美中不足是对于itemView的各种点击事件不够完美。观点只代表个人看法。应最近项目需求实现itemView的子项点击事件,便写篇博客记录一下,若是能够帮到你,我深感荣幸。接下来,便对RecyclerView进行简单的封装,使得它更方便实现各种点击事件。

我们都知道,对与RecyclerView的使用,是创建一个adapter类,然后在adapter类中再创建一个ViewHolder的内部类。我们要做的,正是对这两个类进行封装,让其实现itemView点击事件、长按事件、子项点击事件。

首先,我的处理方式是,对于开发者来说,只需要对adapter进行setxxx()方法的调用,例如设置itemView的点击事件:adapter.setOnRecyclerViewItemClickListener(...);对该方法传入自定义的接口即可。也就是说,我们需要自定义一个adapter类。那我们就先创建一个类,命名为BaseRecylerAdapter,此后,我们也应当创建一个BaseViewHolder类,接下来开始搞事情。

BaseRecylerAdapter类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public abstract class BaseRecyclerAdapter extends RecyclerView.Adapter<BaseViewHolder>
  implements View.OnClickListener
  ,View.OnLongClickListener {
 
 private OnRecyclerViewItemClickListener onRecyclerViewItemClickListener;
 private OnRecyclerViewItemLongClickListener onRecyclerViewItemLongClickListener;
 private OnSubViewClickListener onSubViewClickListener;
 
 @Override
 public void onBindViewHolder(BaseViewHolder holder, int position) {
  holder.itemView.setTag(position);
  holder.onBind(position);
  if (onRecyclerViewItemClickListener != null) {
   holder.itemView.setOnClickListener(this);
  }
  if (onRecyclerViewItemLongClickListener != null) {
   holder.itemView.setOnClickListener(this);
  }
  if (onSubViewClickListener != null) {
   holder.setSubViewClickListener(onSubViewClickListener,position);
  }
 }
 
 public void setOnRecyclerViewItemClickListener(OnRecyclerViewItemClickListener onRecyclerViewItemClickListener) {
  this.onRecyclerViewItemClickListener = onRecyclerViewItemClickListener;
 }
 
 public void setOnRecyclerViewItemLongClickListener(OnRecyclerViewItemLongClickListener onRecyclerViewItemLongClickListener) {
  this.onRecyclerViewItemLongClickListener = onRecyclerViewItemLongClickListener;
 }
 
 public void setOnSubViewClickListener(OnSubViewClickListener listener){
  this.onSubViewClickListener = listener;
 }
 
 @Override
 public void onClick(View v) {
  if (v.getTag() != null) {
   int position = (int) v.getTag();
   onRecyclerViewItemClickListener.onItemClick(position);
  }
 }
 
 @Override
 public boolean onLongClick(View v) {
  if (v.getTag() != null){
   int position = (int)v.getTag();
   onRecyclerViewItemLongClickListener.onItemLongClick(position);
  }
  return true;
 }
 
 public interface OnRecyclerViewItemClickListener {
  void onItemClick(int position);
 }
 
 public interface OnSubViewClickListener{
  void onSubViewClick(View v, int position);
 }
 
 public interface OnRecyclerViewItemLongClickListener {
  void onItemLongClick(int position);
 }
 
}

可以看到我们在类中创建了三个接口类

?
1
2
3
4
5
6
7
8
9
10
11
public interface OnRecyclerViewItemClickListener {
  void onItemClick(int position);
 }
 
 public interface OnSubViewClickListener{
  void onSubViewClick(View v, int position);
 }
 
 public interface OnRecyclerViewItemLongClickListener {
  void onItemLongClick(int position);
 }

这三个接口便是用于点击事件的回调,看名字就能分别出各自的功能。itemView的点击回调public interface OnRecyclerViewItemClickListener,itemView的长按public interface OnRecyclerViewItemLongClickListener,子项View的点击回调public interface OnSubViewClickListener。都是点击事件的处理,没有点击发送怎么行呢,对吧!所以,这个类还实现了View.OnClickListener View.OnLongClickListener 这两个接口,本别实现itemView的点击事件和长按事件。

可以看到,BaseRecyclerAdapter继承自RecyclerView.Adapter<BaseViewHolder>,此时我们只需要实现onBindViewHolder 这个方法即可。来分析这个方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
 public void onBindViewHolder(BaseViewHolder holder, int position) {
  holder.itemView.setTag(position);
  holder.onBind(position);
  if (onRecyclerViewItemClickListener != null) {
   holder.itemView.setOnClickListener(this);
  }
  if (onRecyclerViewItemLongClickListener != null) {
   holder.itemView.setOnClickListener(this);
  }
  if (onSubViewClickListener != null) {
   holder.setSubViewClickListener(onSubViewClickListener,position);
  }
 }

可以看出 这个方法里都是操作我们自定义的BaseViewHolder类。接下来就是三个空判断,也就是说,我们若是没有设置相应的点击事件,就不会初始化对应的点击事件,这样的处理方式还是很常见的。处理这个点击事件最麻烦的就是position的问题,因此我们使用的技巧是,对View对象设置tag的方式。查看源码便知道,View有个方法 setTag(Object obj); 我们就可以将对应的position赋值给这个tag,我们使用View的getTag() 方法就可以得到对应点击View的position了。在BaseRecylerAdapter类实现的点击接口和长按接口就可以知道这样的操作,类容如下。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public void onClick(View v) {
 if (v.getTag() != null) {
  int position = (int) v.getTag();
  onRecyclerViewItemClickListener.onItemClick(position);
 }
}
 
@Override
public boolean onLongClick(View v) {
 if (v.getTag() != null){
  int position = (int)v.getTag();
  onRecyclerViewItemLongClickListener.onItemLongClick(position);
 }
 return true;
}

BaseViewHolder类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public abstract class BaseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
 private BaseRecyclerAdapter.OnSubViewClickListener onSubViewClickListener;
 public BaseViewHolder(View itemView) {
  super(itemView);
  findViewById(itemView);
 }
 
 /**
  * 传入子项点击事件所需参数
  * @param listener 自定义的接口
  * @param tagPosition tag
  */
 public void setSubViewClickListener(BaseRecyclerAdapter.OnSubViewClickListener listener, int tagPosition){
  this.onSubViewClickListener = listener;
  initSubViewClick(tagPosition);
 }
 
 /**
  * 通过id匹配控件(开发者自行实现)
  * @param itemView 父布局
  */
 abstract protected void findViewById(View itemView);
 
 /**
  * 用于装载数据(开发者自行实现)
  * @param position 当前位置
  */
 abstract protected void onBind(int position);
 
 /**
  * 初始化子项的点击事件(为子项设置tag)
  * @param tagPosition tag
  */
 protected void initSubViewClick(int tagPosition){
 
 }
 
 /**
  * 实现子项点击事件的转发
  * @param v
  */
 @Override
 public void onClick(View v) {
  if (v.getTag() != null) {
   int position = (int) v.getTag();
   onSubViewClickListener.onSubViewClick(v,position);
  }
 }
}

这是个抽象类,也就是说,在使用的时候需要实现其中的抽象方法。为了逻辑清晰,我在这里写了两个抽象方法

?
1
2
3
4
5
6
7
8
9
10
11
/**
  * 通过id匹配控件(开发者自行实现)
  * @param itemView 父布局
  */
 abstract protected void findViewById(View itemView);
 
 /**
  * 用于装载数据(开发者自行实现)
  * @param position 当前位置
  */
 abstract protected void onBind(int position);

看注释也就很清楚这两个方法的作用是什么,这里就不多说了。

到此,我们已经实现了itemView的点击和长按事件,接下来我们来实现对itemView子项的点击事件。

在BaseViewHolder类中,也实现了一个View的点击事件接口。子项的点击方式和itemView的点击事件是一样的套路,使用tag。接下来我们来看个例子,就明白了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class RecyclerAdapterMyActivity extends BaseRecyclerAdapter{
 private List<MyActivityBean> list;
 public RecyclerAdapterMyActivity(List<MyActivityBean>list){
  this.list = list;
 }
 
 
 @Override
 public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler_myactivity_activity,parent,false);
  ViewHolder holder = new ViewHolder(view);
  return holder;
 }
 
 
 @Override
 public int getItemCount() {
  return list.size();
 }
 
 public class ViewHolder extends BaseViewHolder {
  private TextView tv_name,tv_title,tv_content;
  private Button activityBtnChat;
  private Button activityBtnCancel;
  ViewHolder(View itemView) {
   super(itemView);
  }
 
  @Override
  protected void findViewById(View itemView) {
   tv_name = itemView.findViewById(R.id.tv_my_activity_name);
   tv_title = itemView.findViewById(R.id.tv_my_activity_title);
   tv_content = itemView.findViewById(R.id.tv_my_activity_content);
   activityBtnChat = itemView.findViewById(R.id.activity_btn_chat);
   activityBtnCancel = itemView.findViewById(R.id.activity_btn_cancel);
 
  }
 
  @Override
  protected void onBind(int position) {
   MyActivityBean bean = list.get(position);
   tv_name.setText(bean.getName());
   tv_title.setText(bean.getTitle());
   tv_content.setText(bean.getContent());
  }
 
  @Override
  protected void initSubViewClick(int tagPosition) {
   activityBtnChat.setTag(tagPosition);
   activityBtnCancel.setTag(tagPosition);
   activityBtnChat.setOnClickListener(this);
   activityBtnCancel.setOnClickListener(this);
  }
 }
}

这段代码是最近项目中的一小段代码。其中,adapter类继承BaseRecyclerAdapter,viewHolder类继承BaseViewHolder。尤其要注意的是ViewHolder的构造方法中一定要有super(itemView); 其余的方法都会按照正确的逻辑执行。若要实现itemView的子项点击事件,需要重写父类的initSubViewClick(int tagPosition) ; 方法。其中参数tagPosition便是对应的itemVIew处于RecyclerView中的位置。在这里是为两个button添加点击事件,先为其设置tag,再设置点击事件,我们这里的setOnClickListener(this) ;  参数传的是this,是因为,我们再父类中实现了View的onClick(View v); 方法。

这样,我们便完成了各类点击事件。

使用方法也很简单,就是直接操作你的adapter就可以了,调用adapter.setXxxx(...) 即可方便地实现各种点击事件。当然,要是你地需求是Touchu事件,或子项地长按事件等,都可以通过这样类似地方式来实现。

最后

方法不止一种,这样地操作方式,这只是我的一种思考。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.jianshu.com/p/8991b9226bdf

相关文章

热门资讯

蜘蛛侠3英雄无归3正片免费播放 蜘蛛侠3在线观看免费高清完整
蜘蛛侠3英雄无归3正片免费播放 蜘蛛侠3在线观看免费高清完整 2021-08-24
2022年最旺的微信头像大全 微信头像2022年最新版图片
2022年最旺的微信头像大全 微信头像2022年最新版图片 2022-01-10
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
返回顶部