android v7相容包RecyclerView的使用(二),recyclerview
上篇文章 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則提供了一種更加靈活的方式。
Google現在使用的這種方法,它不在乎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都用到了適配器。
Google使用了RecyclerView的內部類Adapter代替了傳統的Adapter,所以在RecyclerView中,你不會見到類似SimpleCursorAdapter, ArrayAdapter這樣的適配器。
然而不幸的是Google並沒有提供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 參數看上去有點怪怪的,那麼為什麼不用一個傳回值代替它呢,既然Google這樣做了,必然是有意義的。因為這樣可以讓所有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,但是在內部它也並沒有怎麼對屬性進行操作。
@Overridepublic 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(0);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);
總結為以下幾步
基本源碼同上篇文章,源碼http://download.csdn.net/detail/sbsujjbcy/8489667
本篇文章參考了http://www.grokkingandroid.com/first-glance-androids-recyclerview/