1. 程式人生 > >Android最新元件RecyclerView,替代ListView

Android最新元件RecyclerView,替代ListView

Android最新元件RecyclerView,替代ListView

時間 2014-10-22 20:08:48 CSDN部落格

原文  http://blog.csdn.net/allen315410/article/details/40379159

主題 RecyclerView ListView

轉載請註明出處: http://blog.csdn.net/allen315410/article/details/40379159

萬眾矚目的android最新5.0版本不久前已經正式釋出了,對於我這樣對新事物不感冒的人來說,自然也是會關注的,除了新的android5.0帶來的新的UI設計和使用者體驗之外,最讓android程式設計師感興趣的是5.0版本的sdk和一大堆新的API。5.0據說是額外增加或者修改了5000個API,新增了一些新的元件,下面介紹的RecyclerView就是其中之一,有人說Google設計出的RecyclerView是為了替代一直常用的ListView的,所以既然如此,我們就沒理由不看看這個“傳說”中的RecyclerView是怎麼使用的了。

RecyclerView簡介

該RecyclerView widget是一種更先進的柔性版的ListView。這個小工具是一個容器,用於顯示,能非常有效地維護了意見數量有限,滾動大的資料集。使用 RecyclerView當你擁有的資料的集合,它的元素在執行時改變基於使用者行為和網路事件的小部件。

該RecyclerView類簡化,提供顯示和處理大資料集:

定位專案佈局管理器

預設的動畫為公用項的操作,例如刪除或增加的專案

您還可以在自定義的佈局管理器和動畫的靈活性RecyclerView部件。

要使用RecyclerView小部件,你必須指定一個介面卡和一個佈局管理器。要建立一個介面卡,擴充套件RecyclerView.Adapter類。實施的細節取決於你的資料集的具體情況和意見的型別。欲瞭解更多資訊,請參見示例如下。

佈局管理器 A的內部位置的專案意見RecyclerView,並確定何時再利用專案的看法不再對使用者可見。重用(或回收)的圖,佈局管理器可能會問介面卡與資料集不同的元素替換檢視的內容。以這種方式回收的觀點提高通過避免產生不必要的檢視或執行昂貴效能findViewById()的查詢。

RecyclerView提供這些內建的佈局管理器:

LinearLayoutManager 顯示在垂直或水平滾動列表項。

GridLayoutManager 顯示在網格中的專案。

StaggeredGridLayoutManager 顯示了交錯網格專案。

要建立自定義佈局管理器,擴充套件RecyclerView.LayoutManager類。

動畫

動畫的新增和刪除專案中預設啟用的RecyclerView。要自定義這些動畫,延長 RecyclerView.ItemAnimator類,並使用RecyclerView.setItemAnimator() 方法。

以上內容來自 Google官方文件 的翻譯,翻譯比較生澀(我這英文水平,哎~~對付自己還行),下面直接上一個Demo看看具體的用法。

RecyclerView的用法

下面簡單介紹製作一個小Demo,來一步步分析一下RecyclerView的用法。首先說明一下,RecyclerView是android.support.v7包下提供的元件,所以需要使用RecyclerView時,需要下載這個包,由於我已經將SDK升級到最新版本——API21,所以很容易在/sdk/extras/android/support/v7/appcompat/libs目錄下找到這個jar以及原始碼,建議先升級sdk,再動手做!實在不想動手的下載的,點選博文下方的連結下載原始碼,原始碼裡有RecyclerView的JAR包。

主介面佈局,activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:layout_centerVertical="true"
        android:background="@android:color/transparent"
        android:scrollbars="none" />

</RelativeLayout>

Item佈局,item_recyclerview.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="120dp"
    android:layout_height="120dp" >

    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:scaleType="centerCrop" />

</RelativeLayout>

RecyclerView的資料介面卡,MyAdapter.java

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private int[] mDataset; // 外面傳入的資料

    public static class ViewHolder extends RecyclerView.ViewHolder {

        ImageView mImageView;

        // TODO Auto-generated method stub
        public ViewHolder(View v) {
            super(v);
        }

    }

    public MyAdapter(int[] mDataset) {
        this.mDataset = mDataset;
    }

    /**
     * 獲取總的條目數量
     */
    @Override
    public int getItemCount() {
        // TODO Auto-generated method stub
        return mDataset.length;
    }

    /**
     * 建立ViewHolder
     */
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // TODO Auto-generated method stub
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycleview, parent, false);
        ViewHolder holder = new ViewHolder(v);
        holder.mImageView = (ImageView) v.findViewById(R.id.iv_image);
        return holder;
    }

    /**
     * 將資料繫結到ViewHolder上
     */
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // TODO Auto-generated method stub
        holder.mImageView.setImageResource(mDataset[position]);
    }
}

從上面可以看出這個資料介面卡跟ListView用的BaseAdapter上比,已經發生了很大的變化。首先資料介面卡需要繼承RecyclerView.Adapter<VH>類,該類是個泛型類,泛型型別也是ViewHolder,這個ViewHolder毋庸置疑就是實現元件複用的,Google已經幫我們定義好了,在RecyclerView裡是個內部類,但是具體實現還是扔給android App開發者去實現,需要在介面卡類建立一個內部類,並且繼承RecyclerView,ViewHolder,在這個內部類裡面定義出Item佈局上所有需要複用的元件,最後將這個內部類作為泛型傳遞給RecyclerView.Adapter<VH>,實現需要複寫的3個方法:

