Android UI設計——ListView控制元件使用優化(五)
在上一篇部落格中《Android UI設計——ListView控制元件和自定義Adapter(四) 》(連結)中我們通過一個例項來了解和練習瞭如何去自定義Adapter,本節我們將會繼續以《Android UI設計——ListView控制元件和自定義Adapter(四) 》中的例子來學習ListView在使用中是如何優化的,如果本節看著吃力的話,建議先檢視上篇哦……
convertView優化
有時候我們在ListView中顯示的資料非常多,雖然我們手機的螢幕有限,資料可以通過滾動的方式為我們顯示。在上一節中我們在自定義的Adapter的getView方法中使用的是將資料一次性全部載入完成,以至於當我們開啟我們應用程式時速度非常慢,因為大量的資料是一次性載入完成的,降低了資料的讀取速度,增加佔用的記憶體。那麼這個時候我們就可以通過convertView來解決這個問題。
解決原理
convertView是採用一種”快取“的方式。資料載入時首先載入在手機螢幕上出現的View,當滑動手機螢幕時,會有View被劃出螢幕,同時也會有新的View進入螢幕。此時convertView就將出屏的View儲存下來作為下一個進入螢幕的新的View使用。
Android中有個叫做Recycler的構件,下圖是他的工作原理:
1. ListView先請求一個type1檢視(getView)然後請求其他可見的專案。convertView在getView中是空(null)的。
2. 當item1滾出螢幕,並且一個新的專案從螢幕低端上來時,ListView再請求一個type1檢視。convertView此時不是空值了,它的值是item1。你只需設定新的資料然後返回convertView,不必重新建立一個檢視。
(此處引用於
下面我們對我們上節的示例進行優化:
public class StudentAdapter extends BaseAdapter{
private List<Student> mData;//定義資料。
private LayoutInflater mInflater;//定義Inflater,載入我們自定義的佈局。
/*
定義構造器,在Activity建立物件Adapter的時候將資料data和Inflater傳入自定義的Adapter中進行處理。
*/
public StudentAdapter(LayoutInflater inflater,List<Student> data){
mInflater = inflater;
mData = data;
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertview, ViewGroup viewGroup) {
/*
這是上節中的程式碼,將全部資料載入到ListView中
*/
//獲得ListView中的view
// View viewStudent = mInflater.inflate(R.layout.item_simpleadapter,null);
//獲得學生物件
// Student student = mData.get(position);
//
// ImageView imagePhoto = (ImageView) viewStudent.findViewById(R.id.image_photo);
// TextView name = (TextView) viewStudent.findViewById(R.id.textview_name);
// TextView age = (TextView) viewStudent.findViewById(R.id.textview_age);
// TextView sex = (TextView) viewStudent.findViewById(R.id.textview_sex);
// TextView hobby = (TextView) viewStudent.findViewById(R.id.textview_hobby);
// imagePhoto.setImageResource(student.getImag());
// name.setText(student.getName());
// age.setText(student.getAge());
// sex.setText(student.getSex());
// hobby.setText(student.getHobby());
/*
convertview 優化
*/
//判斷convertview 是否為空,如果為空需要對其進行佈局。
if(convertview == null){
convertview = mInflater.inflate(R.layout.item_simpleadapter,null);
}
//不管是否為空,都需要載入資料。
Student student = mData.get(position);
ImageView imagePhoto = (ImageView) convertview.findViewById(R.id.image_photo);
TextView name = (TextView) convertview.findViewById(R.id.textview_name);
TextView age = (TextView) convertview.findViewById(R.id.textview_age);
TextView sex = (TextView) convertview.findViewById(R.id.textview_sex);
TextView hobby = (TextView) convertview.findViewById(R.id.textview_hobby);
imagePhoto.setImageResource(student.getImag());
name.setText(student.getName());
age.setText(student.getAge());
sex.setText(student.getSex());
hobby.setText(student.getHobby());
return convertview;
}
}
ViewHolder優化
由於fingViewById是一個耗時間的操作,在convertView優化中,雖然將View進行了快取,但還是判斷convertView是否為空後還是需要對其佈局和資料的對映,以至於消耗了時間和記憶體。此時我們可以通過ViewHolder來解決這個問題。
在自定義的Adapter中定義一個內部類ViewHolder,通過ViewHolder將顯示在ListView中的資料通過findViewById獲取到然後在接下來不為空的convertView直接獲取ViewHolder的Tag即可。
看程式碼:
public class StudentAdapter extends BaseAdapter{
private List<Student> mData;//定義資料。
private LayoutInflater mInflater;//定義Inflater,載入我們自定義的佈局。
/*
定義構造器,在Activity建立物件Adapter的時候將資料data和Inflater傳入自定義的Adapter中進行處理。
*/
public StudentAdapter(LayoutInflater inflater,List<Student> data){
mInflater = inflater;
mData = data;
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertview, ViewGroup viewGroup) {
//建立ViewHolder的物件。
ViewHolder viewHolder = null;
//獲得Item位置上的資料。
Student student = mData.get(position);
//convertview 優化
if(convertview == null){
convertview = mInflater.inflate(R.layout.item_simpleadapter,null);
viewHolder = new ViewHolder();
viewHolder.imagePhoto = (ImageView) convertview.findViewById(R.id.image_photo);
viewHolder.name = (TextView) convertview.findViewById(R.id.textview_name);
viewHolder.age = (TextView) convertview.findViewById(R.id.textview_age);
viewHolder.sex = (TextView) convertview.findViewById(R.id.textview_sex);
viewHolder.hobby = (TextView) convertview.findViewById(R.id.textview_hobby);
//convertview為空時,ViewHolder將顯示在ListView中的資料通過findViewById獲取到。
convertview.setTag(viewHolder);
}else{
//convertview不為空時,直接獲取ViewHolder的Tag即可。
viewHolder = (ViewHolder) convertview.getTag();
}
viewHolder.imagePhoto.setImageResource(student.getImag());
viewHolder.name.setText(student.getName());
viewHolder.age.setText(student.getAge());
viewHolder.sex.setText(student.getSex());
viewHolder.hobby.setText(student.getHobby());
return convertview;
}
/*
ViewHolder內部類
*/
class ViewHolder{
TextView name;
TextView age;
TextView sex;
TextView hobby;
ImageView imagePhoto;
}
}
滾動變黑問題優化
有時候會出現滾動變黑問題,解決方法是設定:cacheColorHint屬性,將值設定為透明色。在ListView控制元件佈局中設定:
android:cacheColorHint="#00000000"
設定分隔線
如圖所示的箭頭指向的分隔線是可以設定的哦:
通過在ListView中新增如下語句:
android:divider="#f00000"
android:dividerHeight="2dp"