一、ListView 的運行機制
1、使用match_parent 定義ListView 的高度和寬度屬性。
定義 ListView 控件的高度和寬度要避免使用wrap_content,否則會導致BaseAdapter.getView 重複調用N次,儘量使用match_parent或固定值設置height和width。
2、緩存列表項
ListView 控件在設計上採用了只創建並顯示當前屏幕中的列表項的佈局對象(該佈局對象由BaseAdapter.getView方法的convertView參數保存)。
例如:一屏顯示十個列表項(包括部分顯示在屏幕上的列表項),則只創建十個convertView,ListView會反覆使用這十個convertView顯示進入當前的列表項的內容。
在向上滾屏時,當第一個convertView移出屏幕,該convertView將從屏幕下邊出現。同理,在向下滾屏時,若屏幕最下邊的convertView移出屏幕,該convertView移出屏幕,該convertView從最上邊進入屏幕。
提示:
一屏不一定顯示十個列表項,具體多少根據列表項佈局的高度和設備的高度而定。
實例案例:
package com.jxust.day05_08_listviewoptimize; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; public class MainActivity extends Activity { ListView mlvContact; List<ContactBean> mContacts; ContactAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); initView(); } private void initView() { mlvContact = (ListView) findViewById(R.id.lvContact); mAdapter = new ContactAdapter(mContacts, this); // 創建適配器 mlvContact.setAdapter(mAdapter); // 配置適配器 } private void initData() { String[] names = getResources().getStringArray(R.array.names); String[] phones = getResources().getStringArray(R.array.phones); mContacts = new ArrayList<ContactBean>(); for(int i = 0; i < phones.length;i++){ ContactBean contact = new ContactBean(names[i],phones[i]); mContacts.add(contact); } } class ContactAdapter extends BaseAdapter{ List<ContactBean> contacts; MainActivity context; public ContactAdapter(List<ContactBean> contacts, MainActivity context) { super(); this.contacts = contacts; this.context = context; } // 內部集合的長度 @Override public int getCount() { return contacts.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return null; } @Override public long getItemId(int position) { // TODO Auto-generated method stub return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { Log.i("main", "position="+position); ViewHolder holder = null; if(convertView == null){// 表示第一屏尚未創建列表項,convertView就表示的是null convertView = View.inflate(context, R.layout.item_contact, null); //創建佈局 holder = new ViewHolder(); //把convertView獲取到佈局的屬性賦給ViewHolder對象 holder.tvName = (TextView) convertView.findViewById(R.id.tvName); holder.tvPhone = (TextView) convertView.findViewById(R.id.tvPhone); convertView.setTag(holder); // tag是View中一個屬性,是Object類型的 }else{// 以後的滾動,出現其它的列表項 // 這樣就可以避免重複解析,提高效率 holder = (ViewHolder) convertView.getTag(); } // 取出當前的聯繫人 ContactBean contact = contacts.get(position); holder.tvName.setText(contact.getName()); holder.tvPhone.setText(contact.getPhone()); return convertView; } // 存放item_contact.xml中的兩個部件 class ViewHolder{ TextView tvName,tvPhone; } } }
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <!-- listView中的width和height要設置成match_parent可以避免BaseAdapter.getView 重複調用 --> <ListView android:id="@+id/lvContact" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="#ccc" android:dividerHeight="10dp" /> </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <TextView android:id="@+id/tvName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="張飛" android:textSize="20sp" /> <TextView android:id="@+id/tvPhone" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="13011335577" android:textSize="20sp" /> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Day05_08_ListViewOptimize</string> <string name="hello_world">Hello world!</string> <string name="action_settings">Settings</string> <string-array name="names"> <item>張飛</item> <item>王菲</item> <item>劉亦菲</item> <item>黃飛鴻</item> <item>李菲</item> <item>陳菲</item> <item>趙菲</item> <item>田菲</item> <item>鄭菲</item> <item>鄧菲</item> <item>岳飛</item> <item>郝菲</item> <item>咖啡</item> <item>啡啡</item> </string-array> <string-array name="phones"> <item>13011335577</item> <item>13155335577</item> <item>13311335577</item> <item>13511335577</item> <item>13711335577</item> <item>13811335577</item> <item>13911335577</item> <item>15011335577</item> <item>13111335577</item> <item>18211335577</item> <item>18911335577</item> <item>13088335577</item> <item>13399335577</item> <item>13078335577</item> </string-array> </resources>
通過在logcat觀察發現,經過對convertView的處理,避免了重複解析,從而提高了效率。
並且,特別要注意的是activity_main.xml中有關width和height的取值,我們最好是選擇match_parent
否則:
發現,BaseAdapter.getView被重複調用了3次,這樣就會導致資源的浪費。
但是如果我們使用match_parent