Android编程权威指南(第二版)学习笔记(九)—— 第9章 使用 RecyclerView 显示列表

本章主要讲述了 RecyclerView 的基础使用,单例设计模式以及通过抽象的统一的 activity 来托管 fragment(以减少重复代码量)。

GitHub 地址:
完成第九章

1. 单例(SingleInstance)

单例是特殊的 JAVA 类,在创建实例的时候,一个单例类仅允许创建一个实例。应用能在内存里多久,单例就能存在多久,因此将对象列表保存在单例里的话,就能随时获取到数据,而不用管 activity 和 fragment 的生命周期怎么变化。不过当应用被从内存里移除的时候,单例对象就不复存在了。

要创建单例,需要创建一个带有私有构造方法及 get() 方法的类,如果实例已经存在了,get() 方法就直接返回它,如果还不存在,就需要调用构造方法创建它。书上的代码是这样的:

public class CrimeLab {
    //下面这个静态对象只会创建一次
    private static CrimeLab sCrimeLab;
    
    private List mCrimes;
    
    //程序的其他部分需要使用时,调用下列方法,当第一次使用的时候创建这个对象,如果不是第一次使用的时候就直接返回静态对象。
    public static CrimeLab get(Context context) {
        if (sCrimeLab == null) {
            sCrimeLab = new CrimeLab(context);
        }
        return sCrimeLab;
    }

    //私有的构造方法,只在 get 方法中使用
    private CrimeLab(Context context) {
        mCrimes = new ArrayList<>();
        //初始化数据的语句
        ………………
    }

    //由于对象只创建了一次,故而数据只有一份
    public List getCrimes() {
        return mCrimes;
    }

    public Crime getCrime(UUID id) {
        for (Crime crime : mCrimes) {
            if (crime.getId().equals(id)) {
                return crime;
            }
        }
        return null;
    }
}

单例能方便地控制模型层对象,由一个单例类来控制数据,所有的修改都由它处理,会使数据的一致性控制更加简便。

但是万事总有缺点,

  • 首先,单例无法做到持久的存储,应用的内存被回收时,单例就不复存在了。
  • 其次,单例还不利于单元测试。
  • 最后,单例还容易被滥用,需要注意的是有充足的理由时才使用单例模式存储共享数据。

2. 使用抽象 activity 托管 fragment

由于书中大部分 FragmentActivity 的是类似的,所以可以直接创建一个抽象的类用于被继承,简化代码。

回忆一下使用 fragment 的步骤:

  1. 在托管的 activity 的 onCreate() 方法中新建一个 FragmentManager 对象(getSupportFragmentManager() 方法或者 getFragmentManager() 方法)。
  2. 使用该对象的 findFragmentById() 方法找到放置 fragment 的位置。
  3. 如果 fragment 没有建立,就新建一个 fragment 对象,并使用 FragmentManager 对象的 beginTransaction().add().commit() 的连续方法将 fragment 事务提交到队列中

在这其中,只有新建 fragment 对象是与具体 fragment 有关的,那么我们可以将其写成一个抽象的函数:

protected abstract Fragment createFragment();

3. RecyclerView, Adapter 和 ViewHolder

对于一个列表,之前有 ListView,网格有 GridView,但要实现更加复杂的布局和功能,比如瀑布流的时候,就有些力不从心了。RecyclerView 是 Google 推出 Android 5.0 时一并推出的控件,其具有强大的功能和高度的解耦,有助于开发者实现更加多变具有拓展能力的布局。

3.1 RecyclerView 简介及工作原理

要使用 RecyclerView 显示视图,需要三样东西,即RecyclerView,Adapter, ViewHolder,它们的任务各不相同:

  • RecyclerView 是视图层对象,负责回收和定位屏幕上的 ViewHolder
  • ViewHolder 只负责容纳 View 视图
  • Adapter 是控制器对象,负责创建必要的 ViewHolder,从模型层获取数据并与 ViewHolder 绑定,然后提供给 RecyclerView 显示

RecyclerView 需要显示视图对象时,就会去找它的 Adapter,然后会有如下调用。

  1. 首先,调用 Adapter 的 getItemCount() 方法,RecyclerView 询问数组列表中包含多少个对象。
  2. 接着,调用 Adapter 的 createViewHolder(ViewGroup, int) 方法创建 ViewHolder 以及 ViewHolder 要显示的视图。
  3. 最后,RecyclerView 会传入 ViewHolder 及其位置,调用 onBindViewHolder(ViewHolder, int) 方法。Adapter 会找到目标位置的数据并用数据填充到 ViewHolder 的视图上。

