RecyclerView侧重的是布局的灵活性,虽说可以替代ListView但是连基本的点击事件都没有,这篇文章就来详细讲解如何为RecyclerView的item添加点击事件,顺便复习一下观察者模式。
其实在 这篇文章 中已经提到如何实现,但是里面有很多不规范的地方,而且没有完整的代码。
最终目的
模拟ListView的setOnItemClickListener()方法,调用者只须调用类似于setOnItemClickListener的东西就能获得被点击item的相关数据。
原理
为RecyclerView的每个子item设置setOnClickListener,然后在onClick中再调用一次对外封装的接口,将这个事件传递给外面的调用者。而“为RecyclerView的每个子item设置setOnClickListener”在Adapter中设置。其实直接在onClick中也能完全处理item的点击事件,但是这样会破坏代码的逻辑。
步骤
adapter中
自定义一个继承自RecyclerView.Adapter的MyAdapter。
1.在MyAdapter中定义如下接口,模拟ListView的OnItemClickListener:
//define interface public static interface OnItemClickListener { void onItemClick(View view , int position); }
声明一个这个接口的变量
private OnItemClickListener mOnItemClickListener = null;
在onCreateViewHolder()中为每个item添加点击事件
@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注册点击事件 view.setOnClickListener(this); return vh; }
将点击事件转移给外面的调用者:
@Override public void onClick(View v) { if (mOnItemClickListener != null) { //注意这里使用getTag方法获取position mOnItemClickListener.onItemClick(v,(int)v.getTag()); } }
注意上面调用接口的onItemClick()中的v.getTag()方法,这需要在onBindViewHolder()方法中设置和item的position
@Override public void onBindViewHolder(ViewHolder viewHolder, int position) { viewHolder.mTextView.setText(datas[position]); //将position保存在itemView的Tag中,以便点击时进行获取 viewHolder.itemView.setTag(position); }
最后暴露给外面的调用者,定义一个设置Listener的方法():
public void setOnItemClickListener(OnItemClickListener listener) { this.mOnItemClickListener = listener; }
以上所有步骤都发生在自定义的adapter中,典型的观察者模式,有点绕的地方在于,这里涉及到两个观察者模式的使用,view的setOnClickListener本来就是观察者模式,我们将这个观察者模式的事件监听传递给了我们自己的观察者模式。
在Activity中使用
mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view); //创建默认的线性LayoutManager mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); //如果可以确定每个item的高度是固定的,设置这个选项可以提高性能 mRecyclerView.setHasFixedSize(true); //创建并设置Adapter mAdapter = new MyAdapter(data); mRecyclerView.setAdapter(mAdapter); mAdapter.setOnItemClickListener(new OnItemClickListener(){ @Override public void onItemClick(View view , int position){ Toast.makeText(MainActivity.this, data[position], 600).show(); } });
完整代码
MyAdapter.java
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 OnItemClickListener mOnItemClickListener = null; //define interface public static interface OnItemClickListener { void onItemClick(View view , int position); } @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注册点击事件 view.setOnClickListener(this); return vh; } @Override public void onBindViewHolder(ViewHolder viewHolder, int position) { viewHolder.mTextView.setText(datas[position]); //将position保存在itemView的Tag中,以便点击时进行获取 viewHolder.itemView.setTag(position); } @Override public void onClick(View v) { if (mOnItemClickListener != null) { //注意这里使用getTag方法获取position mOnItemClickListener.onItemClick(v,(int)v.getTag()); } } public void setOnItemClickListener(OnItemClickListener listener) { this.mOnItemClickListener = listener; } //获取数据的数量 @Override public int getItemCount() { return datas.length; } //自定义的ViewHolder,持有每个Item的的所有界面元素 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
<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
package com.example.recyclerviewdemo; import com.example.recyclerviewdemo.MyAdapter.OnItemClickListener; 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); //创建默认的线性LayoutManager mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); //如果可以确定每个item的高度是固定的,设置这个选项可以提高性能 mRecyclerView.setHasFixedSize(true); //创建并设置Adapter mAdapter = new MyAdapter(data); mRecyclerView.setAdapter(mAdapter); mAdapter.setOnItemClickListener(new OnItemClickListener(){ @Override public void onItemClick(View view , int position){ Toast.makeText(MainActivity.this, data[position], 600).show(); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
activity_main.xml
<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:
mListView.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View v, int position, long id) { ... } });
而在我们这里是调用mAdapter的setOnItemClickListener。
RecyclerView侧重的是布局的灵活性,虽说可以替代ListView但是连基本的点击事件都没有,这篇文章就来详细讲解如何为RecyclerView的item添加点击事件,顺便复习一下观察者模式。
其实在 这篇文章 中已经提到如何实现,但是里面有很多不规范的地方,而且没有完整的代码。
最终目的
模拟ListView的setOnItemClickListener()方法,调用者只须调用类似于setOnItemClickListener的东西就能获得被点击item的相关数据。
原理
为RecyclerView的每个子item设置setOnClickListener,然后在onClick中再调用一次对外封装的接口,将这个事件传递给外面的调用者。而“为RecyclerView的每个子item设置setOnClickListener”在Adapter中设置。其实直接在onClick中也能完全处理item的点击事件,但是这样会破坏代码的逻辑。
步骤
adapter中
自定义一个继承自RecyclerView.Adapter的MyAdapter。
1.在MyAdapter中定义如下接口,模拟ListView的OnItemClickListener:
//define interface public static interface OnItemClickListener { void onItemClick(View view , int position); }
声明一个这个接口的变量
private OnItemClickListener mOnItemClickListener = null;
在onCreateViewHolder()中为每个item添加点击事件
@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注册点击事件 view.setOnClickListener(this); return vh; }
将点击事件转移给外面的调用者:
@Override public void onClick(View v) { if (mOnItemClickListener != null) { //注意这里使用getTag方法获取position mOnItemClickListener.onItemClick(v,(int)v.getTag()); } }
注意上面调用接口的onItemClick()中的v.getTag()方法,这需要在onBindViewHolder()方法中设置和item的position
@Override public void onBindViewHolder(ViewHolder viewHolder, int position) { viewHolder.mTextView.setText(datas[position]); //将position保存在itemView的Tag中,以便点击时进行获取 viewHolder.itemView.setTag(position); }
最后暴露给外面的调用者,定义一个设置Listener的方法():
public void setOnItemClickListener(OnItemClickListener listener) { this.mOnItemClickListener = listener; }
以上所有步骤都发生在自定义的adapter中,典型的观察者模式,有点绕的地方在于,这里涉及到两个观察者模式的使用,view的setOnClickListener本来就是观察者模式,我们将这个观察者模式的事件监听传递给了我们自己的观察者模式。
在Activity中使用
mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view); //创建默认的线性LayoutManager mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); //如果可以确定每个item的高度是固定的,设置这个选项可以提高性能 mRecyclerView.setHasFixedSize(true); //创建并设置Adapter mAdapter = new MyAdapter(data); mRecyclerView.setAdapter(mAdapter); mAdapter.setOnItemClickListener(new OnItemClickListener(){ @Override public void onItemClick(View view , int position){ Toast.makeText(MainActivity.this, data[position], 600).show(); } });
完整代码
MyAdapter.java
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 OnItemClickListener mOnItemClickListener = null; //define interface public static interface OnItemClickListener { void onItemClick(View view , int position); } @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注册点击事件 view.setOnClickListener(this); return vh; } @Override public void onBindViewHolder(ViewHolder viewHolder, int position) { viewHolder.mTextView.setText(datas[position]); //将position保存在itemView的Tag中,以便点击时进行获取 viewHolder.itemView.setTag(position); } @Override public void onClick(View v) { if (mOnItemClickListener != null) { //注意这里使用getTag方法获取position mOnItemClickListener.onItemClick(v,(int)v.getTag()); } } public void setOnItemClickListener(OnItemClickListener listener) { this.mOnItemClickListener = listener; } //获取数据的数量 @Override public int getItemCount() { return datas.length; } //自定义的ViewHolder,持有每个Item的的所有界面元素 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
<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
package com.example.recyclerviewdemo; import com.example.recyclerviewdemo.MyAdapter.OnItemClickListener; 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); //创建默认的线性LayoutManager mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); //如果可以确定每个item的高度是固定的,设置这个选项可以提高性能 mRecyclerView.setHasFixedSize(true); //创建并设置Adapter mAdapter = new MyAdapter(data); mRecyclerView.setAdapter(mAdapter); mAdapter.setOnItemClickListener(new OnItemClickListener(){ @Override public void onItemClick(View view , int position){ Toast.makeText(MainActivity.this, data[position], 600).show(); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
activity_main.xml
<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:
mListView.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View v, int position, long id) { ... } });
而在我们这里是调用mAdapter的setOnItemClickListener。