[Android]使用RecyclerView替代ListView(一)
以下内容为原创,欢迎转载,转载请注明
来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4232560.html
RecyclerView是一个比ListView更灵活的一个控件,以后可以直接抛弃ListView了。具体好在哪些地方,往下看就知道了。
首先我们来使用RecyclerView来实现ListView的效果,一个滚动列表,先看下效果图(除了有动画之外,没什么特别--):

每个item的布局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycler_view_test_item_person_view"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="15dp"
android:background="#aabbcc"
>
<TextView
android:id="@+id/recycler_view_test_item_person_name_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:background="#ccbbaa"
/>
<TextView
android:id="@+id/recycler_view_test_item_person_age_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:background="#aaccbb"
android:textSize="15sp"
/>
</LinearLayout>
item的布局很简单,只有两个TextView,一个用来显示名字,一个用来显示年龄。
Person的实体类就不贴代码了,两个属性:名字和年龄。
然后需要使用到RecyclerView,所以需要把support v7添加到class path,并在布局中添加该控件:
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view_test_rv"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#bbccaa"
/>
然后在onCreate中:
recyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(layoutManager);
initData();
adapter = new PersonAdapter(personList);
adapter.setOnRecyclerViewListener(this);
recyclerView.setAdapter(adapter);
如上述代码:
Line1: 使RecyclerView保持固定的大小,这样会提高RecyclerView的性能。
Line3: LinearLayoutManager,如果你需要显示的是横向滚动的列表或者竖直滚动的列表,则使用这个LayoutManager。显然,我们要实现的是ListView的效果,所以需要使用它。生成这个LinearLayoutManager之后可以设置他滚动的方向,默认竖直滚动,所以这里没有显式地设置。
Line6: 初始化数据源。
Line7~9: 跟ListView一样,需要设置RecyclerView的Adapter,但是这里的Adapter跟ListView使用的Adapter不一样,这里的Adapter需要继承RecyclerView.Adapter,需要实现3个方法:
- onCreateViewHolder()
- onBindViewHolder()
- getItemCount()
直接看代码:
package com.wangjie.helloandroid.sample.recycler.person; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.wangjie.androidbucket.log.Logger;
import com.wangjie.helloandroid.R; import java.util.List; /**
* Author: wangjie
* Email: tiantian.china.2@gmail.com
* Date: 1/17/15.
*/
public class PersonAdapter extends RecyclerView.Adapter {
public static interface OnRecyclerViewListener {
void onItemClick(int position);
boolean onItemLongClick(int position);
} private OnRecyclerViewListener onRecyclerViewListener; public void setOnRecyclerViewListener(OnRecyclerViewListener onRecyclerViewListener) {
this.onRecyclerViewListener = onRecyclerViewListener;
} private static final String TAG = PersonAdapter.class.getSimpleName();
private List<Person> list; public PersonAdapter(List<Person> list) {
this.list = list;
} @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
Logger.d(TAG, "onCreateViewHolder, i: " + i);
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_view_test_item_person, null);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
view.setLayoutParams(lp);
return new PersonViewHolder(view);
} @Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
Logger.d(TAG, "onBindViewHolder, i: " + i + ", viewHolder: " + viewHolder);
PersonViewHolder holder = (PersonViewHolder) viewHolder;
holder.position = i;
Person person = list.get(i);
holder.nameTv.setText(person.getName());
holder.ageTv.setText(person.getAge() + "岁");
} @Override
public int getItemCount() {
return list.size();
} class PersonViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener
{
public View rootView;
public TextView nameTv;
public TextView ageTv;
public int position; public PersonViewHolder(View itemView) {
super(itemView);
nameTv = (TextView) itemView.findViewById(R.id.recycler_view_test_item_person_name_tv);
ageTv = (TextView) itemView.findViewById(R.id.recycler_view_test_item_person_age_tv);
rootView = itemView.findViewById(R.id.recycler_view_test_item_person_view);
rootView.setOnClickListener(this);
rootView.setOnLongClickListener(this);
} @Override
public void onClick(View v) {
if (null != onRecyclerViewListener) {
onRecyclerViewListener.onItemClick(position);
}
} @Override
public boolean onLongClick(View v) {
if(null != onRecyclerViewListener){
return onRecyclerViewListener.onItemLongClick(position);
}
return false;
}
} }
如上代码所示:
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i)
这个方法主要生成为每个Item inflater出一个View,但是该方法返回的是一个ViewHolder。方法是把View直接封装在ViewHolder中,然后我们面向的是ViewHolder这个实例,当然这个ViewHolder需要我们自己去编写。直接省去了当初的convertView.setTag(holder)和convertView.getTag()这些繁琐的步骤。
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i)
这个方法主要用于适配渲染数据到View中。方法提供给你了一个viewHolder,而不是原来的convertView。
对比下以前的写法就一目了然了:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(null == convertView){
holder = new ViewHolder();
LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.item, null);
holder.btn = (Button) convertView.findViewById(R.id.btn);
holder.tv = (TextView) convertView.findViewById(R.id.tv);
holder.iv = (TextView) convertView.findViewById(R.id.iv); convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
final HashMap<String, Object> map = list.get(position); holder.iv.setImageResource(Integer.valueOf(map.get("iv").toString()));
holder.tv.setText(map.get("tv").toString()); holder.btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, map.get("btn").toString(), Toast.LENGTH_SHORT).show();
}
}); return convertView;
} class ViewHolder{
Button btn;
ImageView iv;
TextView tv; }
对比后可以发现:
旧的写法中Line5~Line12+Line28部分的代码其实起到的作用相当于新的写法的onCreateViewHolder();
旧的写法中Line14~Line26部分的代码其实起到的作用相当于新的写法的onBindViewHolder();
既然是这样,那我们就把原来相应的代码搬到对应的onCreateViewHolder()和onBindViewHolder()这两个方法中就可以了。
因为RecyclerView帮我们封装了Holder,所以我们自己写的ViewHolder就需要继承RecyclerView.ViewHolder,只有这样,RecyclerView才能帮你去管理这个ViewHolder类。
既然getView方法的渲染数据部分的代码相当于onBindViewHolder(),所以如果调用adapter.notifyDataSetChanged()方法,应该也会重新调用onBindViewHolder()方法才对吧?实验后,果然如此!
除了adapter.notifyDataSetChanged()这个方法之外,新的Adapter还提供了其他的方法,如下:
public final void notifyDataSetChanged()
public final void notifyItemChanged(int position)
public final void notifyItemRangeChanged(int positionStart, int itemCount)
public final void notifyItemInserted(int position)
public final void notifyItemMoved(int fromPosition, int toPosition)
public final void notifyItemRangeInserted(int positionStart, int itemCount)
public final void notifyItemRemoved(int position)
public final void notifyItemRangeRemoved(int positionStart, int itemCount)
基本上看到方法的名字就知道这个方法是干嘛的了,
第一个方法没什么好讲的,跟以前一样。
notifyItemChanged(int position),position数据发生了改变,那调用这个方法,就会回调对应position的onBindViewHolder()方法了,当然,因为ViewHolder是复用的,所以如果position在当前屏幕以外,也就不会回调了,因为没有意义,下次position滚动会当前屏幕以内的时候同样会调用onBindViewHolder()方法刷新数据了。其他的方法也是同样的道理。
public final void notifyItemRangeChanged(int positionStart, int itemCount),顾名思义,可以刷新从positionStart开始itemCount数量的item了(这里的刷新指回调onBindViewHolder()方法)。
public final void notifyItemInserted(int position),这个方法是在第position位置被插入了一条数据的时候可以使用这个方法刷新,注意这个方法调用后会有插入的动画,这个动画可以使用默认的,也可以自己定义。
public final void notifyItemMoved(int fromPosition, int toPosition),这个方法是从fromPosition移动到toPosition为止的时候可以使用这个方法刷新
public final void notifyItemRangeInserted(int positionStart, int itemCount),显然是批量添加。
public final void notifyItemRemoved(int position),第position个被删除的时候刷新,同样会有动画。
public final void notifyItemRangeRemoved(int positionStart, int itemCount),批量删除。
这些方法分析完之后,我们来实现一个点击一个按钮,新增一条数据,长按一个item,删除一条数据的场景。
以下是新增一条数据的代码:
Person person = new Person(i, "WangJie_" + i, 10 + i);
adapter.notifyItemInserted(2);
personList.add(2, person);
adapter.notifyItemRangeChanged(2, adapter.getItemCount());
如上代码:
Line2:表示在position为2的位置,插入一条数据,这个时候动画开始执行。
Line3: 表示在数据源中position为2的位置新增一条数据(其实这个才是真正的新增数据啦)。
Line4: 为什么要刷新position为2以后的数据呢?因为,在position为2的位置插入了一条数据后,新数据的position变成了2,那原来的position为2的应该变成了3,3的应该变成了4,所以2以后的所有数据的position都发生了改变,所以需要把position2以后的数据都要刷新。理论上是这样,但是实际上刷新的数量只有在屏幕上显示的position为2以后的数据而已。如果这里使用notifyDataSetChanged()来刷新屏幕上显示的所有item可以吗?结果不会出错,但是会有一个问题,前面调用了notifyItemInserted()方法后会在执行动画,如果你调用notifyDataSetChanged()刷新屏幕上显示的所有item的话,必然也会刷新当前正在执行动画的那个item,这样导致的结果是,前面的动画还没执行完,它马上又被刷新了,动画就看不见了。所以只要刷新2以后的item就可以了。
看了RecyclerView的api,发现没有setOnItemClickListener--,所以还是自己把onItemClick从Adapter中回调出来吧。这个很简单,就像上面PersonAdaper中写的OnRecyclerViewListener那样。
长按删除的代码如下:
adapter.notifyItemRemoved(position);
personList.remove(position);
adapter.notifyItemRangeChanged(position, adapter.getItemCount());
代码跟之前插入的代码基本一致。先通知执行动画,然后删除数据源中的数据,然后通知position之后的数据刷新就可以了。
这样ListView的效果就实现了。
示例代码:
https://github.com/wangjiegulu/RecyclerViewSample
[Android]使用RecyclerView替代ListView(二):
http://www.cnblogs.com/tiantianbyconan/p/4242541.html
[Android]使用RecyclerView替代ListView(三):
http://www.cnblogs.com/tiantianbyconan/p/4268097.html
[Android]使用RecyclerView替代ListView(一)的更多相关文章
- [Android]使用RecyclerView替代ListView(三)
以下内容为原创,转载请注明: 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4268097.html 这次来使用RecyclerView实现Pinn ...
- [Android]使用RecyclerView替代ListView(二)
以下内容为原创,转载请注明: 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4242541.html 以前写过一篇“[Android]使用Adapte ...
- [Android]使用RecyclerView替代ListView(四:SeizeRecyclerView)
以下内容为原创,欢迎转载,转载请注明 来自天天博客:<> [Android]使用RecyclerView替代ListView(四:SeizeRecyclerView) 在RecyclerV ...
- Android笔记——RecyclerView替代ListView
ListView是常用列表控件,但设置Adapter时自定义代码较为复杂,因此Android3.0后,增加RecyclerView替代ListView RecyclerView没有提供OnItemCl ...
- Android最新组件RecyclerView,替代ListView
转载请注明出处:http://blog.csdn.net/allen315410/article/details/40379159 万众瞩目的android最新5.0版本号不久前已经正式公布了,对于我 ...
- 浅谈RecyclerView(完美替代ListView,GridView)
Android RecyclerView 是Android5.0推出来的,导入support-v7包即可使用. 个人体验来说,RecyclerView绝对是一款功能强大的控件. 首先总结下Recycl ...
- Android控件RecyclerView与ListView的异同
在我的一篇介绍Android新控件RecyclerView的博客(Android L新控件RecyclerView简介)中,一个读者留言说RecyclerView跟ListView之间好像没有什么不同 ...
- 将替代ListView的RecyclerView 的使用(一)
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/by317966834/article/details/36205923 RecyclerView 是 ...
- Android RecyclerView与ListView比较
RecyclerView 概述 RecyclerView 集成自 ViewGroup .RecyclerView是Android-support-V7版本中新增的一个Widgets,官方对于它的介绍是 ...
随机推荐
- Windows下elasticsearch插入数据报错!
按照官方文档操作,但是windows下有些不同,它不认识单引号',因此如果这样操作,就会报错: C:\Users\neusoft>curl localhost:9200/b1/b2/1 -d { ...
- HTML5实现3D和2D可视化QuadTree四叉树碰撞检测
QuadTree四叉树顾名思义就是树状的数据结构,其每个节点有四个孩子节点,可将二维平面递归分割子区域.QuadTree常用于空间数据库索引,3D的椎体可见区域裁剪,甚至图片分析处理,我们今天介绍的是 ...
- OpenStack虚拟云桌面在携程呼叫中心的应用
编者:本文为刘科在第六期[携程技术微分享]中的分享内容.在携程技术中心(微信号ctriptech)微信后台回复[云桌面],可加入微信交流群,和关注云桌面的小伙伴一起玩耍~ 刘科,携程系统研发云平台桌面 ...
- Html5离线缓存详细讲解
离线缓存是Html5新特性之一,简单理解就是第一次加载后将数据缓存,在没有清除缓存前提下,下一次没有网络也可以加载,用在静态数据的网页或游戏比较好用.当然,Html5新的特性都不是所有浏览器都能支持的 ...
- 开源通信(C#)__
GitHub中的README文件,使用markdown格式的文件. 下面简单介绍下相关语法: 大标题: =================================== 大标题一般显示工程名,你 ...
- 介绍开源的.net通信框架NetworkComms框架 源码分析(十一)PacketBuilder
原文网址: http://www.cnblogs.com/csdev Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是 ...
- C#/.NET 基础学习
初识C# C#是微软公司发布的面向对象的.运行于.NET Framework之上的高级程序设计语言.与Java有着明显不同,借鉴Delphi的特点,与COM(组件对象模型)直接集成,是微软公司 .NE ...
- APP Widget的开发
Android中经常用到APP Widget,如时钟,天气预报等. 长按屏幕,在弹出的对话框中选择“窗口小部件”,然后就列出了可选择的小部件,这些小部件就是APP Widget. 本文开发一个APP ...
- android版高仿淘宝客户端源码V2.3
android版高仿淘宝客户端源码V2.3,这个版本我已经更新到2.3了,源码也上传到源码天堂那里了,大家可以看一下吧,该应用实现了我们常用的购物功能了,也就是在手机上进行网购的流程的,如查看产品(浏 ...
- 阿里前端框架Alice是个不错的选择
BootStrap虽然用户群体广大,其整体风格尽管有不少skin可选,但以国情来看还是不好看. 阿里开源的前端框架,个人觉得还是很不错,Alice处处透着支付宝中界面风格的气息,电商感挺强. 以下内容 ...