Android 高级UI设计笔记20:RecyclerView 的详解之RecyclerView添加Item点击事件


1. 引言:

RecyclerView侧重的是布局的灵活性,虽说可以替代ListView但是连基本的点击事件都没有,这篇文章就来详细讲解如何为RecyclerView的item添加点击事件,顺便复习一下观察者模式。

 

2. 最终目的

模拟ListView的setOnItemClickListener()方法,调用者只须调用类似于setOnItemClickListener的东西就能获得被点击item的相关数据。

 

3. 原理

为RecyclerView的每个子item设置setOnClickListener,然后在onClick中再调用一次对外封装的接口,将这个事件传递给外面的调用者。而"为RecyclerView的每个子item设置setOnClickListener"在Adapter中设置。其实直接在onClick中也能完全处理item的点击事件,但是这样会破坏代码的逻辑。

 

4. 具体步骤如下:

在自定义MyAdapter之中(继承自RecyclerView.Adapter

(1)在MyAdapter中定义如下接口,模拟ListView的OnItemClickListener:

 //define interface
    public static interface OnRecyclerViewItemClickListener { void onItemClick(View view , String data); }

 

(2)声明一个这个接口的变量

private OnRecyclerViewItemClickListener mOnItemClickListener = null;

 

(3)在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; }

 

(4)将点击事件转移给外面的调用者:

 @Override public void onClick(View v) { if (mOnItemClickListener != null) { //注意这里使用getTag方法获取数据
 mOnItemClickListener.onItemClick(v,(String)v.getTag()); } }

 

(5)注意上面调用接口的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]); }

(6)最后暴露给外面的调用者,定义一个设置Listener的方法():

 public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) { this.mOnItemClickListener = listener; }

 以上所有步骤都发生在自定义的adapter中,典型的观察者模式,有点绕的地方在于,这里涉及到两个观察者模式的使用,view的setOnClickListener本来就是观察者模式,我们将这个观察者模式的事件监听传递给了我们自己的观察者模式。

 

(7)接下来当然是在Activity中使用,如下:

 1 mRecyclerView = (RecyclerView)findViewById(R.id.my_recycler_view);  2         //创建默认的线性LayoutManager
 3         mLayoutManager = new LinearLayoutManager(this);  4  mRecyclerView.setLayoutManager(mLayoutManager);  5         //如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
 6         mRecyclerView.setHasFixedSize(true);  7         //创建并设置Adapter
 8         mAdapter = new MyAdapter(data);  9  mRecyclerView.setAdapter(mAdapter); 10         mAdapter.setOnItemClickListener(new OnRecyclerViewItemClickListener(){ 11  @Override 12             public void onItemClick(View view , String data){ 13                 Toast.makeText(MainActivity.this, data, 600).show(); 14  } 15         });

 

5. 案例演示:(结合上面的步骤理解)

(1)使用Eclipse创建一个工程,如下:

同时注意API要使用API21

 

(2)首先我们来到主布局activity_main.xml,如下:

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2  xmlns:tools="http://schemas.android.com/tools"
 3  android:layout_width="match_parent"
 4  android:layout_height="match_parent" >
 5 
 6     <android.support.v7.widget.RecyclerView  7         android:id="@+id/recyclerview"
 8  android:layout_width="match_parent"
 9  android:layout_height="match_parent"
10          />
11 
12 </RelativeLayout>

(3)接下来,我们来到RecyclerView的item布局item.xml,如下:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3  xmlns:tools="http://schemas.android.com/tools"
 4  android:layout_width="match_parent"
 5  android:layout_height="50dip" >
 6 
 7     <TextView  8         android:id="@+id/text"
 9  android:text="默认"
10  android:layout_marginTop="5dp"
11  android:gravity="center_horizontal"
12  android:layout_width="match_parent"
13  android:layout_height="wrap_content"
14  android:textSize="20sp" 
15  android:textColor="@android:color/holo_red_dark"/>
16 
17 </RelativeLayout>

 

(4)接下来我们自定义MyAdapter,如下:

 1 package com.himi.recyclerviewdemo;  2 
 3 import android.support.v7.widget.RecyclerView;  4 import android.view.LayoutInflater;  5 import android.view.View;  6 import android.view.ViewGroup;  7 import android.widget.TextView;  8 
 9 public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> 
