Android 的 ListView (三)

ListView - 如何提高執行效能

在 ListView 中,如何有效的提高執行效能是很重要的一點,它有相當多的細節可以優化,這邊會介紹該如何去提高執行的效能。

佈局緩存

在沒有做佈局緩存的時候,我們的 ListView 的執行效率是非常耗時的,因為在 getView() 的方法中,每次都將佈局重新載入了一次,當 ListView 快速的上下滾動時,這邊就會造成效能的低弱。

getView() 中有一個 convertView 參數,這個參數會將之前加載好的佈局進行緩存,以便之後進行重用。我們試著修改原本的 FruitAdapter 中的 getView() 代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class FruitAdapter extends ArrayAdapter<Fruit> {

private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position);
View view;
// use cache view to increase performance
if( convertView == null ) {
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
} else {
view = convertView;
}

ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId());
fruitName.setText(fruit.getName());
return view;
}
}

這樣的話,每次在 getView() 中就會先去判斷是不是有加載的佈局,如果沒有的話會使用 LayoutInflater 去加載佈局,如果有的話,直接使用已加載好的 convertView。這樣可以在快速滾動的時候展示出更好的效能。

ViewHolder

雖然現在已經不會每次都加載佈局,但是我們每次在 getView() 中還是會呼叫 ViewfindViewById() 來獲取元件的一次實體。這邊我們可以用一個 ViewHolder 針對這部份進行優化,繼續修改代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position);
View view;
ViewHolder viewHolder;
// use cache view to increase performance
if( convertView == null ) {
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
viewHolder = new ViewHolder();
viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
view.setTag(viewHolder);
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}

viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}

class ViewHolder {
ImageView fruitImage;
TextView fruitName;
}

我們新增了一個 ViewHolder ,將元件的實體進行緩存,當 convertViewnull 的時候,創建一個 ViewHolder 的實體,並將元件存放在 ViewHolder 裡,然後使用 setTag() 方法, 將 ViewHolder 實體存在 View 中, 當 convertView 不為 null 的時候,利用 getTag()ViewHolder 重新取出, 這樣所有的元件實體都緩存在 ViewHolder 裡, 就沒必要每次透過 findViewById() 獲取元件了。