[Android]使用RecyclerView替代ListView(一)

如下內容爲原創,歡迎轉載,轉載請註明html

來自每天博客:http://www.cnblogs.com/tiantianbyconan/p/4232560.htmljava

 

RecyclerView是一個比ListView更靈活的一個控件,之後能夠直接拋棄ListView了。具體好在哪些地方,往下看就知道了。android

首先咱們來使用RecyclerView來實現ListView的效果,一個滾動列表,先看下效果圖(除了有動畫以外,沒什麼特別--):git

 

每一個item的佈局以下:github

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/recycler_view_test_item_person_view"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:padding="15dp"
              android:background="#aabbcc"
        >
    <TextView
            android:id="@+id/recycler_view_test_item_person_name_tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="18sp"
            android:background="#ccbbaa"
            />
    <TextView
            android:id="@+id/recycler_view_test_item_person_age_tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="5dp"
            android:background="#aaccbb"
            android:textSize="15sp"
            />
</LinearLayout>

item的佈局很簡單,只有兩個TextView,一個用來顯示名字,一個用來顯示年齡。api

Person的實體類就不貼代碼了,兩個屬性:名字和年齡。ide

而後須要使用到RecyclerView,因此須要把support v7添加到class path,並在佈局中添加該控件:佈局

<android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view_test_rv"
                android:scrollbars="vertical"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#bbccaa"
                />

而後在onCreate中:性能

1         recyclerView.setHasFixedSize(true);
2 
3         RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(context);
4         recyclerView.setLayoutManager(layoutManager);
5 
6         initData();
7         adapter = new PersonAdapter(personList);
8         adapter.setOnRecyclerViewListener(this);
9         recyclerView.setAdapter(adapter);    

 

如上述代碼:動畫

Line1: 使RecyclerView保持固定的大小,這樣會提升RecyclerView的性能。

Line3: LinearLayoutManager,若是你須要顯示的是橫向滾動的列表或者豎直滾動的列表,則使用這個LayoutManager。顯然,咱們要實現的是ListView的效果,因此須要使用它。生成這個LinearLayoutManager以後能夠設置他滾動的方向,默認豎直滾動,因此這裏沒有顯式地設置。

Line6: 初始化數據源。

Line7~9: 跟ListView同樣,須要設置RecyclerView的Adapter,可是這裏的Adapter跟ListView使用的Adapter不同,這裏的Adapter須要繼承RecyclerView.Adapter,須要實現3個方法:

- onCreateViewHolder()

- onBindViewHolder()

- getItemCount()

直接看代碼:

 1 package com.wangjie.helloandroid.sample.recycler.person;
 2 
 3 import android.support.v7.widget.RecyclerView;
 4 import android.view.LayoutInflater;
 5 import android.view.View;
 6 import android.view.ViewGroup;
 7 import android.widget.LinearLayout;
 8 import android.widget.TextView;
 9 import com.wangjie.androidbucket.log.Logger;
