ListView源碼分析

一、ListView 的工作原理

Adapter的作用就是ListView界面與數據交互的橋樑,當列表裏面每一項顯示到頁面上時,都會調用Adapter的getView()方法。
系統需要回執ListView時,首先會調用getCount()函數,得到要繪製的這個列表額長度,然後開始從第一行開始繪製,每行的回執方法是調用getView函數。那麼Android是不是爲每一行都會新創建一個View呢?試想加入行數爲幾萬行,內存肯定會爆掉的,所以Android官方早就想到了這一點,在ListView實現中添加了視圖的緩存-Recycler,每當有行移除屏幕的可視區域時,這個被移除的行的View對象就會被添加到Recycler中,也就是在渲染新行時的那個參數convertView。

 

二、ListView的初始化

 

 

疑問一:ListView繪製時是如何獲取每行的View的呢?

首先ListView通過setAdapter方法,將Adapter與ListView關聯起來,查看ListView的setAdater方法源碼可知,setAdater將傳遞經來的Adapter的引用複製給了內部全局變量mAdapter,ListView在繪製每行的時候根據行號position調用父類AbsListView中的obtainView,obtainView首先會從recycler中獲取是否有匹配的視圖,如果存在的話,則調用adapter.getView方法,並傳遞了scrapView給convertView變量,否則傳遞的是null。

疑問二:ListView中數據發生變更了,我們一般會調用Adapter的 notifyDataSetChanged()方法, 那麼視圖是怎麼發生變化的呢?

ListView 中的數據適配器Adapter 採用的是觀察者模式

ListView在setAdapter時,會新建ApdateDataSetObserver,並註冊此觀察者。

AdapterDataSetObserver類實在AbsListView中定義:

AdapterView中AdapterDataSetObserver的實現如下(部分省略):

AdapterDataSetObserver 實現了DataSetObserver接口,並重寫了onChanger方法,裏面調用了requestLayout方法,此方法的作用是要求parent view 重新調用它的哦弄Measure onLayout方法重新佈局視圖,但不會重新繪製任何視圖包括該調用者本身。
requestLayout的實現方法需要到View類中查看:可參考:http://blog.csdn.net/androiddevelop/article/details/8561076

疑問三:RecyclerBin的數據結構是這樣的呢?當ListView有多個視圖類型(在界面上就是有不同的樣式和數據類型)又是怎麼選擇合適的convertView的呢?

首先看一下RecycleBin的類定義(AbsListView中內部類)

從註釋中我們就可以得知 :
RecycleBin一共有兩個存儲結構分別是ActiveViews 和 ScrapViews
ActiveViews儲存當前在界面(手機顯示區域)中顯示View,移出界面的view會存入ScrapViews
ScrapViews存儲當前已經滑動出當前界面(手機顯示區域)顯示的View,這些view存儲起來相當於回收,當再次請求的時候從此存儲中取出反覆使用。
當ListView中有N個視圖類型時,RecycleBin會創建N個scrapView數組,每個類型一個view數組,後面在獲取view時會先判斷view的類型,然後到對應的數組中去取。

怎麼從ScrapViews中獲取可用的view視圖呢?getScrapView  → retrieveFromScrap

retrieveFromScrap(這個不屬於RecycleBin類,是屬於外部類AbslistView中的方法)
根據position,從mScrapView中找:
        1. 如果有view.scrappedFromPosition = position的,直接返回該view;
        2. 否則返回mScrapView中最後一個;
        3. 如果緩存中沒有view,則返回null;


下面,我們來分析下這三種情況在什麼條件下滿足?
         a. 第三種情況,這個最簡單:
         一開始,listview穩定後,顯示N個,此時mScrapView中是沒有緩存view的,當我們向上滾動一小段距離(第一個此時仍顯示部分),新的view將會顯示,此時listview會調用Adapter.getView,但是緩存中沒有,因此convertView是null,所以,我們得分配一塊內存來創建新的convertView;
         b. 第二種情況:
         在a中,我們繼續向上滾動,直接第一個view完全移出屏幕(假設沒有新的item),此時,第一個view就會被detach,並被加入到mScrapView中;然後,我們還繼續向上滾動,直接後面又將要顯示新的item view時,此時,系統會從mScrapView中找position對應的View,顯然,是找不到的,則將從mScrapView中,取最後一個緩存的view傳遞給convertView;
         c. 第一種情況:
        緊接着在b中(標示爲橙色的文字後面),第一個被完全移出,加入到mScrapView中,且沒有新增的item到listview中,此時,緩存中就只有第一個view;然後,我此時向下滑動,則之前的第一個item,將被顯示出來,此時,從緩存中查找position對應的view有沒有,當然,肯定是找到了,就直接返回了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章