本章主要讲述了 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 的步骤:
- 在托管的 activity 的 onCreate() 方法中新建一个 FragmentManager 对象(getSupportFragmentManager() 方法或者 getFragmentManager() 方法)。
- 使用该对象的 findFragmentById() 方法找到放置 fragment 的位置。
- 如果 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,然后会有如下调用。
- 首先,调用 Adapter 的 getItemCount() 方法,RecyclerView 询问数组列表中包含多少个对象。
- 接着,调用 Adapter 的 createViewHolder(ViewGroup, int) 方法创建 ViewHolder 以及 ViewHolder 要显示的视图。
- 最后,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