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 OnRecyclerViewItemClickListener {
- void onItemClick(View view , String data);
- }
聲明一個這個接口的變量
- private OnRecyclerViewItemClickListener 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方法獲取數據
- mOnItemClickListener.onItemClick(v,(String)v.getTag());
- }
- }
注意上面調用接口的onItemClick()中的v.getTag()方法,這需要在onBindViewHolder()方法中設置和item相關的數據
- @Override
- public void onBindViewHolder(ViewHolder viewHolder, int position) {
- viewHolder.mTextView.setText(datas[position]);
- //將數據保存在itemView的Tag中,以便點擊時進行獲取
- viewHolder.itemView.setTag(datas[position]);
- }
最後暴露給外面的調用者,定義一個設置Listener的方法():
- public void setOnItemClickListener(OnRecyclerViewItemClickListener 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 OnRecyclerViewItemClickListener(){
- @Override
- public void onItemClick(View view , String data){
- Toast.makeText(MainActivity.this, data, 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 OnRecyclerViewItemClickListener mOnItemClickListener = null;
- //define interface
- 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註冊點擊事件
- view.setOnClickListener(this);
- return vh;
- }
- @Override
- public void onBindViewHolder(ViewHolder viewHolder, int position) {
- viewHolder.mTextView.setText(datas[position]);
- //將數據保存在itemView的Tag中,以便點擊時進行獲取
- viewHolder.itemView.setTag(datas[position]);
- }
- @Override
- public void onClick(View v) {
- if (mOnItemClickListener != null) {
- //注意這裏使用getTag方法獲取數據
- mOnItemClickListener.onItemClick(v,(String)v.getTag());
- }
- }
- public void setOnItemClickListener(OnRecyclerViewItemClickListener 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.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);
- //創建默認的線性LayoutManager
- mLayoutManager = new LinearLayoutManager(this);
- mRecyclerView.setLayoutManager(mLayoutManager);
- //如果可以確定每個item的高度是固定的,設置這個選項可以提高性能
- mRecyclerView.setHasFixedSize(true);
- //創建並設置Adapter
- 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) {
- // 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。且回調方法public void onItemClick()的參數也不一致,ListView中有被點擊item的position參數,而我們這裏直接是被點擊item的相關數據(這裏只是一個字符串)。