ListView詳解
直接繼承自AbsListView,AbsListView繼承自AdapterView匣屡,AdapterView又繼承自ViewGroup。
Adpater在ListView和數(shù)據(jù)源之間起到了一個橋梁的作用
RecycleBin機制
RecycleBin機制是ListView能夠?qū)崿F(xiàn)成百上千條數(shù)據(jù)都不會OOM最重要的一個原因。RecycleBin是AbsListView的一個內(nèi)部類诈泼。
RecycleBin當(dāng)中使用mActiveViews這個數(shù)組來存儲View唧垦,調(diào)用這個方法后就會根據(jù)傳入的參數(shù)來將ListView中的指定元素存儲到mActiveViews中捅儒。
mActiveViews當(dāng)中所存儲的View,一旦被獲取了之后就會從mActiveViews當(dāng)中移除振亮,下次獲取同樣位置的時候?qū)祷豱ull巧还,所以mActiveViews不能被重復(fù)利用。
addScrapView()用于將一個廢棄的View進行緩存坊秸,該方法接收一個View參數(shù)麸祷,當(dāng)有某個View確定要廢棄掉的時候(比如滾動出了屏幕)就應(yīng)該調(diào)用這個方法來對View進行緩存,RecycleBin當(dāng)中使用mScrapV
iews和mCurrentScrap這兩個List來存儲廢棄View褒搔。
getScrapView 用于從廢棄緩存中取出一個View阶牍,這些廢棄緩存中的View是沒有順序可言的喷面,因此getScrapView()方法中的算法也非常簡單,就是直接從mCurrentScrap當(dāng)中獲取尾部的一個scrap view進行返回荸恕。
我們都知道Adapter當(dāng)中可以重寫一個getViewTypeCount()來表示ListView中有幾種類型的數(shù)據(jù)項乖酬,而setViewTypeCount()方法的作用就是為每種類型的數(shù)據(jù)項都單獨啟用一個RecycleBin緩存機制。
View的流程分三步融求,onMeasure()用于測量View的大小咬像,onLayout()用于確定View的布局,onDraw()用于將View繪制到界面上生宛。
RecyclerView和ListView的異同
在我的一篇介紹Android新控件RecyclerView的博客(Android L新控件RecyclerView簡介)中县昂,一個讀者留言說RecyclerView跟ListView之間好像沒有什么不同,我覺得這是一個好問題陷舅,應(yīng)該明確地區(qū)分一下兩者的睯倒彰,所以我就研究了一下它倆之間的區(qū)別,然后也對兩者的使用有了更加深入的了解莱睁。
Android是一個不斷進化的平臺待讳,Android 5.0的v7版本支持包中引入了新的RecyclerView控件,正如官方文檔所言仰剿,RecyclerView是ListView的豪華增強版创淡。它主要包含以下幾處新的特性,如ViewHolder南吮,ItemDecorator琳彩,LayoutManager,SmothScroller以及增加或刪除item時item動畫等部凑。官方推薦我們采用RecyclerView來取代ListView露乏。
ViewHolder
ViewHolder是用來保存視圖引用的類,無論是ListView亦或是RecyclerView涂邀。只不過在ListView中瘟仿,ViewHolder需要自己來定義,且這只是一種推薦的使用方式比勉,不使用當(dāng)然也可以猾骡,這不是必須的。只不過不使用ViewHolder的話敷搪,ListView每次getView的時候都會調(diào)用findViewById(int),這將導(dǎo)致ListView性能展示遲緩幢哨。而在RecyclerView中使用RecyclerView.ViewHolder則變成了必須赡勘,盡管實現(xiàn)起來稍顯復(fù)雜,但它卻解決了ListView面臨的上述不使用自定義ViewHolder時所面臨的問題捞镰。RecyclerView.ViewHolder被BaseAdapter使用闸与,以將posiiton綁定到上面(可以通過API查看RecyclerView.ViewHolder#getPosition()方法)毙替。
LayoutManager
我們知道ListView只能在垂直方向上滾動,Android API沒有提供ListView在水平方向上面滾動的支持践樱〕Щ或許有多種方式實現(xiàn)水平滑動,但是請想念我拷邢,ListView并不是設(shè)計來做這件事情的袱院。但是RecyclerView相較于ListView,在滾動上面的功能擴展了許多瞭稼。它可以支持多種類型列表的展示要求忽洛,主要如下:
LinearLayoutManager,可以支持水平和豎直方向上滾動的列表环肘。
StaggeredGridLayoutManager欲虚,可以支持交叉網(wǎng)格風(fēng)格的列表,類似于瀑布流或者Pinterest悔雹。
GridLayoutManager复哆,支持網(wǎng)格展示,可以水平或者豎直滾動腌零,如展示圖片的畫廊梯找。
ItemAnimator
列表動畫是一個全新的、擁有無限可能的維度莱没。起初的Android API中初肉,刪除或添加item時,item是無法產(chǎn)生動畫效果的饰躲。后面隨著Android的進化牙咏,Google的Chat Hasse推薦使用ViewPropertyAnimator屬性動畫來實現(xiàn)上述需求。相比較于ListView嘹裂,RecyclerView.ItemAnimator則被提供用于在RecyclerView添加妄壶、刪除或移動item時處理動畫效果。同時寄狼,如果你比較懶丁寄,不想自定義ItemAnimator,你還可以使用DefaultItemAnimator泊愧。
Adapter
ListView的Adapter中伊磺,getView是最重要的方法,它將視圖跟position綁定起來删咱,是所有神奇的事情發(fā)生的地方屑埋。同時我們也能夠通過registerDataObserver在Adapter中注冊一個觀察者。RecyclerView也有這個特性痰滋,RecyclerView.AdapterDataObserver就是這個觀察者摘能。ListView有三個Adapter的默認(rèn)實現(xiàn)续崖,分別是ArrayAdapter、CursorAdapter和SimpleCursorAdapter团搞。然而严望,RecyclerView的Adapter則擁有除了內(nèi)置的內(nèi)DB游標(biāo)和ArrayList的支持之外的所有功能。RecyclerView.Adapter的實現(xiàn)的逻恐,我們必須采取措施將數(shù)據(jù)提供給Adapter像吻,正如BaseAdapter對ListView所做的那樣。
ItemDecoration
在ListView中如果我們想要在item之間添加間隔符梢莽,我們只需要在布局文件中對ListView添加如下屬性即可:
1 android:divider="@android:color/transparent"
2 android:dividerHeight="5dp"
有趣的是萧豆,RecyclerView在默認(rèn)情況下并不在item之間展示間隔符。盡管Google的家伙有意地將這個問題遺留給我們?nèi)プ远x間隔符昏名,但這的確增加了開發(fā)人員的負(fù)擔(dān)涮雷。如果你想要添加間隔符,你必須使用RecyclerView.ItemDecoration類來實現(xiàn)轻局『檠迹或者,你可以應(yīng)用官方示例中的DividerItemDecoration.java文件仑扑。
OnItemTouchListener
ListView通過AdapterView.OnItemClickListener接口來探測點擊事件览爵。而RecyclerView則通過RecyclerView.OnItemTouchListener接口來探測觸摸事件。它雖然增加了實現(xiàn)的難度镇饮,但是卻給予開發(fā)人員攔截觸摸事件更多的控制權(quán)限蜓竹。OthersListView可以設(shè)置選擇模式,并添加MultiChoiceModeListener储藐,如下所示:
1 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
2 listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
3 public boolean onCreateActionMode(ActionMode mode, Menu menu) { ... }
4 public void onItemCheckedStateChanged(ActionMode mode, int position,
5 long id, boolean checked) { ... }
6 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
7 switch (item.getItemId()) {
8 case R.id.menu_item_delete_crime:
9 CrimeAdapter adapter = (CrimeAdapter)getListAdapter();
10 CrimeLab crimeLab = CrimeLab.get(getActivity());
11 for (int i = adapter.getCount() - 1; i >= 0; i--) {
12 if (getListView().isItemChecked(i)) {
13 crimeLab.deleteCrime(adapter.getItem(i));
14 }
15 }
16 mode.finish();
17 adapter.notifyDataSetChanged();
18 return true;
19 default:
20 return false;
21 }
22 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { ... }
23 public void onDestroyActionMode(ActionMode mode) { ... }
24 });
而RecyclerView則沒有此功能俱济。
總之,通過比較我們可以發(fā)現(xiàn)钙勃,RecyclerView充滿了大量的自定義功能蛛碌,它可以用于實現(xiàn)復(fù)雜的列表或網(wǎng)格,但實現(xiàn)起來稍顯得復(fù)雜辖源。