10 import com.wangjie.helloandroid.R;
11 
12 import java.util.List;
13 
14 /**
15  * Author: wangjie
16  * Email: tiantian.china.2@gmail.com
17  * Date: 1/17/15.
18  */
19 public class PersonAdapter extends RecyclerView.Adapter {
20     public static interface OnRecyclerViewListener {
21         void onItemClick(int position);
22         boolean onItemLongClick(int position);
23     }
24 
25     private OnRecyclerViewListener onRecyclerViewListener;
26 
27     public void setOnRecyclerViewListener(OnRecyclerViewListener onRecyclerViewListener) {
28         this.onRecyclerViewListener = onRecyclerViewListener;
29     }
30 
31     private static final String TAG = PersonAdapter.class.getSimpleName();
32     private List<Person> list;
33 
34     public PersonAdapter(List<Person> list) {
35         this.list = list;
36     }
37 
38     @Override
39     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
40         Logger.d(TAG, "onCreateViewHolder, i: " + i);
41         View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_view_test_item_person, null);
42         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
43         view.setLayoutParams(lp);
44         return new PersonViewHolder(view);
45     }
46 
47     @Override
48     public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
49         Logger.d(TAG, "onBindViewHolder, i: " + i + ", viewHolder: " + viewHolder);
50         PersonViewHolder holder = (PersonViewHolder) viewHolder;
51         holder.position = i;
52         Person person = list.get(i);
53         holder.nameTv.setText(person.getName());
54         holder.ageTv.setText(person.getAge() + "歲");
55     }
56 
57     @Override
58     public int getItemCount() {
59         return list.size();
60     }
61 
62     class PersonViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener
63     {
64         public View rootView;
65         public TextView nameTv;
66         public TextView ageTv;
67         public int position;
68 
69         public PersonViewHolder(View itemView) {
70             super(itemView);
71             nameTv = (TextView) itemView.findViewById(R.id.recycler_view_test_item_person_name_tv);
72             ageTv = (TextView) itemView.findViewById(R.id.recycler_view_test_item_person_age_tv);
73             rootView = itemView.findViewById(R.id.recycler_view_test_item_person_view);
74             rootView.setOnClickListener(this);
75             rootView.setOnLongClickListener(this);
76         }
77 
78         @Override
79         public void onClick(View v) {
80             if (null != onRecyclerViewListener) {
81                 onRecyclerViewListener.onItemClick(position);
82             }
83         }
84 
85         @Override
86         public boolean onLongClick(View v) {
87             if(null != onRecyclerViewListener){
88                 return onRecyclerViewListener.onItemLongClick(position);
89             }
90             return false;
91         }
92     }
93 
94 }

如上代碼所示:

public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i)

這個方法主要生成爲每一個Item inflater出一個View,可是該方法返回的是一個ViewHolder。方法是把View直接封裝在ViewHolder中,而後咱們面向的是ViewHolder這個實例,固然這個ViewHolder須要咱們本身去編寫。直接省去了當初的convertView.setTag(holder)和convertView.getTag()這些繁瑣的步驟。

 

public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i)

這個方法主要用於適配渲染數據到View中。方法提供給你了一個viewHolder,而不是原來的convertView。

對比下之前的寫法就一目瞭然了:

 1 @Override
 2     public View getView(int position, View convertView, ViewGroup parent) {
 3         ViewHolder holder;
 4         if(null == convertView){
 5             holder = new ViewHolder();
 6             LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 7             convertView = mInflater.inflate(R.layout.item, null);
 8             holder.btn = (Button) convertView.findViewById(R.id.btn);
 9             holder.tv = (TextView) convertView.findViewById(R.id.tv);
10             holder.iv = (TextView) convertView.findViewById(R.id.iv);
11 
12             convertView.setTag(holder);
13         }else{
14             holder = (ViewHolder) convertView.getTag();
15         }
16         final HashMap<String, Object> map = list.get(position);
17 
18         holder.iv.setImageResource(Integer.valueOf(map.get("iv").toString()));
19         holder.tv.setText(map.get("tv").toString());
20 
21         holder.btn.setOnClickListener(new View.OnClickListener() {
22             @Override
23             public void onClick(View v) {
24                 Toast.makeText(context, map.get("btn").toString(), Toast.LENGTH_SHORT).show();
25             }
26         });
27 
28         return convertView;
29     }
30 
31     class ViewHolder{
32         Button btn;
33         ImageView iv;
34         TextView tv;
35 
36     }

對比後能夠發現:

舊的寫法中Line5~Line12+Line28部分的代碼其實起到的做用至關於新的寫法的onCreateViewHolder();

舊的寫法中Line14~Line26部分的代碼其實起到的做用至關於新的寫法的onBindViewHolder();

既然是這樣,那咱們就把原來相應的代碼搬到對應的onCreateViewHolder()和onBindViewHolder()這兩個方法中就能夠了。

由於RecyclerView幫咱們封裝了Holder,因此咱們本身寫的ViewHolder就須要繼承RecyclerView.ViewHolder,只有這樣,RecyclerView才能幫你去管理這個ViewHolder類。

既然getView方法的渲染數據部分的代碼至關於onBindViewHolder(),因此若是調用adapter.notifyDataSetChanged()方法,應該也會從新調用onBindViewHolder()方法纔對吧?實驗後,果真如此!

