本章主要講述了 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.lxweimin.com/u/723da691aa42