http://blog.csdn.net/u010940300/article/details/44196671
使用convertView,viewHolder來優化Listview都是針對Adapter中的getView()方法來優化的
下面這個是沒有優化的getView()方法
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); // 獲取當前項的Fruit實例
View view=LayoutInflater.from(getContext()).inflate(resourceId, null);
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;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
優化點1:
這個Item顯示的View。如果當Item的數量足夠大,再爲每一個Item都創建一個View對象,必將佔用很多內存。
(沒有複用,導致每次使用都要創建)
優化點2:
創建View對象View view=LayoutInflater.from(getContext()).inflate(resourceId, null);
從xml中生成View,這是屬於IO操作也是耗時操作,所以必將影響性能。
優化點3:
加載ListItem中的TextView,ImageView(就本例來說)
convertView可以解決1和2
ViewHolder可以解決3
Android提供了一個叫做Recycler(反覆循環器)的構件,
就是當ListView的Item從上方滾出屏幕視角之外,對應Item的View會被緩
存到Recycler中,相應的會從下方生成一個Item,
而此時調用的getView中的convertView參數就是滾出屏幕的Item的View,所以說
如果能重用這個convertView,就會大大改善性能。
public class FruitAdapter extends ArrayAdapter<Fruit> {
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
Fruit fruit = getItem(position);
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
} 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;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
使用convertView優化之後
可以看到,現在我們在 getView()方法中進行了判斷,
1.如果 convertView 爲空,則使用 LayoutInflater 去加載佈局,
2.如果不爲空則直接對 convertView 進行重用。
(已經創建的View對象可以直接複用)
(對xml文件中的Layout——轉化——>View對象 這個過程進行優化,不用重複加載佈局文件)
這樣就大大提高了 ListView的運行效率,在快速滾動的時候也可以表現出更好的性能。
現在開始使用ViewHolder進行優化
雖然現在已經不會再重複去加載佈局,但是每次在 getView()方法中還是會調用 View 的 findViewById()方法來獲取一次控件的實例。 我們可以藉助一個 ViewHolder 來對這部分性能進行優化,修改 FruitAdapter 中的代碼,如下
public class FruitAdapter extends ArrayAdapter<Fruit> {
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
Fruit fruit = getItem(position);
View view;
ViewHolder viewHolder;
if (convertView == null)
{
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
//添加ViewHolder
viewHolder = new ViewHolder();
viewHolder.fruitImage = (ImageView) view.findViewById (R.id.fruit_image);
viewHolder.fruitName = (TextView) view.findViewById (R.id.fruit_name);
//將ImageView,TextView控件保存在ViewHolder中
view.setTag(viewHolder); // 將ViewHolder存儲在View中
//setTag是用來給 view添加附加信息的
//詳見 http://blog.csdn.net/pkxiuluo01/article/details/7380874
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag(); // 重新獲取ViewHolder
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
//在這裏我們將存放在ViewHolder中的ImageView,TextView實例拿出來使用,而不是findviewById重新創建
return view;
}
class ViewHolder {
ImageView fruitImage;
TextView fruitName;
}
}
- 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
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 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
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
我們新增了一個內部類 ViewHolder,用於對控件的實例進行緩存。當 convertView 爲空 的時候,創建一個 ViewHolder 對象,並將控件的實例都存放在 ViewHolder 裏,然後調用 View 的 setTag()方法,將 ViewHolder 對象存儲在 View 中。當 convertView 不爲空的時候則調用 View 的 getTag()方法,把 ViewHolder 重新取出。這樣所有控件的實例都緩存在了 ViewHolder 裏,就沒有必要每次都通過 findViewById()方法來獲取控件實例了。
setTag和getTag的用法
view的setTag和getTag方法其實很簡單,在實際編寫代碼的時候一個view不僅僅是爲了顯示一些字符串、圖片,有時我們還需要他們攜帶一些其他的數據以便我們對該view的識別或者其他操作。於是android 的設計者們就創造了setTag(Object)方法來存放一些數據和view綁定,我們可以理解爲這個是view 的標籤也可以理解爲view 作爲一個容器存放了一些數據。而這些數據我們也可以通過getTag() 方法來取出來。
到這裏setTag和getTag大家應該已經明白了。再回到上面的話題,我們通過convertview的setTag方法和getTag方法來將我們要顯示的數據來綁定在convertview上。如果convertview 是第一次展示我們就創建新的Holder對象與之綁定,並在最後通過return convertview 返回,去顯示;如果convertview 是回收來的那麼我們就不必創建新的holder對象,只需要把原來的綁定的holder取出加上新的數據就行了。