getItemCount() 獲取Item的總數。

onCreateViewHolder(ViewGroup parent, int viewType) 建立ViewHolder。

onBindViewHolder(ViewHolder holder, int position) 將資料繫結到ViewHolder。

與ListView資料適配的對比

ListView裡面有個getView()方法返回的View是Item的佈局,那麼這個RecyclerView的Item的佈局在哪控制?其實是這樣的,RecyclerView對ViewHolder也進行了一定的封裝,我們建立的ViewHolder必須繼承RecyclerView.ViewHolder,這個RecyclerView.ViewHolder的構造時必須傳入一個View,這個View相當於我們ListView的getView()中的convertView (即:我們需要inflate的item佈局需要傳入)。

還有一點,ListView中convertView是複用的,在RecyclerView中,是把ViewHolder作為快取的單位了,然後convertView作為ViewHolder的成員變數保持在ViewHolder中,也就是說,假設沒有螢幕顯示10個條目,則會建立10個ViewHolder快取起來,每次複用的是ViewHolder,所以他把getView這個方法變為了onCreateViewHolder。

最後,就在Activity裡使用這個RecyclerView,MainActivity.java

public class MainActivity extends Activity {

    /** RecyclerView物件 */
    private RecyclerView recyclerView;
    /** 圖片資源 */
    private int[] mDataset;
    /** 資料介面卡 */
    private MyAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        // 初始化圖片資料
        mDataset = new int[] { R.drawable.a, R.drawable.b, //
                R.drawable.c, R.drawable.d, R.drawable.e, //
                R.drawable.f, R.drawable.g, R.drawable.h, R.drawable.i };
        // 設定佈局管理器
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        recyclerView.setLayoutManager(linearLayoutManager);
        // 設定介面卡
        mAdapter = new MyAdapter(mDataset);
        recyclerView.setAdapter(mAdapter);
    }
}

在MainActivity使用RecyclerView如同使用ListView一樣的簡單,唯一不同的地方就是,需要給RecyclerView設定一個佈局管理器,Google為我們提供了3種不同的佈局管理(詳細請看最上面的簡介),這裡我使用的LinearLayoutmanager,並且設定佈局為水平顯示。 好了,關於RecyclerView的基本用法講完了,那個關於RecyclerView的另外兩個佈局管理器就暫時不說了,大同小異。下面是執行效果圖

為RecyclerView設定事件回撥

再使用RecyclerView元件時,發現了一個令人“痛心疾首”的問題:RecyclerView居然沒有點選Item的事件監聽設定,類似於ListView中起碼有個setOnItemClickListener方法,用於監聽Item點選並作出相應的邏輯處理。但是翻遍了RecyclerView的API,都沒有發現這個或者類似這個功能的方法可用,這不得不說是個“悲劇”,還聽說這個是為了替代ListView的,看來並不是這樣的,請Google出來解釋解釋啊!

好了,Google應該近期是不會解釋的了,但是我們得自己想辦法解決這個問題,就是為RecyclerView新增一個回撥函式,這個倒不難吧!實在無法領會的,可以看看我前面的那篇博文, Android自定義元件——仿ios滑動按鈕 ,裡面有一些相關的用法。下面是我在Adapter裡新增的回撥函式。

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private int[] mDataset; // 外面傳入的資料

    /**
     * Item的回撥介面
     * 
     */
    public interface OnItemClickListener {
        void onItemClickListener(View view, int position);
    }

    private OnItemClickListener listener; // 點選Item的回撥物件

    /**
     * 設定回撥監聽
     * 
     * @param listener
     */
    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {

        ImageView mImageView;

        // TODO Auto-generated method stub
        public ViewHolder(View v) {
            super(v);
        }

    }

    public MyAdapter(int[] mDataset) {
        this.mDataset = mDataset;
    }

    /**
     * 獲取總的條目數量
     */
    @Override
    public int getItemCount() {
        // TODO Auto-generated method stub
        return mDataset.length;
    }

    /**
     * 建立ViewHolder
     */
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // TODO Auto-generated method stub
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycleview, parent, false);
        ViewHolder holder = new ViewHolder(v);
        holder.mImageView = (ImageView) v.findViewById(R.id.iv_image);
        return holder;
    }

    /**
     * 將資料繫結到ViewHolder上
     */
    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        // TODO Auto-generated method stub
        holder.mImageView.setImageResource(mDataset[position]);
        if (listener != null) {
            holder.mImageView.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    listener.onItemClickListener(v, position);
                }
            });
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

首先在Adapter裡定義一個內部介面,介面內定義回撥函式,然後向外暴露一個設定這個介面物件的方法,通過這個方法設定內部介面的物件,最後在ViewHolder繫結資料的方法中,通過介面物件呼叫介面方法,將相關資訊傳遞出去。下面是在MainActivity裡設定這個監聽方法:

mAdapter.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClickListener(View view, int position) {
                // TODO Auto-generated method stub
                Toast.makeText(MainActivity.this, position + "", Toast.LENGTH_SHORT).show();
            }
        });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

以下是執行效果圖:

原始碼請在這裡下載

    </div>
    <div class="article_social">
     <div class="article_like">
<div class="circle circle-like" id="my_zan" data_id="3IziIba">  </div>
  • 1
  • 2
  • 3
  • 4