过程图示如下:


Android编程权威指南(第二版)学习笔记(九)—— 第9章 使用 RecyclerView 显示列表_第1张图片
这里写图片描述

需要注意的是,相对于 onBindViewHolder(ViewHolder, int) 方法,createViewHolder(ViewGroup, int) 方法的调用并不频繁。一旦创建了够用的 ViewHolder,RecyclerVIew 就会停止调用 createViewHolder() 方法,然后通过回收旧的 ViewHolder 来节约时间和内存。

3.2 使用 RecyclerView

介绍了 RecyclerView 的各种细节,我们来看看它具体怎么使用吧。

3.2.1 添加 RecyclerView 依赖库

在 File - Project Structure 菜单项,选择 app 模块,然后单击 Dependencies 选项页,单击加号,找到并添加 recyclerview-v7 支持库。

3.2.2 在布局文件中使用 RecyclerView 并在 JAVA 代码中声明

示例 JAVA 代码如下:

mCrimeRecyclerView = (RecyclerView) view.findViewById(R.id.crime_recycler_view);
mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

RecyclerView 视图创建完成后,就立即转交给了 LayoutManager 对象。LayoutManager 实际上负责定位列表项和定义屏幕滚动行为,因此如果没有 LayoutManger 的支持,不仅 RecyclerView 无法工作,还会导致应用崩溃。在示例中使用的 LinearLayoutManager 是以竖直列表的方式展示列表项,内置的还有GridLayoutManager ,还有很多第三方的库可以使用。

3.2.3 实现 Adapter 和 ViewHolder

ViewHolder 需要做的事情很简单,就是将自定义的 view 中的组件找出来并绑定在这个 ViewHolder 的成员变量上。

比如定义了一个有标题和图片的 item,那么这个 Holder 可以这么写:

class ItemHolder extends RecyclerView.ViewHolder {
    
    public TextView mTitle;
    public ImageView mImg;
    
    public ItemHolder(View itemView) {
        super(itemView);

        mTitle = (TextView) itemView.findViewById(R.id.tv_item_title);
        mImg = (ImageView) itemView.findViewById(R.id.iv_item_img);
    }
}

如果有监听器的话,也可以写在构造函数中

对于 Adapter 来说,要做的事就更多了,我来一一梳理:

  • 从模型层获取数据
    一般在 Adapter 内部声明一个数据模型的成员变量,在 Adapter 的构造函数中进行初始化

  • 重写 ViewHolder 这个父类的三个方法

    • onCreateViewHolder(ViewGroup parent, int viewType)
      每当 RecyclerView 需要新的 View 视图来显示列表项的时候就会调用这个方法。在这其中,我们创建 View 视图,然后封装到 ViewHolder 中,此时并不需要向视图加载数据。
     //一个典型的 onCreateViewHolder 方法的内部
     LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
     View view = layoutInflater.inflate(R.layout.list_item, parent, false);
     return new ItemHolder(view);
    
  • onBindViewHolder(ItemHolder holder, int position)
    这个方法负责将 ViewHolder 的 View 视图和模型层的数据绑定起来。拿到 ViewHolder 和列表项在数据集中的索引位置后,我们通过索引位置找到要显示的数据进行绑定。绑定完毕后,刷新显示 View 视图。

//典型的 onBindViewHolder 方法内部
Data data = mDataList.get(position); 
// 注意上面的 mDataList 就是在 Adatper 的构造函数中初始化的 Adapter 的成员变量
holder.mTitle.setText(data.getTitle(position));
holder.mImg.setImageResource(data.getImgRes(position));
  • getItemCount()
    返回要展示的数据的数量,一般是数据集的 size

到此一个基本的 Adapter 就创建完了,在主程序中声明并初始化 Adapter,调用 RecyclerView 的 setAdapter 方法即可显示出列表了~


GitHub Page: kniost.github.io
:http://www.jianshu.com/u/723da691aa42

你可能感兴趣的:(Android编程权威指南(第二版)学习笔记(九)—— 第9章 使用 RecyclerView 显示列表)