ListView 是繼承AbListView,AbListView是所有列表類控件的基類。
在ListView數據加載中最關鍵的一個函數就是makeAndAddView(),這個函數的作用就獲得一個ChildView並把該ChildView添加到List中,具體見源碼分析:
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
boolean selected) {
View child;//即ChildView
//如果數據沒有發生改變
if (!mDataChanged) {
//優先從循環器中獲取該位置的視圖
// Try to use an existing view for this position
child = mRecycler.getActiveView(position);
if (child != null) {
// Found it -- we're using an existing child
//如果找到了就直接添加到List中
// This just needs to be positioned
setupChild(child, position, y, flow, childrenLeft, selected, true);
return child;
}
}
//如果數據發生了改變,則在該位置上新建一個視圖,或者如果可能的話轉換一個已經沒有用的視圖(可能是當整個ListView其他位置發生了變化,但是該位置的ChildView並未發生任何變化)
// Make a new view for this position, or convert an unused view if possible
child = obtainView(position, mIsScrap);
// This needs to be positioned and measured
setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
//返回該childView
return child;
}
當ListView發生滑動操作時,若干已經加載的ChildView會被因滑動而被暫時隱藏掉,為了避免下次顯示再重新加載,這時ListView的緩存機制就會被觸發,即運行layoutChildren()函數(其實任何觸碰事件都會觸發,即onTouchEvent() -。-)。
那麼ListView的緩存機制是依靠什麼來緩存的呢?答案就是AbListView中 的內部類RecycleBin。關於RecycleBin的具體作用,源碼中的注釋已經解釋的非常清楚了,在此就不在贅述。
/**
* The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of
* storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the
* start of a layout. By construction, they are displaying current information. At the end of
* layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that
* could potentially be used by the adapter to avoid allocating views unnecessarily.
*... ...
*/
當需要緩存ActiveViews時會調用fillActiveViews()函數,該函數會把ListView中的所有ActiveViews 一次性都緩存起來。
/**
* Fill ActiveViews with all of the children of the AbsListView.
* ... ...
*/
void fillActiveViews(int childCount, int firstActivePosition) {
if (mActiveViews.length < childCount) {
mActiveViews = new View[childCount];
}
mFirstActivePosition = firstActivePosition;
//noinspection MismatchedReadAndWriteOfArray
final View[] activeViews = mActiveViews;
... ...
}
而對於ScrapViews則是調用的addScrapView()函數。
/**
* Puts a view into the list of scrap views.
* <p>
* If the list data hasn't changed or the adapter has stable IDs, views
* with transient state will be preserved for later retrieval.
*
* @param scrap The view to add
* @param position The view's position within its parent
*/
void addScrapView(View scrap, int position) {
... ...
// Don't scrap views that have transient state.
final boolean scrapHasTransientState = scrap.hasTransientState();
if (scrapHasTransientState) {
//Transient狀態
... ...
}else{
//scrap狀態
... ...
}
... ...
}
該函數中又分為兩個不同的level,一個是transient瞬時態,另一個就是一般的普通態,關於這兩個狀態的區分我個人的想法是為了更加快速的獲取ScrapViews,因為處於瞬時狀態的view最有可能是接下來將要在界面上顯示的View,畢竟你向上或向下滑動列表時目的就是這個,這一點在obtainView()函數中得到了體現:
View obtainView(int position, boolean[] isScrap) {
... ...
//優先獲取TransientStateView
scrapView = mRecycler.getTransientStateView(position);
if (scrapView == null) {
scrapView = mRecycler.getScrapView(position);
}
... ...
}
還有一個比較重要的函數就是scrapActiveViews()函數,它的作用是將目前所有的ActiveViews降級為ScrapViews,並將之前的所有ScrapViews清除。該函數在每次調用layoutChildern()函數時必定會被調用執行,目的就是為清空所有當前的ActiveViews,為新產生的ActiveViews做好准備。
/**
* Move all views remaining in mActiveViews to mScrapViews.
*/
void scrapActiveViews() {
... ...
//該函數確保mScrapViews的大小不會超過mActiveViews
pruneScrapViews();
}
以上是閱讀了ListView以及AbListView源碼後的一些心得總結,畢竟閱讀Android源碼也才剛剛起步,還有很多地方理解的不是很透徹,上文若有理解不當之處歡迎各位指正。
更多Android相關信息見Android 專題頁面 http://www.linuxidc.com/topicnews.aspx?tid=11