上篇文章 android v7相容包RecyclerView的使用(一)講了RecyclerView的最基本用法,現在開始挖掘更詳細的内容。
在RecyclerView的API中,有這樣一句話
A flexible view for providing a limited window into a large data set.
大緻意思就是:當有大量的資料顯示在一個有限大小的視窗上時,RecyclerView就是解決這種情況的一個靈活的View。
從以上描述可以看出RecyclerView的使用場景。如果我們有大量的同一類型的資料要顯示在螢幕上,而此時很有可能整個螢幕無法完全顯示所有的資料,這時候RecyclerView就是一個合适的選擇。當使用者上下滾動螢幕的時候,item的循環重複利用也在同時進行,當一個新的item進入可視範圍,必然有一個舊的item移出可視範圍,而這個被移出的item也就被循環重用。那麼循環重用item有什麼作用呢,其實他是很有用的一種方法,因為它節省了CPU資源和記憶體。
或許你會說我們用ListView用了很長的時間了,RecyclerView與我們以前的方式有什麼特别的地方呢,從前,我們使用listview的時候,顯示,循環重用以及其它的一些東西都有一定程度上的耦合性,現在RecyclerView則提供了一種更加靈活的方式。
谷歌現在使用的這種方法,它不在乎RecyclerView所看到的東西,也不在乎元素是否顯示在正确的地方,更不在乎每個元素是如何分隔的。RecyclerView做的事僅僅是是回收。是以得名RecyclerView。
下面是幾個和RecyclerView有關的最重要的類
Adapter:擴充卡,綁定資料集
ViewHolder:根據目前的資料儲存視圖
LayoutManager:布局管理器。決定item如何擺放
ItemDecoration:勉強了解為item裝飾器,可以美化item
ItemAnimator:動畫,當一個item被增加,删除或者重新擺放時,會有動畫效果。
ViewHolder
安卓官方已經推薦使用ViewHolder模式有很長一段時間了,因為它能在一定程度上大大提高效率,界面流暢性增加。RecyclerView的擴充卡是一個内部類,我們通過繼承它來擴充我們的子類,就像這樣。
public final static class ListItemViewHolder extends RecyclerView.ViewHolder {
TextView title;//item界面上的一個元素
TextView description;//item界面上的一個元素
//當然可以繼續增加這些元素
public ListItemViewHolder(View itemView) {
super(itemView);
//關聯引用
title= (TextView) itemView.findViewById(R.id.title);
description= (TextView) itemView.findViewById(R.id.description);
}
}
RecyclerView.Adapter
擴充卡完成兩個主要功能:負責建立基礎資料集和單個item布局之間的聯系。擴充卡是Android的一個重要部分之一。它被用在許多地方,比如ListView, AutoCompleteTextView, Spinner都用到了擴充卡。
谷歌使用了RecyclerView的内部類Adapter代替了傳統的Adapter,是以在RecyclerView中,你不會見到類似SimpleCursorAdapter, ArrayAdapter這樣的擴充卡。
然而不幸的是谷歌并沒有提供RecyclerView.Adapter的預設實作類,它是一個抽象類,是以我們必須去實作三個方法。
public VH onCreateViewHolder(ViewGroup parent, int viewType)
public void onBindViewHolder(VH holder, int position)
public int getItemCount()
VH是一個繼承自ViewHolder的泛型,在子類中必須提供具體的類型。最基本的擴充卡寫法如下
public class RecyclerViewAdapter extends
RecyclerView.Adapter
<RecyclerViewAdapter.ItemViewHolder> {
private List<DemoModel> items;
RecyclerViewAdapter(List<DemoModel> modelData) {
if (modelData == null) {
throw new IllegalArgumentException(
"modelData must not be null");
}
this.items = modelData;
}
@Override
public ItemViewHolder onCreateViewHolder(
ViewGroup viewGroup, int viewType) {
View itemView = LayoutInflater.
from(viewGroup.getContext()).
inflate(R.layout.item,viewGroup,false);
return new ItemViewHolder(itemView);
}
@Override
public void onBindViewHolder(
ItemViewHolder viewHolder, int position) {
DemoModel model = items.get(position);
viewHolder.title.setText(model.getTitle());
viewHolder.description.setText(model.getDescription());
}
@Override
public int getItemCount() {
return items.size();
}
public final static class ItemViewHolder
extends RecyclerView.ViewHolder {
// ... shown above in the ViewHolder section
}
}
RecyclerView.LayoutManager
布局管理器是RecyclerView最有意思的一個地方,它負責對所有子item進行布局,在最新的v7相容庫中,它有三個實作類,分别是LinearLayoutManager(線性布局),GridLayoutManager(a網格布局),StaggeredGridLayoutManager(流式布局),預設情況下,如果我們不設定布局管理器,将使用線性布局。我們可以通過繼承該類實作自己的布局管理器,由我們自己決定如何擺放item的内容,然而,當你去看上面的三個實作類的代碼時,你會發現這是多麼不容易的一件事。。。代碼好長。。。真的!布局管理器的具體使用方法将再開一篇部落格,這裡先介紹一下線性布局管理器的簡單使用。代碼如下
//執行個體化對象
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
//設定布局方向為豎直
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
//滾動到那一項
layoutManager.scrollToPosition(currPos);
//設定布局管理器
recyclerView.setLayoutManager(layoutManager);
RecyclerView.ItemDecoration
姑且我叫它為item裝飾器,使用ItemDecoration我們可以給item增加偏移量,增加item分割線,高亮等等。我們可以能增加很多個ItemDecoration,RecyclerView 會依次周遊所有的ItemDecoration并調用繪圖方法去完成裝飾。它有三個抽象方法
public void onDraw(Canvas c, RecyclerView parent)
public void onDrawOver(Canvas c, RecyclerView parent)
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)
在onDraw中繪制的内容可能會被item的内容遮擋,但是你在onDrawOver中繪制的内容會被繪制在item上方。我們可以使用偏移量對item進行簡單分割,但是如果你想顯示一條分割線,你就不得不去使用onDrawOver方法去顯式的繪制一條分隔線。
布局管理器會在測量階段調用getItemOffset方法,它與一個Rect類型的參數,outRect 參數看上去有點怪怪的,那麼為什麼不用一個傳回值代替它呢,既然谷歌這樣做了,必然是有意義的。因為這樣可以讓所有item去複用一個Rect對象是以節約了資源,這樣做不一定是最好的,但是的确可以有效節約資源。
有一點要值得注意的是,onDraw()/onDrawOver()方法并不會為每個item調用,它隻會被調用一次去繪制所有item的裝飾内容。是以我們必須在該方法中周遊所有item進行繪制。
上篇文章簡單提供了一個DividerItemDecoration的源碼,當然我們可以通過繼承ItemDecoration 去實作更加豐富的内容。
RecyclerView.ItemAnimator
ItemAnimator類主要是處理動畫效果,以下三種情況,會觸發該動畫。
- 一個item被添加到資料集中
- 一個item從資料集中被移除
- 一項在界面中位置被移動了
幸運的是,在v7相容包中提供了一個預設的實作類叫做DefaultItemAnimator,如果我們不設定,預設就會适應該類。
如果想讓動畫生效,很明顯android必須知道資料改變了。在早期的擴充卡中,我們需要調用notifyDataSetChanged方法通知android資料集改變了。但是現在不再适用了,這個方法會重繪所有可見item但是不會觸發任何動畫,我們必須調用類似以下的方法才能觸發動畫。
public final void notifyItemInserted(int position)
public final void notifyItemRemoved(int position)
Listeners
監聽器,RecyclerView中不再有OnItemClickListener和OnItemLongClickListener監聽器,但是我們可以使用OnItemTouchListener再配合手勢去識别這些事件,這意味着我們需要編寫更多的代碼去實作同樣的效果。
綜上所述
在布局檔案中使用,代碼如下
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
tools:listitem="@layout/item_demo_01"
/>
tools屬性會渲染目前布局,當我們切換到可視化編輯器的時候可以看到效果,當然也可以不寫,隻是看不到效果罷了。
我們可以看到,并沒有使用一些特别的屬性,實際上RecyclerView的屬性都是來自其父類ViewGroup。在RecyclerView中有一個方法用到了AttributeSet,這個方法就是generateLayoutParams,但是在内部它也并沒有怎麼對屬性進行操作。
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
if (mLayout == null) {
throw new IllegalStateException("RecyclerView has no LayoutManager");
}
return mLayout.generateLayoutParams(getContext(), attrs);
}
而在Activity中使用它也是很簡單的
//獲得控件引用
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
//執行個體化布局管理器并設定布局管理器,滾動到頂部
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
layoutManager.scrollToPosition();
recyclerView.setLayoutManager(layoutManager);
// 當item大小一樣時有利于優化,具體怎麼個優化法我也不知道。。
recyclerView.setHasFixedSize(true);
// 綁定擴充卡并提供資料集
List<DemoModel> items = new ArratList<DemoModel>();
//......這裡進行資料的初始化工作
adapter = new RecyclerViewDemoAdapter(items);
recyclerView.setAdapter(adapter);
//使用裝飾器,該類的源碼在上一篇文章中有提供
RecyclerView.ItemDecoration itemDecoration =
new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST);
//注意這裡是add而不是set,說明可以添加多個
recyclerView.addItemDecoration(itemDecoration);
// 下面這句話可以不寫,因為預設就是使用該類;
// 當我們自定義了動畫,才有必要去設定
recyclerView.setItemAnimator(new DefaultItemAnimator());
// 設定監聽器,需要配合手勢
//recyclerView.addOnItemTouchListener(this);
總結為以下幾步
- 獲得RecyclerView引用
- 建立布局管理器并添加到RecyclerView中
- 建立擴充卡并添加到RecyclerView中
- 建立0到多個item裝飾器ItemDecoration,前提是有必要的話
- 建立ItemAnimator,如果有必要的話
- 建立0到多個監聽器并增加到RecyclerView中
基本源碼同上篇文章,源碼下載下傳位址http://download.csdn.net/detail/sbsujjbcy/8489667
本篇文章參考了http://www.grokkingandroid.com/first-glance-androids-recyclerview/