Android App开发中使用RecyclerView替代ListView的实践

RecyclerView是Android 5.0的新特性,可以直接代替ListView与GridView,并且能够实现瀑布流的布局,感觉RecyclerView使用的好处就是它不关心布局,只关心资源的回收与复用,正因为如此,RecyclerView中将ViewHolder进行了单独的编写,这也正是google所不断提倡的,另外,RecyclerView能够更简单的实现布局的转换。
新的视图类RecyclerView,它被用来代替ListView以及GridView,提供更为高效的回收复用机制,同时实现管理与视图的解耦合。RecyclerView的特点:
1、RecyclerView不关心布局,需要使用setLayoutManager进行设置布局。
2、RecyclerView不关心分割线,因此分割线需要我们自己想办法设置。
3、RecyclerView不关心Item的点击事件与动画效果,需要自己编写接口进行监听。
4、RecyclerView仅关注View的回收与复用。
相关的类:
1、RecyclerView.Adapter:托管数据集合,为每个Item创建视图;
2、RecyclerView.ViewHolder:承载Item视图的子视图;
3、RecyclerView.LayoutManager:负责Item视图的布局;
4、RecyclerView.ItemDecoration:为每个Item视图添加分割线;
5、RecyclerView.ItemAnimator:负责添加、删除数据时的动画效果;

RecyclerView代替ListView的使用示例
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个方法:

1、onCreateViewHolder()

2、onBindViewHolder()

3、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;

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;

  }

对比后可以发现:

1、旧的写法中Line5~Line12+Line28部分的代码其实起到的作用相当于新的写法的onCreateViewHolder();

2、旧的写法中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的效果就实现了。

(0)

