是時候講清楚了:我瞭解RecylerView有些晚。太遲了。
這隻能怪我自己。你可以從文件中得知RecylerView應該會取代ListView,並且在工具包裡沒有幾個檢視比ListView更重要。所以,RecyclerView十分重要。
但是它們還是會有很多不同,對嗎?嗯。所以我一直推遲對它的講解,直到幾周之前我為Hack Night準備一個講座的時候。我最終做了大量的研究,我承認這些研究很有樂趣。
最終的結果是RecyclerView非常酷,值的去替換listView。它把以前非常棘手的東西變的非常非常簡單。最重要的是,它可以讓一個item輕鬆的從一個可行的列表中動畫流入和流出,而不像之前那樣慢。
通過指出所有的這些要點,我決定把ListView從我們的 Android programming guide中刪除,並用RecyclerView代替。我發現使用RecyclerView會使大多數的工作變的非常容易,並且最終的程式碼非常簡潔。
除了一點:選擇模式(choice modes)。setChoiceMode()被去掉了,並且讓一個RecyclerView轉換多項選擇,會導致了一些有趣的問題。在以後的幾篇部落格中我將會按照步驟來跟你分享我的解決方案。在這裡我先從RecyclerView的基本用法開始。
RecyclerView做的更少
如果你打算替換ListView,那麼現在讓我們討論下RecyclerView中的一些重大變化。
第1步,當然這是非常重要的,你需要在build.gradle裡寫入下面這一行:
compile ‘com.android.support:recyclerview-v7:+’
第二步:有一個很好的的去掉setChoiceMode(int)的原因。RecyclerView能做的比ListView更多,但是比起ListView它有更少的責任。創造性的,RecyclerView並不需要處理:
1.在螢幕上item的位置
2.動畫檢視
3.除了滾動可以捕獲任何觸控事件
但是,所有的這些都被放到了ListView中,而RecyclerView是通過使用合作者類去(collaborator class)做這些工作的。
前兩個,RecyclerView通過使用LayoutManager和ItemAnimator來實現。RecyclerView擁有一個預設的ItemAdapter,所以你不必要擔心這些。你要做的是給item一個LayoutManager。這是我們的CriminalIntent應用中list fragment的OnCreateView():
02 |
public View
onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) |
05 |
View
v = inflater.inflate(R.layout.fragment_recyclerview, parent, false ); |
07 |
mRecyclerView
= (RecyclerView) v.findViewById(R.id.recycler_view); |
08 |
mRecyclerView.setLayoutManager( new LinearLayoutManager(getActivity())); |
09 |
mCrimes
= CrimeLab.get(getActivity()).getCrimes(); |
10 |
mRecyclerView.setAdapter( new CrimeAdapter()); |
LinearLayoutManager在RecyclerView support library中。它會讓RecyclerView中的item像ListView中顯示的那樣。還有其它的佈局管理者,比如網格,或者交錯網格。至於CrimeAdapter,我將立即討論它。
當在ListView中點選一個item時,你可能會這樣做:
01 |
public View
onCreateView(LayoutInflater inflater, ViewGroup parent,Bundle savedInstanceState) |
06 |
mListView.setOnItemClickListener( this ); |
11 |
public void onItemClick(AdapterView<?>
parent, View view, int position, |
通過監聽事件你可以處理adapter返回的單個item的點選事件。這並不常見,也不推薦使用。雖然ListView給了你一些不錯的功能,諸如,允許在點選處理之上建立可選擇的item的ListView.setChoiceMode(int)。(setChoiceMode(int)可以讓你選擇單獨的item或者是多個item)
然而,RecyclerView並不需要處理這些,所以它不需要模式選擇。你不能使用OnItemClickListener來捕獲item被點選。RecyclerView提供了一個OnItemTouchListener,但這不同於OnItemClickListener:這個事件並不告訴你哪個item被觸發了。你需要通過MotionEvent去尋找哪個item被觸發,但大多數情況這並不需要。
ViewHolder
另一個大的不同是view holder變的更重要。如果你從我們這裡學習怎樣使用ListView,你可以不知道什麼是view holder,因為我們並沒有教那樣做。View holder是附加在ListView中每一行的物件。一般的view holder會如下圖所展示的這樣:
01 |
private static class ViewHolder
{ |
02 |
private CheckBox
mSolvedCheckBox; |
05 |
private class CrimeAdapter extends ArrayAdapter<Crime>
{ |
06 |
public CrimeAdapter(ArrayList<Crime>
crimes) { |
07 |
super (getActivity(), 0 ,
crimes); |
11 |
public View
getView( int position,
View convertView, ViewGroup parent) { |
13 |
if ( null ==
convertView) { |
14 |
convertView
= LayoutInflater.from(getActivity()) |
15 |
.inflate(R.layout.list_item_crime,
parent, false ); |
16 |
holder
= new ViewHolder(); |
17 |
holder.mSolvedCheckBox
= (CheckBox) convertView |
18 |
.findViewById(R.id.solvedCheckBox); |
20 |
convertView.setTag(holder); |
23 |
ViewHolder
holder = (ViewHolder) convertView.getTag(); |
24 |
Crime
crime = getItem(postion); |
25 |
holder.mSolvedCheckBox.setChecked(crime.isSolved()); |
當view獲得檢視時,你也需要建立一個ViewHolder物件,並且添入相應的資料。然後通過setTag()把ViewHolder關聯到view上。
你每建立一個view,相應的要建立一個ViewHolder。之前使用ViewHolder是為了實現滾動效能更好:它通過呼叫findViewById()儲存了solvedCheckBox所以在每次尋找元件時更方便。如果你有其它的操作需要呼叫元件,你同樣會選擇使用ViewHolder,而不是使用getView()。
下面是我們為RecyclerView寫的包含ViewHolder的程式碼:
01 |
private class CrimeHolder extends ViewHolder
{ |
02 |
private final CheckBox
mSolvedCheckBox; |
05 |
public CrimeHolder(View
itemView) { |
08 |
mSolvedCheckBox
= (CheckBox) itemView |
09 |
.findViewById(R.id.crime_list_item_solvedCheckBox); |
12 |
public void bindCrime(Crime
crime) { |
14 |
mSolvedCheckBox.setChecked(crime.isSolved()); |
18 |
private class CrimeAdapter |
19 |
extends RecyclerView.Adapter<CrimeHolder>
{ |
21 |
public CrimeHolder
onCreateViewHolder(ViewGroup parent, int pos)
{ |
22 |
View
view = LayoutInflater.from(parent.getContext()) |
23 |
.inflate(R.layout.list_item_crime,
parent, false ); |
24 |
return new CrimeHolder(view); |
28 |
public void onBindViewHolder(CrimeHolder
holder, int pos)
{ |
29 |
Crime
crime = mCrimes.get(pos); |
30 |
holder.bindCrime(crime); |
34 |
public int getItemCount()
{ |
35 |
return mCrimes.size(); |
注意這裡沒有ArrayAdapter,所以你需要為Adapter關聯一個List。幸運的是這並不難。
就像ListView Adapter中的那樣,你需要要建立兩個類:一個Adapter和一個ViewHolder。在RecyclerView的adapter中,更像是系統的一塊基石。你的RecyclerView.Adapter去創造和繫結ViewHolder,而不是之前的View。
同樣你建立的ViewHolder更加健壯。RecyclerView.ViewHolder子類擁有一大堆RecyclerView使用的方法。ViewHolder知道剛剛繫結的是哪個位置和item的id。
這樣ViewHolder就被建立了。以前控制整個item檢視是ListView的工作,ViewHolder只是控制其中的一小部分。現在,ViewHolder在ViewHolder.itemView中控制所有檢視,並在建構函式中給itemView賦值。
自從ViewHolder有了這個新的責任,它同樣可以被賦予更多其它的責任。所以在新的實現中我們加入了一個新的方法——bindCrime(),以前我們常常在getView()裡做很多的工作。ViewHolder同樣成為捕獲特定item點選事件的場所。
01 |
private class CrimeHolder extends ViewHolder |
02 |
implements View.OnClickListener
{ |
05 |
public CrimeHolder(View
itemView) { |
08 |
itemView.setOnClickListener( this ); |
14 |
public void onClick(View
v) { |
16 |
Intent
i = CrimeActivity.getIntent(v.getContext(), mCrime); |
在ListView中關於怎樣控制點選事件有一些地方比較模糊:是每個單獨的視力控制這些事件,還是ListView通過OnItemClickListener控制?在RecyclerView中ViewHolder是確定做為行級(row-level)控制物件來處理這些細節。
我們之前看到LayoutManager處理檢視位置,ItemAnimator處理動畫。ViewHolder是最後的部分:它的職責是處理髮生在RecyclerView中特定item的事件。
選擇模式及下一個是什麼
那麼選擇模式(choice mode)怎麼辦?怎麼在RecyclerView中使用它們?我們在這裡會很自然的在ViewHolder中展示出來,因為它處理item的點選事件。
在這裡我有一些壞訊息:ViewHolder並沒有提供任何工具來實現選擇。去實現單個或多個選擇,我將在下一篇部落格中講遇到的兩個問題:
1.跟蹤選擇。我們需要一些程式碼來獲取哪個item被選擇了,或者選擇有沒有被啟用。
2.展示哪些item被選擇了。ListView通過呼叫每個item檢視中的setActivated()實現。你可以在Lollipop中實現,但是在實現過程中幾個陷阱。
轉載自併發程式設計網 – ifeve.com本文連結地址: RecyclerView
Part 1:為ListView專家寫的基礎