10                                 implements View.OnClickListener { 11 
12     private String[] datas; 13 
14     public MyAdapter(String[] datas) { 15         this.datas = datas; 16 
17  } 18 
19 
20     /**
21  * 1.定义接口 22      */
23     public static interface OnRecyclerViewItemClickListener { 24         
25         void onItemClick(View view, String data); 26         
27  } 28 
29     /**
30  * 3.在onCreateViewHolder()中为每个item添加点击事件 31      */
32  @Override 33     public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { 34         View view = LayoutInflater.from(viewGroup.getContext()) 35                 .inflate(R.layout.item, viewGroup, false); 36         
37         ViewHolder vh = new ViewHolder(view); 38         // 将创建的View注册点击事件
39         view.setOnClickListener(this); 40         
41         return vh; 42  } 43     
44     /**
45  * 5.注意上面调用接口的onItemClick()中的v.getTag()方法, 46  * 这需要在onBindViewHolder()方法中设置和item相关的数据 47      */
48  @Override 49     public void onBindViewHolder(ViewHolder viewHolder, int position) { 50         
51  viewHolder.mTextView.setText(datas[position]); 52         
53         // 将数据保存在itemView的Tag中,以便点击时进行获取
54  viewHolder.itemView.setTag(datas[position]); 55  } 56 
57     /**
58  * 4.将点击事件转移给外面的调用者 59      */
60     public void onClick(View v) { 61         if (mOnItemClickListener != null) { 62             // 注意这里使用getTag方法获取数据
63  mOnItemClickListener.onItemClick(v, (String) v.getTag()); 64  } 65  } 66 
67     /**
68  * 2.声明一个这个接口的变量 69      */
70     private OnRecyclerViewItemClickListener mOnItemClickListener = null; 71     
72     
73     /**
74  *6.最后暴露给外面的调用者,定义一个设置Listener的方法() 75      */
76     
77     public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) { 78         this.mOnItemClickListener = listener; 79  } 80     
81     
82 
83     // 获取数据的数量
84  @Override 85     public int getItemCount() { 86         return datas.length; 87  } 88 
89     // 自定义的ViewHolder,持有每个Item的的所有界面元素
90     public static class ViewHolder extends RecyclerView.ViewHolder { 91         public TextView mTextView; 92 
93         public ViewHolder(View view) { 94             super(view); 95             mTextView = (TextView) view.findViewById(R.id.text); 96  } 97  } 98 
99 }

 

(5)接下来来到MainActivity,如下:

 1 package com.himi.recyclerviewdemo;  2 
 3 import com.himi.recyclerviewdemo.MyAdapter.OnRecyclerViewItemClickListener;  4 
 5 import android.app.Activity;  6 import android.os.Bundle;  7 import android.support.v7.widget.LinearLayoutManager;  8 import android.support.v7.widget.RecyclerView;  9 import android.view.View; 10 import android.widget.Toast; 11 
12 public class MainActivity extends Activity { 13 
14     private RecyclerView mRecyclerView; 15     private LinearLayoutManager mLayoutManager; 16     private MyAdapter mAdapter; 17     private String[] data = new String[] { 18                 "刘德华", "周杰伦", "梁朝伟", "郭富城", "黎明", "张学友", 19                 "成龙", "午马", "洪金宝", "林正英", "元彪", "林志颖", 20                 "吴奇隆", "苏有朋", "赵薇", "陈坤", "周润发", "范冰冰", 21                 "贾静雯", "周星驰" 
22  }; 23 
24  @Override 25     protected void onCreate(Bundle savedInstanceState) { 26         super.onCreate(savedInstanceState); 27  setContentView(R.layout.activity_main); 28         mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview); 29 
30         // 创建默认的线性LayoutManager
31         mLayoutManager = new LinearLayoutManager(this); 32  mRecyclerView.setLayoutManager(mLayoutManager); 33 
34         // 如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
35         mRecyclerView.setHasFixedSize(true); 36         
37         //添加item间的分割线
38         mRecyclerView.addItemDecoration(new RecycleViewDivider(this, LinearLayoutManager.HORIZONTAL)); 39 
40         // 创建并设置Adapter
41         mAdapter = new MyAdapter(data); 42  mRecyclerView.setAdapter(mAdapter); 43         mAdapter.setOnItemClickListener(new OnRecyclerViewItemClickListener() { 44  @Override 45             public void onItemClick(View view, String data) { 46                 Toast.makeText(MainActivity.this, data, 1).show(); 47  } 48  }); 49  } 50 
51 }

