美文网首页
Android编程权威指南(第二版)学习笔记(九)—— 第9章

Android编程权威指南(第二版)学习笔记(九)—— 第9章

作者: kniost | 来源:发表于2017-05-17 15:25 被阅读54次

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

    GitHub 地址:
    完成第九章

    1. 单例(SingleInstance)

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

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

    public class CrimeLab {
        //下面这个静态对象只会创建一次
        private static CrimeLab sCrimeLab;
        
        private List<Crime> 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<Crime> 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 的视图上。

    过程图示如下:


    这里写图片描述

    需要注意的是,相对于 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章

          本文链接:https://www.haomeiwen.com/subject/kapdxxtx.html