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

 

时间: 2024-10-13 11:21:36

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

Android 高级UI设计笔记19:PopupWindow使用详解

1. PopupWindow使用 PopupWindow这个类用来实现一个弹出框,可以使用任意布局的View作为其内容,这个弹出框是悬浮在当前activity之上的. 2. PopupWindow使用的案例: (1)首先是我们弹出框的布局设计,如下: 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.

Android 高级UI设计笔记09:Android如何实现无限滚动列表

ListView和GridView已经成为原生的Android应用实现中两个最流行的设计模式.目前,这些模式被大量的开发者使用,主要是因为他们是简单而直接的实现,同时他们提供了一个良好,整洁的用户体验. 对于ListView和GridView一个共同基本要求是:当用户向下滚动时可以动态加载数据支持无限滚动.下面教你如何在自己的应用中实现这个特性. 具体流程如下: (1)我们需要的一个主要组件是InfiniteScrollListener类,它实现了OnScrollListener接口.让我们直接

Android 高级UI设计笔记07:RecyclerView 的使用(ListView的扩展)

1. 使用RecyclerView       在 Android 应用程序中列表是一个非常重要的控件,适用场合非常多,如新闻列表.应用列表.消息列表等等,但是从Android 一出生到现在并没有非常好用的列表控件,早期的 ListView 用法非常复杂,尤其是自定义列表,简直就是地狱,因为其中还涉及到很多效率优化的问题,新手很难写出高效率的基于列表应用,而且 ListView 只能垂直方向呈现内容,使用很不灵活,为了解决这个缺陷,Android 官方推出了 RecyclerView 控件,用来

Android 高级UI设计笔记10:使用RecyclerView实现通讯录

1. 这里我们首先说明一下我们的目的,使用RecyclerView控件实现手机通讯录效果,如下:

Android 高级UI设计笔记06:仿微信图片选择器

仿微信图片选择器: 一.项目整体分析: 1. Android加载图片的3个目标: (1)尽可能的去避免内存溢出. a. 根据图片的显示大小去压缩图片 b. 使用缓存对我们图片进行管理(LruCache) (2)用户操作UI控件必须充分的流畅. a. getView里面尽可能不去做耗时的操作(异步加载 + 回调显示) (3)用户预期显示的图片尽可能的快(图片的加载策略的选择,一般选择是LIFO). a. LIFO 2. 定义一个Imageloader完成上面1中的3个目标: Imageloader

Android 高级UI设计笔记12:ImageSwitcher图片切换器

1. ImageSwitcher ImageSwitcher是Android中控制图片展示效果的一个控件,如:幻灯片效果...,颇有感觉啊.做相册一绝 2. 重要方法 setImageURI(Uri uri):设置图片地址 setImageResource(int resid):设置图片资源库 setImageDrawable(Drawable drawable):绘制图片 3. 设置动画效果 imageSwitcher.setInAnimation(AnimationUtils.loadAni

Android 高级UI设计笔记11:Gallery(画廊控件)之Gallery基本使用

1. 这里要向大家介绍Android控件Gallery(画廊控件) Gallery控件主要用于横向显示图像列表,不过按常规做法.Gallery组件只能有限地显示指定的图像.也就是说,如果为Gallery组件指定了10张图像,那么当Gallery组件显示到第10张时,就不会再继续显示了. 2. Gallery基本用法: 步骤如下: (1)首先我们新建一个Android工程,工程目录如下: (2)activity_main.xml(主布局文件),如下: 在activity_main.xml添加Ima

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

Android开发是目前最热门的移动开发技术之一,随着开发者的不断努力和Android社区的进步,Android开发技术已经日趋成熟,当然,在Android开源社区中也涌现了很多不错的开源UI项目,它们可以帮助Android开发者更方便快捷地完成想要的功能.本文是Android系列的第一篇,主要是向大家推荐一些常用的Android UI组件,它们都是开源的. 1.图表引擎 -  AChartEngine AChartEngine是一款基于Android的图表绘制引擎,它为Android开发者提供

Android 高级UI设计笔记22:Android 指示引导页(带圆点)

1. 引导页: 我们在安装某个软件首次运行时,大部分都会有一个引导页的提示,介绍软件新功能的加入或者使用说明等,支持滑动且下面会有几个圆点,显示共有多少页和当前图片的位置,类似如下效果: 2. 引导页具体实现功能如下: 可以左右滑动图片. 图片的索引圆点显示,看出当前图片所在的位置和图片的数量. 可任意左右滑动. 图片的索引圆点带有动画效果. 最后一页显示按钮,点击进入应用. 3. 引导页实现逻辑过程: 利用ViewPager实现用户引导界面. 在这里,我们需要用到google提到的一个支持包—