天天看點

android v7相容包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則提供了一種更加靈活的方式。

谷歌現在使用的這種方法,它不在乎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);
           

總結為以下幾步

  1. 獲得RecyclerView引用
  2. 建立布局管理器并添加到RecyclerView中
  3. 建立擴充卡并添加到RecyclerView中
  4. 建立0到多個item裝飾器ItemDecoration,前提是有必要的話
  5. 建立ItemAnimator,如果有必要的話
  6. 建立0到多個監聽器并增加到RecyclerView中

基本源碼同上篇文章,源碼下載下傳位址http://download.csdn.net/detail/sbsujjbcy/8489667

本篇文章參考了http://www.grokkingandroid.com/first-glance-androids-recyclerview/