【Android - MD】之RecyclerView的使用
RecyclerView是Android 5.0新特性——Material Design中的一个控件,它将ListView、GridView整合到一起,可以使用极少的代码在ListView、GridView和瀑布流等布局方式之间转换。RecyclerView整体使用的是插件式的方式,解耦度相比提高了不少,非常灵活。
RecyclerView之所以叫RecyclerView,是因为它的特性:它不关心Item是否显示在正确的位置上;不关心Item间如何分隔;不关心增加与删除的动画效果,只关心如何回收和复用View。
RecyclerView可以实现的Item布局方式:
- 类似ListView的样式(横、纵都可以实现)
- 类似GridView的样式(横、纵都可以实现)
- 瀑布流样式(交错布局)
RecyclerView中可能用到的类:
- LayoutManager:用来管理RecyclerView中的Item的布局方式
- ItemDecoration:用来绘制RecyclerView中Item之间的间隔
- ItemAnimation:用来绘制RecyclerView中的各种动画
- RecyclerView.ViewHolder:用来存放每个Item中的控件
- RecyclerView.Adapter:RecyclerView的适配器类的父类
1、RecyclerView适配数据:
RecyclerView适配数据的方法和ListView、GridView使用的BaseAdapter适配数据的方法不太相同,RecyclerView内部提供了一个ViewHolder用来盛放item中出现的控件,相当于BaseAdapter中我们自己定义的ViewHolder相同,从这可以看出,从RecyclerView开始,Android开始“逼”我们对Item进行回收和复用;RecyclerView内部还提供了一个Adapter,其中有三个抽象方法:
- getItemCount():获取Item的个数
- onCreateViewHolder():返回当前Item的ViewHolder
- onBindViewHolder():向ViewHolder中适配数据
说了这么多,下面贴一下RecyclerView的适配器类RecyclerAdaper中的代码:
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
private Context context;
private List<String> data;
private LayoutInflater inflater;
public RecyclerAdapter(Context context, List<String> data, boolean isStagger, OnRecyclerViewItemOperationListener listener) {
this.context = context;
this.data = data;
this.inflater = LayoutInflater.from(context);
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.recycleritem_item, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
holder.tv.setText(data.get(position));
}
@Override
public int getItemCount() {
return data.size();
}
static class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public MyViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.item_tv);
}
}
}
2、展示模式:
如果是ListView或者GridView,那么直接设置适配器就可以显示数据了,而RecyclerView不行,因为RecyclerView是可以在ListView和GridView,甚至瀑布流之间进行任意切换的,因此我们还需要设置它的布局模式,这里就用到了LayoutManager类。LayoutManager是一个抽象类,我们最常用的子类有两个:LinearLayoutManager(适合线性布局,用于实现ListView的效果)和StaggeredGridLayoutManager(适合格子布局,用于实现GridView或瀑布流的效果)。
我们可以通过RecyclerView对象的setLayoutManager()方法设置它的展示模式:
rv.setLayoutManager(new LinearLayoutManager(MainActivity.this, LinearLayoutManager.VERTICAL, false));
3、分隔线:
这里说的分隔线是RecyclerView的Item之间的分隔线,其实我们大可以不用“正儿八经”的给RecyclerView设置分隔线,因为我们可以使用Item的margin来设置间距,简介实现分隔线的效果。
RecyclerView也给我们提供了一个分隔线的抽象类——ItemDecoration,可以帮助我们实现分隔线,但是Android没有给我们提供这个类的子类,因此,我们需要自己去写。GitHub上有很多大神发布了一些分隔线的类,这里贴出其中一个DividerItemDecoration类来:
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
// 系统默认的分隔条的Drawable资源的ID
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
// 绘制Item间间隔的Drawable
private Drawable mDivider;
// 方向(水平、数值)
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
// 获取系统提供的分隔条的Drawable对象
mDivider = a.getDrawable(0);
// 回收TypedArray所占用的控件
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
/**
* 如果设置为纵向列表的样式,则调用这个方法
*/
public void drawVertical(Canvas c, RecyclerView parent) {
// Item距离左边缘的距离
final int left = parent.getPaddingLeft();
// Item距离右边缘的距离
final int right = parent.getWidth() - parent.getPaddingRight();
// 获取Item的总数
final int childCount = parent.getChildCount();
// 开始绘制所有Item之间的分隔线
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
RecyclerView v = new RecyclerView(parent.getContext());
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
// Item距离上边缘的距离
final int top = child.getBottom() + params.bottomMargin;
// Item距离下边缘的距离
final int bottom = top + mDivider.getIntrinsicHeight();
// 分隔线可以看成是一个长方形,所以需要设置它的上下左右的位置
mDivider.setBounds(left, top, right, bottom);
// 开始绘制分隔线
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
这样,我们只需要调用下面这行代码,就可以为RecyclerView设置分隔线了:
// 设置RecyclerView的分隔线
rv.addItemDecoration(new DividerItemDecoration(MainActivity.this, DividerItemDecoration.VERTICAL_LIST));
现在,我们倒回来看一下这个DividerItemDecoration类,其中提到了android.R.attr.listDivider这个属性,这个类中调用的是当前主题中设置的listDivider属性的值,我们可以通过修改主题中的这个属性来达到自定义分隔线的目的。我们只需要自己设计一个分隔线布局,然后在res/styles.xml文件中的AppTheme中添加下面这行代码,就可以实现自定义分隔线了:
<item name="android:listDivider">@drawable/divider_gradient</item>
4、动画:
现在很多APP中都用到了RecyclerView,其中不乏有一些非常炫酷的动画,例如:向下滑动的时候使用动画添加Item、添加Item时候的动画、删除Item时候的动画等。GitHub上也有很多这类动画,大家可以找自己喜欢的动画来设置。
这里用的是系统给我们提供的一种动画——DefaultItemAnimator,我们直接调用下面这行代码,就为RecyclerView设置好了动画。
// 设置RecyclerView的动画效果
rv.setItemAnimator(new DefaultItemAnimator());
这个动画只有添加/删除Item的时候的动画,没有下滑加载Item时候的动画。
5、点击和长按事件:
RecyclerView中没有给我们提供OnClickListener、OnLongClickListener这类接口,因此,我们需要自己写,方法就是使用接口回调。我们可以在Adapter中设置一个接口,里面有点击和长按两个抽象方法,然后在onBindViewHolder()方法中设置Item的View的点击和长按事件,分别回调这两个抽象方法,然后在外界为Adapter对象设置这个接口即可。具体的Adapter的代码如下:
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
private Context context;
private List<String> data;
private LayoutInflater inflater;
private OnRecyclerViewItemOperationListener listener;
public RecyclerAdapter(Context context, List<String> data, boolean isStagger, OnRecyclerViewItemOperationListener listener) {
this.context = context;
this.data = data;
this.inflater = LayoutInflater.from(context);
this.listener = listener;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.recycleritem_item, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 如果不使用这个方法,则获取添加/删除的Item的position会出错
int layoutPosition = holder.getLayoutPosition();
listener.onRecyclerViewItemClickListener(layoutPosition);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
listener.onRecyclerViewItemLongClickListener(holder.getLayoutPosition());
return true;
}
});
holder.tv.setText(data.get(position));
}
@Override
public int getItemCount() {
return data.size();
}
static class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public MyViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.item_tv);
}
}
public interface OnRecyclerViewItemOperationListener {
void onRecyclerViewItemClickListener(int position);
void onRecyclerViewItemLongClickListener(int position);
}
}
6、添加/删除Item:
添加、删除操作主要就是修改适配器绑定的数据源中的数据,加一项或删一项。在RecyclerView中需要注意的是,如果我们为RecyclerView设置了动画,就不能调用Adapter对象的notifyDataSetChanged()方法去更新数据源,因为如果调用notifyDataSetChanged()方法,就没有了动画效果。我们需要调用notifyItemInserted()方法来更新添加Item后的数据源,调用notifyItemRemoved()方法来更新删除Item后的数据源。
另外,在添加了新的Item之后,如果我们调用onBindViewHolder()方法参数中的position,就会出现新添加的Item的position不准确的问题,因此,我们需要使用holder.getLayoutPosition()方法来获取当前Item所在的位置。
最后贴一下我做的一个RecyclerView的小DEMO中的截屏,然后贴源码地址:
下面是码云上的源码地址,供大家参考。
【Android - MD】之RecyclerView的使用的更多相关文章
- Android 高级编程 RecyclerView 控件的使用
RecyclerView 是Android 新添加的一个用来取代ListView的控件,它的灵活性与可替代性比listview更好. 看一下继承关系: ava.lang.Object ↳ and ...
- Android学习之RecyclerView
RecyclerView是android-support-v7-21版本号中新增的一个Widget,官方介绍RecyclerView 是 ListView 的升级版本号,更加先进和灵活. 开发环境 - ...
- android开发学习 ------- RecyclerView多类型实例
实现RecyclerView多类型的实例:效果如下图所示 public class CarFragment extends Fragment{ private View view; private R ...
- Android最新组件RecyclerView,替代ListView
转载请注明出处:http://blog.csdn.net/allen315410/article/details/40379159 万众瞩目的android最新5.0版本号不久前已经正式公布了,对于我 ...
- Android控件RecyclerView的基本用法
Android控件RecyclerView的基本用法 转 https://www.jianshu.com/p/e71a4b73098f github: https://github.com/Cym ...
- Android教程2020 - RecyclerView使用入门
本文介绍RecyclerView的使用入门.这里给出一种比较常见的使用方式. Android教程2020 - 系列总览 本文链接 想必读者朋友对列表的表现形式已经不再陌生.手机上有联系人列表,文件列表 ...
- Android学习之RecyclerView初探究
•RecyclerView基本用法 RecyclerView是新增的控件,为了让 RecyclerView 在所有 Android 版本上都能使用; Android 团队将 RecyclerView ...
- Android Studio开发RecyclerView遇到的各种问题以及解决(一)
以前一直在用ListView,,,最近才看RecyclerView发现好强大.RecyclerView前提是Android版本在5.0以上,本人以前用的是eclipse只支持到4.4.索性就安装一个A ...
- 【Android - MD】之CoordinatorLayout的使用
CoordinatorLayout是Android 5.0新特性--Material Design中的一个布局控件,主要用来协调各个子视图之间的工作,也可以用来作为顶部布局.CoordinatorLa ...
随机推荐
- Git (2)
要使用Git首先遇到的问题是怎么把文件加到库中. 很简单. 新建一个目录,然后git init. 完成上述工作之后的唯一改动是在当前目录下生成了一个.git的子目录.这个子目录是一个集中的数据库,包含 ...
- WPF提示框效果
WPF提示框效果 1,新建WPF应用程序 2,添加用户控件Message 3,在Message中编写如下代码 <Border x:Name="border" BorderTh ...
- InstallShield:自己备份
LIST listData;//声明listData listData = ListCreate(STRINGLIST);//创建一个空的实际字符串或数字列表. //参数都是在上个界面中赋值,然后在下 ...
- Sharepoint 问题集锦 - 配置
错误 在sharepoint designer中编辑List的表单的时候,提示soap:Server服务器无法处理请求. ---> 值不在预期的范围内. 解释: 这个是由于我在本地测试的时候,使 ...
- [BZOJ 3129] [Sdoi2013] 方程 【容斥+组合数取模+中国剩余定理】
题目链接:BZOJ - 3129 题目分析 使用隔板法的思想,如果没有任何限制条件,那么方案数就是 C(m - 1, n - 1). 如果有一个限制条件是 xi >= Ai ,那么我们就可以将 ...
- GCD介绍(一): 基本概念和Dispatch Queue
什么是GCD? Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写.从基本功能上讲,GCD有点像NSOperationQueue,他们都允许 ...
- 网站性能优化— WebP 全方位介绍
谈到优化网站性能时,主要目标之一就是减少要发送到浏览器的数据量(即 payload).而当前,图片通常是页面构成中最耗费流量的部分,因此降低图片的大小是一个最为有效的优化网页前端性能的办法. 有很多工 ...
- Life Forms
poj3294:http://poj.org/problem?id=3294 题意:就是求n个串的中一个最大的子串,这个子串在超过n/2的串中出现. 题解:这是一道好题.首先一种解法就是用后缀数组来搞 ...
- 严重推荐一个免费开源数据库建模工具软件 --OpenSystemArchitect 4.0
嘿嘿,对于我这样的新手,这个工具还是很令人兴奋的. 真的是术业有专攻啊.关键还是免费开源 EXCEL,VISO,PPT,PS,CD,FREEHAND不是不可以,只是.人家还是专业点,方便点.. Ope ...
- Android开源项目发现---Spinner选择器与日历选择器篇(持续更新)
1. android-times-square Android日历部件 支持选取单个日期,多个日期,及日期区间段和对话框形式显示 项目地址:https://github.com/square/andr ...