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中使用,如下:
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();
}
});
5. 案例演示:(结合上面的步骤理解)
(1)使用Eclipse创建一个工程,如下:
同时注意API要使用API21
(2)首先我们来到主布局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" > <android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/> </RelativeLayout>
(3)接下来,我们来到RecyclerView的item布局item.xml,如下:
<?xml version="1.0" encoding="utf-8"?>
<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:text="默认"
android:layout_marginTop="5dp"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="@android:color/holo_red_dark"/> </RelativeLayout>
(4)接下来我们自定义MyAdapter,如下:
package com.himi.recyclerviewdemo; import android.support.v7.widget.RecyclerView;
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; } /**
* 1.定义接口
*/
public static interface OnRecyclerViewItemClickListener { void onItemClick(View view, String data); } /**
* 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;
} /**
* 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]);
} /**
* 4.将点击事件转移给外面的调用者
*/
public void onClick(View v) {
if (mOnItemClickListener != null) {
// 注意这里使用getTag方法获取数据
mOnItemClickListener.onItemClick(v, (String) v.getTag());
}
} /**
* 2.声明一个这个接口的变量
*/
private OnRecyclerViewItemClickListener mOnItemClickListener = null; /**
*6.最后暴露给外面的调用者,定义一个设置Listener的方法()
*/ 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);
}
} }
(5)接下来来到MainActivity,如下:
package com.himi.recyclerviewdemo; import com.himi.recyclerviewdemo.MyAdapter.OnRecyclerViewItemClickListener; import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Toast; public class MainActivity extends Activity { private RecyclerView mRecyclerView;
private LinearLayoutManager mLayoutManager;
private MyAdapter mAdapter;
private String[] data = new String[] {
"刘德华", "周杰伦", "梁朝伟", "郭富城", "黎明", "张学友",
"成龙", "午马", "洪金宝", "林正英", "元彪", "林志颖",
"吴奇隆", "苏有朋", "赵薇", "陈坤", "周润发", "范冰冰",
"贾静雯", "周星驰"
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview); // 创建默认的线性LayoutManager
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager); // 如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
mRecyclerView.setHasFixedSize(true); //添加item间的分割线
mRecyclerView.addItemDecoration(new RecycleViewDivider(this, LinearLayoutManager.HORIZONTAL)); // 创建并设置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, 1).show();
}
});
} }
这里使用到的自定义分割线类RecycleViewDivider是别人写的,我直接拿来用了,在此我表示感谢,如下:
package com.himi.recyclerviewdemo; import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ItemDecoration;
import android.view.View; public class RecycleViewDivider extends ItemDecoration {
private Paint mPaint;
private Drawable mDivider;
private int mDividerHeight = 2;//分割线高度,默认为1px
private int mOrientation;//列表的方向:LinearLayoutManager.VERTICAL或LinearLayoutManager.HORIZONTAL
private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; /**
* 默认分割线:高度为2px,颜色为灰色
*
* @param context
* @param orientation 列表方向
*/
public RecycleViewDivider(Context context, int orientation) {
if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
throw new IllegalArgumentException("请输入正确的参数!");
}
mOrientation = orientation; final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
} /**
* 自定义分割线
*
* @param context
* @param orientation 列表方向
* @param drawableId 分割线图片
*/
public RecycleViewDivider(Context context, int orientation, int drawableId) {
this(context, orientation);
mDivider = ContextCompat.getDrawable(context, drawableId);
mDividerHeight = mDivider.getIntrinsicHeight();
} /**
* 自定义分割线
*
* @param context
* @param orientation 列表方向
* @param dividerHeight 分割线高度
* @param dividerColor 分割线颜色
*/
public RecycleViewDivider(Context context, int orientation, int dividerHeight, int dividerColor) {
this(context, orientation);
mDividerHeight = dividerHeight;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(dividerColor);
mPaint.setStyle(Paint.Style.FILL);
} //获取分割线尺寸
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.set(0, 0, 0, mDividerHeight);
} //绘制分割线
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if (mOrientation == LinearLayoutManager.VERTICAL) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
} //绘制横向 item 分割线
private void drawHorizontal(Canvas canvas, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getMeasuredWidth() - parent.getPaddingRight();
final int childSize = parent.getChildCount();
for (int i = 0; i < childSize; i++) {
final View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
final int top = child.getBottom() + layoutParams.bottomMargin;
final int bottom = top + mDividerHeight;
if (mDivider != null) {
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
if (mPaint != null) {
canvas.drawRect(left, top, right, bottom, mPaint);
}
}
} //绘制纵向 item 分割线
private void drawVertical(Canvas canvas, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
final int childSize = parent.getChildCount();
for (int i = 0; i < childSize; i++) {
final View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
final int left = child.getRight() + layoutParams.rightMargin;
final int right = left + mDividerHeight;
if (mDivider != null) {
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(canvas);
}
if (mPaint != null) {
canvas.drawRect(left, top, right, bottom, mPaint);
}
}
}
}
(6)部署程序到手机上,如下:
6. 总结:
在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的相关数据(这里只是一个字符串)。
Android 高级UI设计笔记20:RecyclerView 的详解之RecyclerView添加Item点击事件的更多相关文章
- Android 高级UI设计笔记03:使用ListView实现左右滑动删除Item
1. 这里就是实现一个很简单的功能,使用ListView实现左右滑动删除Item: (1)当我们在ListView的某个Item,向左滑动显示一个删除按钮,用户点击按钮,即可以删除该项item,并且有 ...
- Android 高级UI设计笔记07:RecyclerView 的详解
1. 使用RecyclerView 在 Android 应用程序中列表是一个非常重要的控件,适用场合非常多,如新闻列表.应用列表.消息列表等等,但是从Android 一出生到现在并没有非常 ...
- Android 高级UI设计笔记06:仿微信图片选择器(转载)
仿微信图片选择器: 一.项目整体分析: 1. Android加载图片的3个目标: (1)尽可能的去避免内存溢出. a. 根据图片的显示大小去压缩图片 b. 使用缓存对我们图片进行管理(LruCache ...
- Android 高级UI设计笔记21:Android SegmentView(分段选择控件)
1. 分段控制(SegmentView) 首先我们先看看什么是SegmentView的效果,如下: 分段控制这个View控件是ios7的分段控制,和QQ消息页面顶部的效果一样,android没有这个控 ...
- Android 高级UI设计笔记09:Android如何实现无限滚动列表
ListView和GridView已经成为原生的Android应用实现中两个最流行的设计模式.目前,这些模式被大量的开发者使用,主要是因为他们是简单而直接的实现,同时他们提供了一个良好,整洁的用户体验 ...
- Android 高级UI设计笔记09:Android实现无限滚动列表
1. 无限滚动列表应用场景: ListView和GridView已经成为原生的Android应用实现中两个最流行的设计模式.目前,这些模式被大量的开发者使用,主要是因为他们是简单而直接的实现,同时他们 ...
- Android 高级UI设计笔记01:使用ExpandableListView组件(ListView的扩展)
1.ExpandableListView是一个用来显示二级节点的ListView. 比如如下效果的界面: 2.使用ExpandableListView步骤 (1)要给ExpandableListVie ...
- Android 高级UI设计笔记22:Android 指示引导页(带圆点)
1. 引导页: 我们在安装某个软件首次运行时,大部分都会有一个引导页的提示,介绍软件新功能的加入或者使用说明等,支持滑动且下面会有几个圆点,显示共有多少页和当前图片的位置,类似如下效果: 2. 引导页 ...
- Android 高级UI设计笔记19:PopupWindow使用详解
1. PopupWindow使用 PopupWindow这个类用来实现一个弹出框,可以使用任意布局的View作为其内容,这个弹出框是悬浮在当前activity之上的. 2. PopupWindow使用 ...
随机推荐
- Codeforces 711 D. Directed Roads (DFS判环)
题目链接:http://codeforces.com/problemset/problem/711/D 给你一个n个节点n条边的有向图,可以把一条边反向,现在问有多少种方式可以使这个图没有环. 每个连 ...
- POJ2299Ultra-QuickSort (线段树和归并排序的解法)
题目大意就是说帮你给一些(n个)乱序的数,让你求冒泡排序需要交换数的次数(n<=500000) 此题最初真不会做,我也只是在听了章爷的讲解后才慢慢明白过来的 首先介绍线段树的解法: 我们先将原数 ...
- How Tomcat Works(十四)
我们已经知道,在tomcat中有四种类型的servlet容器,分别为Engine.Host.Context 和Wrapper,本文接下来对tomcat中Wrapper接口的标准实现进行说明. 对于每个 ...
- ACM数学问题分类(汇总帖)
数论 组合数学 计算几何 博弈论 线性代数 高等数学 线性规划 概率统计
- Python单步调试
运行 运行python -m pdb test.py (Pdb) 会自动停在第一行,等待调试,这时你可以看看帮助 (Pdb) h 几个关键命令 断点设置 (Pdb)b 10 #断点设置在本py的第10 ...
- github命令
git push origin master //把本地源码库push到Github上 git pull origin master //从Github上pull到本地源码库
- Hard problem
1022: Hard problem Time Limit: 1 Sec Memory Limit: 128 MB Submit: 43 Solved: 12 Description The ...
- JavaBean技术
引言: JavaBeans与一般Java类的编写类似. 在JSP页面中要使用JavaBeans,只要在JSP页面中使用JavaBeans的操作标记就可以了. JavaBeans的编写和使用非常简单,下 ...
- Java常见排序算法之冒泡排序
在学习算法的过程中,我们难免会接触很多和排序相关的算法.总而言之,对于任何编程人员来说,基本的排序算法是必须要掌握的. 从今天开始,我们将要进行基本的排序算法的讲解.Are you ready?Let ...
- JDBC-ODBC桥乱码问题解决方案
按照网上提供的ODBC连接数据库的相关资料编写代码,成功编译后运行发现,非中文字段显示正确,而中文字段却是每个汉字以?显示.关于这方面的错误baidu或google下可以找到很多解答方案,我也尝试过其 ...