ListView作為Android開發中使用頻率最高的一個控制元件,保證ListView的流暢執行,對使用者體驗的提高至關重要。Adapter是ListView和資料來源之間的中間人,當每條資料進入可見區時,Adapter 的 getView() 會被呼叫,返回代表具體資料的檢視,在成百上千條資料觸控滾動時頻繁呼叫,因此如何優化Adapter是提高ListView效能的關鍵。
1. 使用ViewHolder模式,重複利用convertView,減少頻繁查詢
在2009年 Google IO開發者大會中已做說明,看一下使用不同實現方式之間的差距:
Adapter 顯示每條資料的 XML 佈局檔案如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal"> <ImageView android:id="@+id/icon" android:layout_width="48dip" android:layout_height="48dip" /> <TextView android:id="@+id/text" android:layout_gravity="center_vertical" android:layout_width="0dip" android:layout_weight="1.0" android:layout_height="wrap_content" /> </LinearLayout>
1. 最慢最不實用的方式
public View getView(int position, View convertView, ViewGroup parent) { View item = mInflater.inflate(R.layout.list_item_icon_text, null); ((TextView) item.findViewById(R.id.text)).setText(DATA[position]); ((ImageView) item.findViewById(R.id.icon)).setImageBitmap( (position & 1) == 1 ? mIcon1 : mIcon2); return item; }
2. 使用 convertView 回收檢視, 效率提高 200%
public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.item, null); } ((TextView) convertView.findViewById(R.id.text)).setText(DATA[position]); ((ImageView) convertView.findViewById(R.id.icon)).setImageBitmap( (position & 1) == 1 ? mIcon1 : mIcon2); return convertView; }
3. 使用 ViewHolder 模式, 效率再提高 50%
static class ViewHolder { TextView text; ImageView icon; }
public View getView(int pos, View convertView, ViewGroup parent){ ViewHolder holder; if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item, null); holder = new ViewHolder(); holder.text = (TextView) convertView.findViewById(R.id.text)); holder.icon = (ImageView) convertView.findViewButId(R.id.icon)); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.text.setText(DATA[pos]); holder.icon.setImageBitmap((pos & 1) == 1 ? mIcon1 : mIcon2); return convertView; }
更新率比較如下圖:
2. 使用工作執行緒載入資料,減輕UI主執行緒負擔,使UI主執行緒只專注於UI繪製
// Using an AsyncTask to load the slow images in a background thread new AsyncTask<ViewHolder, Void, Bitmap>() { private ViewHolder v; @Override protected Bitmap doInBackground(ViewHolder... params) { v = params[0]; return mFakeImageLoader.getImage(); } @Override protected void onPostExecute(Bitmap result) { super.onPostExecute(result); if (v.position == position) { // If this item hasn't been recycled already, hide the // progress and set and show the image v.progress.setVisibility(View.GONE); v.icon.setVisibility(View.VISIBLE); v.icon.setImageBitmap(result); } } }.execute(holder);