ListView性能優化

對於一個應用來說,用戶體驗是一個App來說除了定位以外,最重要的一個因素,而ListView這個組件是一個非常消耗內存的組件,對ListView組件的優化直接影響了應用的用戶體驗
最簡單的處理
提升 ListView 的運行效率
之所以說 ListView 這個控件很難用, 就是因爲它有很多的細節可以優化, 其中運行效率
就是很重要的一點。 目前我們ListView的運行效率是很低的, 因爲在FruitAdapter的getView()
方法中每次都將佈局重新加載了一遍,當 ListView快速滾動的時候這就會成爲性能的瓶頸。
仔細觀察,getView()方法中還有一個 convertView 參數,這個參數用於將之前加載好的
佈局進行緩存,以便之後可以進行重用。修改 FruitAdapter 中的代碼,如下所示:

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;
}
}

可以看到,現在我們在 getView()方法中進行了判斷,如果 convertView 爲空,則使用
LayoutInflater 去加載佈局,如果不爲空則直接對 convertView 進行重用。這樣就大大提高了
ListView的運行效率,在快速滾動的時候也可以表現出更好的性能。
不過, 目前我們的這份代碼還是可以繼續優化的, 雖然現在已經不會再重複去加載佈局,
但是每次在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 = new ViewHolder();
viewHolder.fruitImage = (ImageView) view.findViewById
(R.id.fruit_image);
viewHolder.fruitName = (TextView) view.findViewById
(R.id.fruit_name);
view.setTag(viewHolder); //  將ViewHolder 存儲在View 中
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag(); //  重新獲取ViewHolder
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder {
ImageView fruitImage;
TextView fruitName;
}
}

具體講解:
第一:
重用了convertView,很大程度上的減少了內存的消耗。通過判斷convertView是否爲null,是的話就需要產生一個視圖出來,然後給這個視圖數據,最後將這個視圖返回給底層,呈獻給用戶。
特點:如果當前的convertView爲null,則通過LayoutInflat產生一個view。

publicViewgetView(int position,View convertView,View Groupparent)
{
if(convertView==null)
{
convertView=LayoutInflater.from(context).inflate(R.layout.section_list_item1,null);
}
TextViewtv_name=(TextView)convertView.findViewById(R.id.contact_contactinfoitem_tv_name);
TextViewtv_phone=(TextView)convertView.findViewById(R.id.contact_contactinfoitem_tv_phoneNum);
ContactInfo1confo=contacts.get(position);
if(confo!=null){//toseteveryitem'stext
tv_name.setText(confo.getContactName());
tv_phone.setText(confo.getContact_Phone());
}
return   convertView;
} 

第二:
上面的寫法會有一個缺點,就是每次在getVIew的時候,都需要重新的findViewById,重新找到控件,然後進行控件的賦值以及事件相應設置。這樣其實在做重複的事情,因爲的geiview中,其實包含有這些控件,而且這些控件的id還都是一樣的,也就是其實只要在view中findViewById一次,後面無需要每次都要findViewById了。
下面給出第二種寫法
寫發的特點,通常有一個內部類classViewHolder,這個ViewHolder,用來標識view中一些控件,方便進行一些事件相應操作的設置,比如onClick等等,這樣可以不用每次都要findViewById了,減少了性能的消耗。同時重用了convertView,很大程度上的減少了內存的消耗。
代碼

ViewCode
publicViewgetView(int  position,View convertView,View   Groupparent)
{
ViewHolder holder;
if(convertView==null){
convertView=LayoutInflater.from(context).inflate(R.layout.section_list_item1,null);
holder=new ViewHolder();
holder.tv_name=(TextView)convertView.findViewById(R.id.contact_contactinfoitem_tv_name);
holder.tv_phone=(TextView)convertView.findViewById(R.id.contact_contactinfoitem_tv_phoneNum);
convertView.setTag(holder);
}
else
{
holder=(ViewHolder)convertView.getTag();
}
ContactInfo 1confo=contacts.get(position);
Log.i("my","confo"+confo.getContactName());
if(confo!=null){//toseteveryitem'stext

holder.tv_name.setText(confo.getContactName());
holder.tv_phone.setText(confo.getContact_Phone());
}
return convertView;
}
class ViewHolder
{
TextView tv_name,tv_phone;
}

 個人覺得這個寫法是最舒服的,最舒服的意思是看着代碼有一種很爽,看的很清晰。
特點,使用了內部類classViewHolder、重用了convertView。
區別第二種寫法是,使用了一個臨時變量Viewview=convertView,然後修改view,最後返回view
代碼如下:

ViewCode
@Override
publicViewgetView(int   position,View   convertView,View   Groupparent)
{
Viewview=convertView;
ViewHolder holder;
if(view==null){
view=LayoutInflater.from(context).inflate(R.layout.section_list_item1,null);
holder=new  ViewHolder();
holder.tv_name=(TextView)view.findViewById(R.id.contact_contactinfoitem_tv_name);
holder.tv_phone=(TextView)view.findViewById(R.id.contact_contactinfoitem_tv_phoneNum);
view.setTag(holder);
}
else
{
holder=(ViewHolder)view.getTag();
}
ContactInfo1confo=contacts.get(position);
Log.i("my","confo"+confo.getContactName());
if(confo!=null){//toseteveryitem'stext

holder.tv_name.setText(confo.getContactName());
holder.tv_phone.setText(confo.getContact_Phone());
}
returnview;
}
classViewHolder
{
TextViewtv_name,tv_phone;
} 

秉承Geek精神 ,還會對這個代碼進行優化
最終 目前 最完美的寫法:

ViewCode
@Override
publicViewgetView(intposition,ViewconvertView,ViewGroupparent)
{
Viewview=convertView;
ViewHolderholder;
if(view==null){
view=LayoutInflater.from(context).inflate(R.layout.section_list_item1,null);
holder=newViewHolder();
holder.tv_name=(TextView)view.findViewById(R.id.contact_contactinfoitem_tv_name);
holder.tv_phone=(TextView)view.findViewById(R.id.contact_contactinfoitem_tv_phoneNum);
view.setTag(holder);
}
else
{
holder=(ViewHolder)view.getTag();
}
ContactInfo1confo=contacts.get(position);
Log.i("my","confo"+confo.getContactName());
if(confo!=null){//toseteveryitem'stext

holder.tv_name.setText(confo.getContactName());
holder.tv_phone.setText(confo.getContact_Phone());
}
returnview;
}
static class ViewHolder
{
TextViewtv_name,tv_phone;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章