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 interfacepublic static interface OnRecyclerViewItemClickListener {void onItemClick(View view , String data);}

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

private OnRecyclerViewItemClickListener mOnItemClickListener = null;

(3)在onCreateViewHolder()中为每个item添加点击事件

 @Overridepublic 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)将点击事件转移给外面的调用者:

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

(5)注意上面调用接口的onItemClick()中的v.getTag()方法,这需要在onBindViewHolder()方法中设置和item相关的数据

@Overridepublic 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的相关数据(这里只是一个字符串)。

 

转载于:https://www.cnblogs.com/hebao0514/p/5643225.html

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

  1. Android 高级UI设计笔记08:Android开发者常用的7款Android UI组件(转载)

    Android开发是目前最热门的移动开发技术之一,随着开发者的不断努力和Android社区的进步,Android开发技术已经日趋成熟,当然,在Android开源社区中也涌现了很多不错的开源UI项目,它 ...

  2. android 之UI 高级控件Adapter(适配器详解)

    了解适配器组件: 适配器是一个连接数据源和AdapterView的桥梁,通过它能有效的实现数据源与AdapterView的分离设置,使AdaptereView与数据的绑定更加简便,修改更加方便. Ad ...

  3. Android 高级UI解密 (三) :Canvas裁剪 与 二维、三维Camera几何变换(图层Layer原理)

    Android的绘图机制是核心内容之一,无论是什么样的功能最终都是以图像的形式呈现给用户.因此掌握Android的绘图技巧,有助于Android理解层次的提高,在面对产品经理提出的idea时也更有底气 ...

  4. 你连《Android高级UI与FrameWork源码》都搞不懂学什么Android?还敢面试阿里P7!

    Android高级UI与FrameWork源码 重要性? 这块知识是现今使用者最多的,我们称之为Android2013~2016年的技术,但是,即使是这样的技术,Android开发者也往往因为网上Co ...

  5. android高级UI之贝塞尔曲线<上>---基本概念、德卡斯特里奥算法

    在上一次android高级UI之Canvas综合案例操练 - cexo - 博客园对于Android UI绘制中核心的Canvas进行了相关的学习,这块的学习也中断一年多了,既然主业是Android开 ...

  6. Android 高级UI解密 (四) :花式玩转贝塞尔曲线(波浪、轨迹变换动画)

    讲解此UI系列必然少不了一个奇妙数学曲线-–贝塞尔曲线,它目前运用于App的范围是在太广了,最初的QQ气泡拖拽,到个人界面的波浪效果.Loading波浪效果,甚至于轨迹变化的动画都可以依赖贝塞尔曲线完 ...

  7. android炫酷动画代码,Android高级UI特效仿直播点赞动画效果

    Android高级UI特效仿直播点赞动画效果 发布时间:2020-10-02 16:06:18 来源:脚本之家 阅读:117 作者:mrr 本文给大家分享高级UI特效仿直播点赞效果-一个优美炫酷的点赞 ...

  8. NC高级UI工厂笔记

    NC高级UI工厂笔记 本篇文章主要介绍了"NC高级UI工厂笔记",主要涉及到NC高级UI工厂笔记方面的内容,对于NC高级UI工厂笔记感兴趣的同学可以参考一下. 1.控制按钮是否可见 ...

  9. Android高级UI系列教程(二)

    上期回顾 Android高级UI系列教程(一)_我想月薪过万的博客-CSDN博客https://blog.csdn.net/qq_41885673/article/details/121870917 ...

最新文章

  1. java中缀表达式转后缀表达式(逆波兰算法)
  2. Webwork 学习之路【08】结合实战简析Controller 配置
  3. MPLS BGP标签分发过程——Vecloud
  4. 错误 1093 You can't specify target table 'table name' for update in FROM clause
  5. airdroid黑屏_如何使用AirDroid从PC控制Android设备
  6. spring自动装配依赖包_解决Spring自动装配中的循环依赖
  7. linux下gsoap的初次使用 (c)
  8. 北向资金进行股票、期货指数、基金策略
  9. linux如何自动化部署脚本实现免密登录并访问资源
  10. flask登录验证用ajax,基于 Ajax 请求的 Flask-Login 认证
  11. Circular buffer
  12. vue中v-on指令的使用之Vue知识点归纳(四)
  13. c语言snprintf函数简介
  14. 人生长途,感受着不断拼搏...
  15. 超级好用的一个php上传图片类(随机名_缩略图_加水印),php教程_超级好用的一个php上传图片类(随机名,缩略图,加水印)...
  16. 临近空间大气环境特性
  17. 远程计算机蓝屏,什么原因造成了蓝屏 电脑蓝屏错误代码介绍
  18. 神经网络长什么样不知道?这有一份简单的 pytorch可视化技巧
  19. 【论文阅读】AProNet: Detecting objects with precise orientation from aerial images
  20. 《数字图像处理》题库5:计算题 ①

热门文章

  1. 禁用人脸识别四个月后,旧金山人证明了他们的先见之明
  2. TPU3.0今日上岗!谷歌AI芯片甩竞争对手好几条街!
  3. 利用 Numpy 进行矩阵相关运算
  4. 为什么「道理都懂,然而执行力差」的现象如此普遍?
  5. 计算机科学千分之一高校,69所计算机科学进入ESI世界前1%的高校及排名,西电全国第四!...
  6. MATLAB在温室中的应用,基于MATLAB的日光温室内气温的图形显示方法与流程
  7. mysql 存储地理信息_使用Amazon Aurora存储和处理地理信息数据
  8. 开元弧焊机器人编程_焊接机器人的技术现状和展望
  9. Oracle DML、DDL、DCL的区别
  10. 015_JavaMail