除了adapter.notifyDataSetChanged()這個方法以外,新的Adapter還提供了其餘的方法,以下:

        public final void notifyDataSetChanged()
        public final void notifyItemChanged(int position)
        public final void notifyItemRangeChanged(int positionStart, int itemCount)
        public final void notifyItemInserted(int position) 
        public final void notifyItemMoved(int fromPosition, int toPosition)
        public final void notifyItemRangeInserted(int positionStart, int itemCount)
        public final void notifyItemRemoved(int position)
        public final void notifyItemRangeRemoved(int positionStart, int itemCount) 

基本上看到方法的名字就知道這個方法是幹嗎的了,

第一個方法沒什麼好講的,跟之前同樣。

notifyItemChanged(int position),position數據發生了改變,那調用這個方法,就會回調對應position的onBindViewHolder()方法了,固然,由於ViewHolder是複用的,因此若是position在當前屏幕之外,也就不會回調了,由於沒有意義,下次position滾動會當前屏幕之內的時候一樣會調用onBindViewHolder()方法刷新數據了。其餘的方法也是一樣的道理。

public final void notifyItemRangeChanged(int positionStart, int itemCount),顧名思義,能夠刷新從positionStart開始itemCount數量的item了(這裏的刷新指回調onBindViewHolder()方法)。

public final void notifyItemInserted(int position),這個方法是在第position位置被插入了一條數據的時候可使用這個方法刷新,注意這個方法調用後會有插入的動畫,這個動畫可使用默認的,也能夠本身定義。

public final void notifyItemMoved(int fromPosition, int toPosition),這個方法是從fromPosition移動到toPosition爲止的時候可使用這個方法刷新

public final void notifyItemRangeInserted(int positionStart, int itemCount),顯然是批量添加。

public final void notifyItemRemoved(int position),第position個被刪除的時候刷新,一樣會有動畫。

public final void notifyItemRangeRemoved(int positionStart, int itemCount),批量刪除。

 

這些方法分析完以後,咱們來實現一個點擊一個按鈕,新增一條數據,長按一個item,刪除一條數據的場景。

如下是新增一條數據的代碼:

1 Person person = new Person(i, "WangJie_" + i, 10 + i);
2 adapter.notifyItemInserted(2);
3 personList.add(2, person);
4 adapter.notifyItemRangeChanged(2, adapter.getItemCount());

如上代碼:

Line2:表示在position爲2的位置,插入一條數據,這個時候動畫開始執行。

Line3: 表示在數據源中position爲2的位置新增一條數據(其實這個纔是真正的新增數據啦)。

Line4: 爲何要刷新position爲2之後的數據呢?由於,在position爲2的位置插入了一條數據後,新數據的position變成了2,那原來的position爲2的應該變成了3,3的應該變成了4,因此2之後的全部數據的position都發生了改變,因此須要把position2之後的數據都要刷新。理論上是這樣,可是實際上刷新的數量只有在屏幕上顯示的position爲2之後的數據而已。若是這裏使用notifyDataSetChanged()來刷新屏幕上顯示的全部item能夠嗎?結果不會出錯,可是會有一個問題,前面調用了notifyItemInserted()方法後會在執行動畫,若是你調用notifyDataSetChanged()刷新屏幕上顯示的全部item的話,必然也會刷新當前正在執行動畫的那個item,這樣致使的結果是,前面的動畫還沒執行完,它立刻又被刷新了,動畫就看不見了。因此只要刷新2之後的item就能夠了。

 

看了RecyclerView的api,發現沒有setOnItemClickListener--,因此仍是本身把onItemClick從Adapter中回調出來吧。這個很簡單,就像上面PersonAdaper中寫的OnRecyclerViewListener那樣。

 

長按刪除的代碼以下:

1 adapter.notifyItemRemoved(position);
2 personList.remove(position);
3 adapter.notifyItemRangeChanged(position, adapter.getItemCount());

代碼跟以前插入的代碼基本一致。先通知執行動畫,而後刪除數據源中的數據,而後通知position以後的數據刷新就能夠了。

這樣ListView的效果就實現了。

 

示例代碼:

https://github.com/wangjiegulu/RecyclerViewSample

 

[Android]使用RecyclerView替代ListView(二)

http://www.cnblogs.com/tiantianbyconan/p/4242541.html

 

[Android]使用RecyclerView替代ListView(三) 

http://www.cnblogs.com/tiantianbyconan/p/4268097.html

相關文章
相關標籤/搜索