完美解決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