最终目的
模拟ListView的setOnItemClickListener()方法,调用者只须调用类似于setOnItemClickListener的东西就能获得被点击item的相关数据。
原理
为RecyclerView的每个子item设置setOnClickListener,然后在onClick中再调用一次对外封装的接口,将这个事件传递给外面的调用者。而“为RecyclerView的每个子item设置setOnClickListener”在Adapter中设置。其实直接在onClick中也能完全处理item的点击事件,但是这样会破坏代码的逻辑。
步骤
adapter中
自定义一个继承自RecyclerView.Adapter的MyAdapter。
1.在MyAdapter中定义如下接口,模拟ListView的OnItemClickListener:
1 2 3 4
| public static interface OnRecyclerViewItemClickListener { void onItemClick(View view , String data); }
|
声明一个这个接口的变量
private OnRecyclerViewItemClickListener mOnItemClickListener = null;
在onCreateViewHolder()中为每个item添加点击事件
1 2 3 4 5 6 7 8
| @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false); ViewHolder vh = new ViewHolder(view); view.setOnClickListener(this); return vh; }
|
将点击事件转移给外面的调用者:
1 2 3 4 5 6 7
| @Override public void onClick(View v) { if (mOnItemClickListener != null) { mOnItemClickListener.onItemClick(v,(String)v.getTag()); } }
|
注意上面调用接口的onItemClick()中的v.getTag()方法,这需要在onBindViewHolder()方法中设置和item相关的数据
1 2 3 4 5 6
| @Override public void onBindViewHolder(ViewHolder viewHolder, int position) { viewHolder.mTextView.setText(datas[position]); viewHolder.itemView.setTag(datas[position]); }
|
最后暴露给外面的调用者,定义一个设置Listener的方法():
1 2 3
| public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) { this.mOnItemClickListener = listener; }
|
以上所有步骤都发生在自定义的adapter中,典型的观察者模式,有点绕的地方在于,这里涉及到两个观察者模式的使用,view的setOnClickListener本来就是观察者模式,我们将这个观察者模式的事件监听传递给了我们自己的观察者模式。
在Activity中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view);
mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setHasFixedSize(true);
mAdapter = new MyAdapter(data); mRecyclerView.setAdapter(mAdapter); mAdapter.setOnItemClickListener(new OnRecyclerViewItemClickListener(){ @Override public void onItemClick(View view , String data){ Toast.makeText(MainActivity.this, data, 600).show(); } });
|
完整代码
MyAdapter.java
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
| package com.example.recyclerviewdemo; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener{ private String[] datas; public MyAdapter(String[] datas) { this.datas = datas; } private OnRecyclerViewItemClickListener mOnItemClickListener = null; public static interface OnRecyclerViewItemClickListener { void onItemClick(View view , String data); } @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false); ViewHolder vh = new ViewHolder(view); view.setOnClickListener(this); return vh; } @Override public void onBindViewHolder(ViewHolder viewHolder, int position) { viewHolder.mTextView.setText(datas[position]); viewHolder.itemView.setTag(datas[position]); } @Override public void onClick(View v) { if (mOnItemClickListener != null) { mOnItemClickListener.onItemClick(v,(String)v.getTag()); } } public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) { this.mOnItemClickListener = listener; } @Override public int getItemCount() { return datas.length; } public static class ViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public ViewHolder(View view){ super(view); mTextView = (TextView) view.findViewById(R.id.text); } } }
|
item.xml
1 2 3 4 5 6 7 8 9 10 11
| <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="50dip" > <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
|
MainActivity.java
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
| package com.example.recyclerviewdemo; import com.example.recyclerviewdemo.MyAdapter.OnRecyclerViewItemClickListener; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Toast; public class MainActivity extends ActionBarActivity { private RecyclerView mRecyclerView; private LinearLayoutManager mLayoutManager; private MyAdapter mAdapter; private String[] data= new String[] {"aa","bb", "aa","bb", "aa","bb", "aa","bb", "aa","bb","aa","bb", "aa","bb", "aa","bb", "aa","bb", "aa","bb" }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view); mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); mRecyclerView.setHasFixedSize(true); mAdapter = new MyAdapter(data); mRecyclerView.setAdapter(mAdapter); mAdapter.setOnItemClickListener(new OnRecyclerViewItemClickListener(){ @Override public void onItemClick(View view , String data){ Toast.makeText(MainActivity.this, data, 600).show(); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
|
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11
| <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/my_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"/> </RelativeLayout>
|
总结
在ListView中我们是调用ListView的setOnItemClickListener:
1 2 3 4 5 6 7
| mListView.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View v, int position, long id) { ... } });
|
而在我们这里是调用mAdapter的setOnItemClickListener。且回调方法public void onItemClick()
的参数也不一致,ListView中有被点击item的position参数,而我们这里直接是被点击item的相关数据(这里只是一个字符串)。
RecyclerView onItemClick 按钮和布局都有单击事件时的处理方式
RecyclerView为了给开发者提供更大的自由度,没有默认的提供onItemClick接口。
网上有一种比较简单的实现方式,适用于不需要针对item里面某个按钮做特殊处理的情况
我目前项目的需求是:
1.单击item,跳转到用户信息页。
2.单击加关注按钮,更改按钮状态。
adapter代码如下
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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| public class MyAdapter extends RecyclerView.Adapter<MyAdapter.BaseViewHolder> { private List<User> mList; private Context mContext; public PraisedAdapter(Context context,List<User> list){ mContext = context; this.mList = list; } @Override public BaseViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { View itemView = LayoutInflater.from(viewGroup.getContext()). inflate(R.layout.adapter_test, viewGroup, false); return new BaseViewHolder(itemView, new IMyViewHolderClicks() { @Override public void onItemClick(String uid) { } @Override public void onFollowStatusChange(final User user,final int position) { notifyItemChanged(position); } }); } @Override public void onBindViewHolder(final BaseViewHolder baseViewHolder,final int position) { User user = mList.get(position); baseViewHolder.bind(user); } @Override public int getItemCount() { return mList.size(); } public class BaseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ TextView tvFollowStatus; LinearLayout linearContainer; IMyViewHolderClicks mListener; public BaseViewHolder(View v,IMyViewHolderClicks listener) { super(v); tvFollowStatus = (TextView)v.findViewById(R.id.tv_follow_status); linearContainer = (LinearLayout)v.findViewById(R.id.linear_container); mListener = listener; tvFollowStatus.setOnClickListener(this); linearContainer.setOnClickListener(this); } public void bind(User user) { if(user.getIs_attention()==1){ tvFollowStatus.setText("已关注"); }else{ tvFollowStatus.setText("加关注"); } tvFollowStatus.setTag(user); tvFollowStatus.setClickable(true); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.tv_follow_status: tvFollowStatus.setClickable(false); mListener.onFollowStatusChange((User) tvFollowStatus.getTag(),getLayoutPosition()); break; case R.id.linear_container: mListener.onItemClick(((User)tvFollowStatus.getTag()).getId()); break; } } } private interface IMyViewHolderClicks{ public void onItemClick(String uid); public void onFollowStatusChange(User user,int position); } }
|
以前在经常在绑定数据(bindViewHolder)中加入setOnClickListener(new View.OnClickListener)
,这样每次都创建会很影响效率。
附上一篇详细介绍RecyclerView的地址