getView()不復用convertView,ListView即毫無複用!(ListView回收機制)
#前提概要
getView()不復用convertView,ListView即毫無複用!
標題的話不知道有沒有震驚到大家,反正是與筆者一直以來的想法相悖的。
之前筆者一直認為,使用BaseAdapter如果不復用convertView,那麼可能會一定程度上降低ListView的效能,但是ListView本身一定還有其優化效能的方式,所以寫demo時也經常偷一下懶,就不寫convertView的複用了。
但是卻絕對想不到,如果getView中不復用convertView,ListView本身的複用機制就幾乎毫無作用了。
#ObtainView()
ListView內部對於View的獲取的優化其實主要還是通過ObtainView(),這個方法在ListView中是檢視不到的,這是它的父類AbsListView的方法。
View obtainView(int position, boolean[] outMetadata) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView");
outMetadata[0] = false;
// Check whether we have a transient state view. Attempt to re-bind the
// data and discard the view if we fail.
final View transientView = mRecycler.getTransientStateView(position);
if (transientView != null) {
final LayoutParams params = (LayoutParams) transientView.getLayoutParams();
// If the view type hasn't changed, attempt to re-bind the data.
if (params.viewType == mAdapter.getItemViewType(position)) {
final View updatedView = mAdapter.getView(position, transientView, this);
// If we failed to re-bind the data, scrap the obtained view.
if (updatedView != transientView) {
setItemViewLayoutParams(updatedView, position);
mRecycler.addScrapView(updatedView, position);
}
}
outMetadata[0] = true;
// Finish the temporary detach started in addScrapView().
transientView.dispatchFinishTemporaryDetach();
return transientView;
}
final View scrapView = mRecycler.getScrapView(position);
final View child = mAdapter.getView(position, scrapView, this);
if (scrapView != null) {
if (child != scrapView) {
// Failed to re-bind the data, return scrap to the heap.
mRecycler.addScrapView(scrapView, position);
} else if (child.isTemporarilyDetached()) {
outMetadata[0] = true;
// Finish the temporary detach started in addScrapView().
child.dispatchFinishTemporaryDetach();
}
}
if (mCacheColorHint != 0) {
child.setDrawingCacheBackgroundColor(mCacheColorHint);
}
if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
setItemViewLayoutParams(child, position);
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
if (mAccessibilityDelegate == null) {
mAccessibilityDelegate = new ListItemAccessibilityDelegate();
}
if (child.getAccessibilityDelegate() == null) {
child.setAccessibilityDelegate(mAccessibilityDelegate);
}
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return child;
}
程式碼比較長,但是並不是所有的都要看,transientView是Listview內部對帶動畫的View的一些複用處理,我們可以暫時避開它不看,主要的邏輯其實是scrapView那一塊。
final View scrapView = mRecycler.getScrapView(position);
final View child = mAdapter.getView(position, scrapView, this);
if (scrapView != null) {
if (child != scrapView) {
// Failed to re-bind the data, return scrap to the heap.
mRecycler.addScrapView(scrapView, position);
} else if (child.isTemporarilyDetached()) {
outMetadata[0] = true;
// Finish the temporary detach started in addScrapView().
child.dispatchFinishTemporaryDetach();
}
}
.........................................(此處程式碼省略)
return child;
此處邏輯主要如下:
1、從回收站中獲取到要複用的View,即scrapView 。
2、執行adapter的getView操作,並且獲取到View,即child。
3、如果存在複用的View,那麼就檢視一下getView(),返回的結果與它是不是同一個物件,如果是同一個物件,那麼代表複用成功(getView中僅僅修改了資料)。如果不是同一個物件,代表沒有複用,把這個複用的View重新放回回收站中。
此處的第二個引數scrapView 其實就是我們getView方法中的引數convertView。
我們可以看到,ListView的複用方式主要是通過判斷convertView與getView的返回物件是否是同一個物件,如果是就複用,如果不是就不復用。
倘若我們的getView()方法僅僅為了方便每次建立新的View,而沒有複用convertView,那麼必然ListView就不會複用了。
#SimpleAdapter的getView()
當然一般我們會重寫getView只是在BaseAdapter中,那麼其他的官方所寫的ListView使用的Adatper是不是就複用了呢?
我們可以看一下SimpleAdapter的getView()方法:
public View getView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(mInflater, position, convertView, parent, mResource);
}
private View createViewFromResource(LayoutInflater inflater, int position, View convertView,
ViewGroup parent, int resource) {
View v;
if (convertView == null) {
v = inflater.inflate(resource, parent, false);
} else {
v = convertView;
}
bindView(position, v);
return v;
}
如上程式碼所示,我們可以看到,在需求是“每個Item佈局相同”的情況下,的確是通過返回convertView來保證ListView的複用。
#ViewHolder
到此處,好奇的讀者可能又會想到“那麼ViewHolder在ListView的複用中又是擔任什麼樣的一個角色呢?”。
我們且看我們複用時getView()的demo程式碼:
static class ViewHolder {
public ImageView img;
public TextView title;
public TextView info;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.list_item, null); //這裡確定你listview裡面每一個item的layout
holder.img = (ImageView) convertView.findViewById(R.id.img); //此處是將內容與控制元件繫結。
holder.title = (TextView) convertView.findViewById(R.id.tv);//注意:此處的findVIewById前要加convertView.
holder.info = (TextView) convertView.findViewById(R.id.info);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag(); //這裡是為了提高listview的執行效率
}
holder.img.setBackgroundResource((Integer) data.get(position).get("img"));
holder.title.setText((String) data.get(position).get("title"));
holder.info.setText((String) data.get(position).get("info"));
return convertView;
}
這個示例中,我們可以看到,最終返回的是convertView,所以ListView的複用機制是成立的。所以這就避免了View的多次建立,即不會每次都執行如下程式碼建立View:
convertView = mInflater.inflate(R.layout.list_item, null);
而同時也使用setTag()的方法,將自定義的ViewHolder放入了,這不是針對“建立View”的優化,而是針對“獲取View”的優化,使用了ViewHolder之後,就不需要每次都去執行從convertView中findViewById的操作了。
當然這一切的前提是Listview即將建立的佈局與複用的佈局相同的情況,或者說僅僅只有資料不同的情況。
##文章如有謬誤,歡迎評論指正。
相關文章
- ListView的複用和快取機制View快取
- ListView回收機制相關分析View
- 利用convertView優化ListView效能View優化
- Android ListView的getview()中重複呼叫(position重複呼叫)AndroidView
- 關於ListView的getView方法被多次重複呼叫的問題View
- ListView 中的 RecycleBin 機制View
- ListView或RecyclerView子項item複用資料錯亂View
- 原始碼解析ListView中的RecycleBin機制原始碼View
- 再次探究Android ListView快取機制AndroidView快取
- listView中多個listItem佈局時,convertView快取及使用View快取
- 常見面試題之ListView的複用及如何優化面試題View優化
- 安卓5.1原始碼解析 : ListView解析 從繪製,重新整理機制到Item的回收機制全面講解安卓原始碼View
- Android回顧--(十) ListView的優化和多佈局複用AndroidView優化
- ListViewView
- 用ListView簡單實現滑動列表View
- android 用ListView實現表格樣式AndroidView
- 基於滑動場景解析RecyclerView的回收複用機制原理View
- RecyclerView的複用機制View
- 安卓 listview安卓View
- QML::ListViewView
- ListView不響應OnItemClickListener的解決方案View
- 解決Flutter的ListView巢狀ListView滑動衝突以及無限高度問題FlutterView巢狀
- ListView優化View優化
- Android ListViewAndroidView
- c# listviewC#View
- RecyclerView快取機制(咋複用?)View快取
- IO多路複用機制詳解
- WinUI 3學習筆記(2)—— 用ListView來展示集合UI筆記View
- 最新一課 老師指點用Listview介面卡View
- 深入理解Android中的快取機制(二)RecyclerView跟ListView快取機制對比Android快取View
- 用垃圾回收機制解釋JavaScript中的閉包JavaScript
- [譯]Flutter - 掌握ListViewFlutterView
- Flutter - Listview 詳解FlutterView
- ListView簡單使用View
- Android之ListViewAndroidView
- Android的ListViewAndroidView
- Listview禁止滑動View
- 8ListView4View