Android中ListView的緩存機制

一、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