这里使用到的自定义分割线类RecycleViewDivider是别人写的,我直接拿来用了,在此我表示感谢,如下:

 1 package com.himi.recyclerviewdemo;  2 
 3 import android.content.Context;  4 import android.content.res.TypedArray;  5 import android.graphics.Canvas;  6 import android.graphics.Paint;  7 import android.graphics.Rect;  8 import android.graphics.drawable.Drawable;  9 import android.support.v4.content.ContextCompat;  10 import android.support.v7.widget.LinearLayoutManager;  11 import android.support.v7.widget.RecyclerView;  12 import android.support.v7.widget.RecyclerView.ItemDecoration;  13 import android.view.View;  14 
 15 public class RecycleViewDivider extends ItemDecoration {  16       private Paint mPaint;  17         private Drawable mDivider;  18         private int mDividerHeight = 2;//分割线高度,默认为1px
 19         private int mOrientation;//列表的方向:LinearLayoutManager.VERTICAL或LinearLayoutManager.HORIZONTAL
 20         private static final int[] ATTRS = new int[]{android.R.attr.listDivider};  21 
 22         /**
 23  * 默认分割线:高度为2px,颜色为灰色  24  *  25  * @param context  26  * @param orientation 列表方向  27          */
 28         public RecycleViewDivider(Context context, int orientation) {  29             if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {  30                 throw new IllegalArgumentException("请输入正确的参数!");  31  }  32             mOrientation = orientation;  33 
 34             final TypedArray a = context.obtainStyledAttributes(ATTRS);  35             mDivider = a.getDrawable(0);  36  a.recycle();  37  }  38 
 39         /**
 40  * 自定义分割线  41  *  42  * @param context  43  * @param orientation 列表方向  44  * @param drawableId 分割线图片  45          */
 46         public RecycleViewDivider(Context context, int orientation, int drawableId) {  47             this(context, orientation);  48             mDivider = ContextCompat.getDrawable(context, drawableId);  49             mDividerHeight = mDivider.getIntrinsicHeight();  50  }  51 
 52         /**
 53  * 自定义分割线  54  *  55  * @param context  56  * @param orientation 列表方向  57  * @param dividerHeight 分割线高度  58  * @param dividerColor 分割线颜色  59          */
 60         public RecycleViewDivider(Context context, int orientation, int dividerHeight, int dividerColor) {  61             this(context, orientation);  62             mDividerHeight = dividerHeight;  63             mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  64  mPaint.setColor(dividerColor);  65  mPaint.setStyle(Paint.Style.FILL);  66  }  67 
 68 
 69         //获取分割线尺寸
 70  @Override  71         public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {  72             super.getItemOffsets(outRect, view, parent, state);  73             outRect.set(0, 0, 0, mDividerHeight);  74  }  75 
 76         //绘制分割线
 77  @Override  78         public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {  79             super.onDraw(c, parent, state);  80             if (mOrientation == LinearLayoutManager.VERTICAL) {  81  drawVertical(c, parent);  82             } else {  83  drawHorizontal(c, parent);  84  }  85  }  86 
 87         //绘制横向 item 分割线
 88         private void drawHorizontal(Canvas canvas, RecyclerView parent) {  89             final int left = parent.getPaddingLeft();  90             final int right = parent.getMeasuredWidth() - parent.getPaddingRight();  91             final int childSize = parent.getChildCount();  92             for (int i = 0; i < childSize; i++) {  93                 final View child = parent.getChildAt(i);  94                 RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();  95                 final int top = child.getBottom() + layoutParams.bottomMargin;  96                 final int bottom = top + mDividerHeight;  97                 if (mDivider != null) {  98  mDivider.setBounds(left, top, right, bottom);  99  mDivider.draw(canvas); 100  } 101                 if (mPaint != null) { 102  canvas.drawRect(left, top, right, bottom, mPaint); 103  } 104  } 105  } 106 
107         //绘制纵向 item 分割线
108         private void drawVertical(Canvas canvas, RecyclerView parent) { 109             final int top = parent.getPaddingTop(); 110             final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom(); 111             final int childSize = parent.getChildCount(); 112             for (int i = 0; i < childSize; i++) { 113                 final View child = parent.getChildAt(i); 114                 RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams(); 115                 final int left = child.getRight() + layoutParams.rightMargin; 116                 final int right = left + mDividerHeight; 117                 if (mDivider != null) { 118  mDivider.setBounds(left, top, right, bottom); 119  mDivider.draw(canvas); 120  } 121                 if (mPaint != null) { 122  canvas.drawRect(left, top, right, bottom, mPaint); 123  } 124  } 125  } 126     }

(6)部署程序到手机上,如下:

 

 

6. 总结:

在ListView中我们是调用ListView的setOnItemClickListener:

1 mListView.setOnItemClickListener(new OnItemClickListener() { 2             public void onItemClick(AdapterView<?> parent, View v, int position, long id) { 3                  
4  ... 5                 
6  } 7         });

而在我们这里是调用mAdapter的setOnItemClickListener。且回调方法public void onItemClick()的参数也不一致,ListView中有被点击item的position参数,而我们这里直接是被点击item的相关数据(这里只是一个字符串)。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM