自定義 ArrayAdapter 重寫 getView() 時,發現兩筆資料卻觸發 4 次以上呼叫,甚至更多。深入探討後發現,核心在於 ListView 與其父容器的 layout_width/height 設定為 wrap_content 導致多次 measure 遞歸計算,進而反覆觸發 getView 以取得子視圖尺寸。
public class CustomCollectAdapter extends ArrayAdapter<House> {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// ... ViewHolder 模式、異步載圖、按鈕事件等標準邏輯
System.out.println(house.toString()); // 此行重複打印
return view;
}
}
Android 視圖繪製分 Measure → Layout → Draw 三階段。ListView 在 Measure 階段需計算自身高度,若父容器也是 wrap_content,則形成雙向依賴:
fill_parent:尺寸確定,Measure 呼叫較少。wrap_content:需遞歸詢問每個 Item 高度,getView 被頻繁調用以測量尺寸。wrap_content:Measure 過程可能反覆迭代,導致調用次數不可控。onMeasure 中快取尺寸或限制測量模式getView 執行耗時邏輯(如複雜運算、IO),改用異步載圖、ViewHolder 快取代碼中採用 AsyncTask + SoftReference 快取圖片,避免 OOM:
public class AsyncImageLoaderTask extends AsyncTask<Object, Object, Bitmap> {
private HashMap<String, SoftReference<Bitmap>> imageCache = new HashMap<>();
@Override
protected Bitmap doInBackground(Object... params) {
String url = (String) params[0];
ImageView view = (ImageView) params[1];
// 先查快取,再下載,最後存入快取
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) view.setImageBitmap(bitmap);
}
}
結論:佈局屬性決定 Measure 行為,Measure 行為直接驅動 getView 調用頻率。優先固定容器尺寸,輔以 ViewHolder 與異步載圖,即可徹底解決重複調用與效能問題。