相关推荐

  • RecyclerView的使用之多种Item加载布局

    本文给大家介石介绍下如何利用RecyclerView实现多Item布局的加载,多Item布局的加载的意思就是在开发过程中List的每一项可能根据需求的不同会加载不同的Layout. 下面给大家展示下演示效果图: * 图片资源版权归属于Facebook dribbble RecyclerView实现加载不同的Layout的核心就是在Adapter的onCreateViewHolder里面去根据需求而加载不同的布局. 具体的实现步骤:(以Android Studio作为开发工具) 1:Gradle配

  • Android应用开发中RecyclerView组件使用入门教程

    RecyclerView是一种列表容器, 发布很久了, 才想起来写点什么. RecyclerView相比于ListView, 在回收重用时更具有灵活性, 也就是低耦合, 并且提供了扩展. 加载多个视图时, 应该多用RecyclerView代替ListView. 那么我们来看看这东西应该怎么用? 比如生成一个瀑布流的视图. 首先我们从一个HelloWorld写起, 看看如何构建一个RecyclerView. 1. 依赖库 Gradle配置, 添加Recycler库 compile 'com.and

  • Android使用RecyclerView实现自定义列表、点击事件以及下拉刷新

    Android使用RecyclerView 1. 什么是RecyclerView RecyclerView 是 Android-support-v7-21 版本中新增的一个 Widgets,官方对于它的介绍则是:RecyclerView 是 ListView 的升级版本,更加先进和灵活. 简单来说就是:RecyclerView是一种新的视图组,目标是为任何基于适配器的视图提供相似的渲染方式.它被作为ListView和GridView控件的继承者,在最新的support-V7版本中提供支持. 2.

  • 使用RecyclerView添加Header和Footer的方法

    RecyclerView与ListView原理是类似的:都是仅仅维护少量的View并且可以展示大量的数据集.RecyclerView用以下两种方式简化了数据的展示和处理: 使用LayoutManager来确定每一个item的排列方式. 为增加和删除项目提供默认的动画效果. RecyclerView虽然作为ListView的替代者有着较好的性能提升,但是ListView的一些常用功能却没有提供,比如我们平时会经常用到的addHeaderView,addFooterView,既然RecyclerVi

  • 将替代ListView的RecyclerView 的使用详解(一)

    RecyclerView 是 android-support-v7-21 版本中新增的一个 Widgets, 还有一个 CardView 会在下次介绍使用.官方介绍 RecyclerView 是 ListView 的升级版本,更加先进和灵活.我们写一个简单的实例例,来看一下究竟有多先进和灵活. build.gradle 配置 android { compileSdkVersion 'android-L' buildToolsVersion "20.0.0" defaultConfig

  • Android中使用RecyclerView实现下拉刷新和上拉加载

    推荐阅读:使用RecyclerView添加Header和Footer的方法                       RecyclerView的使用之HelloWorld RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍如何为RecyclerView添加下拉刷新和上拉加载,过去在ListView当中添加下拉刷新和上拉加载是非常方便的利用addHeaderView和addFooterVie

  • Android App开发中使用RecyclerView实现Gallery画廊的实例

    什么是RecyclerView         RecyclerView是Android 5.0 materials design中的组件之一,相应的还有CardView.Palette等.看名字我们就能看出一点端倪,没错,它主要的特点就是复用.我们知道,Listview中的Adapter中可以实现ViewHolder的复用.RecyclerView提供了一个耦合度更低的方式来复用ViewHolder,并且可以轻松的实现ListView.GridView以及瀑布流的效果. RecyclerVie

  • Android App开发中使用RecyclerView替代ListView的实践

    RecyclerView是Android 5.0的新特性,可以直接代替ListView与GridView,并且能够实现瀑布流的布局,感觉RecyclerView使用的好处就是它不关心布局,只关心资源的回收与复用,正因为如此,RecyclerView中将ViewHolder进行了单独的编写,这也正是google所不断提倡的,另外,RecyclerView能够更简单的实现布局的转换. 新的视图类RecyclerView,它被用来代替ListView以及GridView,提供更为高效的回收复用机制,同

  • Android App开发中ViewPager组件的入门使用教程

    首先让大家有个全局的认识,直接上个项目,看看仅仅通过这几行代码,竟然就能完成如此强悍的功能.下篇再仔细讲讲为什么要这么写. 效果图: 实现了三个view间的相互滑动 第一个VIEW向第二个VIEW滑动: 第二个VIEW向第三个VIEW滑动: 一.新建项目,引入ViewPager控件 ViewPager.它是google SDk中自带的一个附加包的一个类,可以用来实现屏幕间的切换. 1.在主布局文件里加入 <RelativeLayout xmlns:android="http://schem

  • Android app开发中的Fragment入门学习教程

    在Android3.0上开始引入了一个新概念叫Fragment.它有自己的布局文件,可以作为组件排布,也可以相互组合去实现不同的布局显示.使用Fragment可以重复利用代码,并且可以满足不同设备尺寸的需求.Fragment不能单独存在,只能存在于Activity中,而一个Activity可以拥有多个Fragment.很重要的一点是,Fragment可以和Activity中的其它组件一起使用,无需重写所有Activity的接口.所以使用Fragment就可以这样来完成上例中"主界面-详细界面&q

  • 浅谈Android app开发中Fragment的Transaction操作

    在Android中,对Fragment的操作都是通过FragmentTransaction来执行.而从Fragment的结果来看,FragmentTransaction中对Fragment的操作大致可以分为两类: 显示:add() replace() show() attach() 隐藏:remove() hide() detach() 对于每一组方法,虽然最后产生的效果类似,但方法背后带来的副作用以及对Fragment的生命周期的影响都不尽相同. add() vs. replace() 只有在

  • Android App开发中RecyclerView控件的基本使用教程

    概述 RecyclerView出现已经有一段时间了,相信大家肯定不陌生了,大家可以通过导入support-v7对其进行使用. 据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实这样功能的控件我们并不陌生,例如:ListView.GridView. 那么有了ListView.GridView为什么还需要RecyclerView这样的控件呢?整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecor

  • 实例讲解Android app开发中ListView的基本使用及优化

    一.直接使用ListView组件创建 1.直接在XML中创建ListView用entries属性附上一个数组资源 其中divider属性是设置分割线可以使用颜色和drawable资源分割 <ListView android:id="@+id/listView1" android:layout_width="match_parent" android:layout_height="wrap_content" android:divider=&

  • 深入解析Android App开发中Context的用法

    Context在开发Android应用的过程中扮演着非常重要的角色,比如启动一个Activity需要使用context.startActivity方法,将一个xml文件转换为一个View对象也需要使用Context对象,可以这么说,离开了这个类,Android开发寸步难行,对于这样一个类,我们又对他了解多少呢.我就说说我的感受吧,在刚开始学习Android开发时,感觉使用Context的地方一直就是传入一个Activity对象,久而久之感觉只要是Context的地方就传入一个Activity就行

  • Android App开发中Gradle构建过程的配置方法

    在build文件中使用了Android或者Java插件之后就会自动创建一系列可以运行的任务. Gradle中有如下一下默认约定的任务: 1. assemble 该任务包含了项目中的所有打包相关的任务,比如java项目中打的jar包,Android项目中打的apk 2. check 该任务包含了项目中所有验证相关的任务,比如运行测试的任务 3. build 该任务包含了assemble和check 4. clean 该任务会清空项目的所有的输出,删除所有在assemble任务中打的包 assemb

  • Android App开发中创建Fragment组件的教程

    你可以认为Fragment作为Activity的一个模块部分,有它自己的生命周期,获取它自己的事件,并且你可以在Activity运行的时候添加或者移除它(有点像你可以在不同的Activity中重用的一个"子Activity").这节课程讲述如何使用Support Library继承Fragment类,所以你的应用程序仍然是兼容运行的系统版本低于Android1.6的设备. 注意:如果你决定你的应用要求的最低的API级别是11或者更高,你不需要使用Support Library,反而能使

随机推荐