Android關於ListView的優化

當使用自定義Adapter來建立ListView時,注意如下幾點,能夠起到優化的做用:android

  1. 在XML文件佈局ListView時,android:layout_height不要定義爲wrap_content,而且ListView的全部父節點佈局的android:layout_height都不要定義爲wrap_content。 正常狀況下,一屏幕顯示多少item,那麼Adapter中的getView()函數會被調用幾回。若是android:layout_height定義爲wrap_content,那麼getView()將被成倍調用。因此,建議使用fill_parent或者固定高度尺寸。ide

  2. 在getView()函數中,經過convertView != null的判斷來複用convertView。函數

  3. 定義一個靜態內部類ViewHolder,其中包含了item佈局中的各個控件。 在初始化convertView時,也new一個ViewHolder,來保存convertView中的各個子控件。當複用convertView時,直接對相應的ViewHolder進行改動便可。避免convertView的findViewById()的耗時操做。佈局

activity_demo_list.xml優化

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="fill_parent" 
	android:layout_height="fill_parent"
	android:orientation="vertical">
	<ListView
	    android:id="@+id/list"
	    android:layout_width="fill_parent"
	    android:layout_height="fill_parent"/>
</LinearLayout>

demo_list_item.xmlthis

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center_vertical">
    <TextView 
        android:id="@+id/text"
        android:layout_width="fill_parent"
    	android:layout_height="fill_parent"
    	android:textSize="20sp"
    	android:textColor="@android:color/background_dark"/>
</LinearLayout>
public class DemoSimpleAdapter extends BaseAdapter {
	private List<String> mData = new ArrayList<String>();
	private Context mContext;
	private LayoutInflater mFlater;
	private ViewHolder viewHolder = null;

	private static class ViewHolder {
		public View bgView;
		public TextView text;
	}

	public DemoSimpleAdapter(Context context, List<String> data){
		this.mContext = context;
		this.mData = data;
		this.mFlater = LayoutInflater.from(mContext);
	}
	
	public void resetData(List<String> data){
		this.mData = data;
		this.notifyDataSetChanged();
	}

	// 調用notifyDataSetChanged()將重繪整個可見的ListView
	// 下面自定義函數,只重繪position所在的某一項item:
	public boolean notifyDataSetChanged(int position, ListView listView) {
		boolean updated = false;
		int firstVisiblePosition = listView.getFirstVisiblePosition();
		int lastVisiblePosition = listView.getLastVisiblePosition();
		if ( position >= firstVisiblePosition && position <= lastVisiblePosition ) {
			View v = listView.getChildAt(position - firstVisiblePosition);
			if ( v != null ) {
				View layout = v.getContentView();
				ViewCache holder = (ViewCache) layout.getTag();
				if ( holder != null ) {
					resetViewCacheHolder(position, holder);
					updated = true;
				}
			}
		}
		return updated;
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return mData.size();
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return mData.get(position);
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		
		if ( convertView == null ) {
			convertView = mFlater.inflate(R.layout.demo_list_item,null);
			viewHolder = new ViewHolder();
			viewHolder.bgView = convertView;
			viewHolder.text = (TextView) convertView.findViewById(R.id.text);
			convertView.setTag(viewHolder);
		} else {
			viewHolder = (ViewHolder) convertView.getTag();
		}
		resetViewCacheHolder(position, viewCache);
		return convertView;
	}

	private void resetViewCacheHolder(int position, ViewHolder holder) {
		if( position%2 == 0 ) {
            		holder.bgView.setBackgroundColor(Color.parseColor("#ffffff"));
        	} else {
            		holder.bgView.setBackgroundColor(Color.parseColor("#f8fbd9"));
        	}
        	holder.text.setText(mData.get(position));
	}

}

更多參考:http://mobile.51cto.com/abased-445617.htmcode

4.分頁加載:每次只加載固定數量的items,當滑動到ListView底部時,FooterView顯示「正在加載」,加載其餘數據。xml

<1>. 定義FooterView的佈局:paging_load_list_footer.xmlhtm

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:paddingTop="10dip"
    android:paddingBottom="10dip"
    android:background="@android:color/background_light"
    android:orientation="horizontal"
    android:gravity="center" >
    <ProgressBar 
        android:id="@+id/pb_refresh"
        android:layout_width="20dip"
        android:layout_height="20dip"
        style="@android:attr/progressBarStyleSmall" />
    <TextView 
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dip"
        android:textSize="14sp"
        android:textColor="@android:color/background_dark"
        android:text="正在加載中..." />
</LinearLayout>

<2>. 定義上拉刷新監聽器OnFootLoadingListener。索引

public interface OnFootLoadingListener{  
    /** 
     * 這裏是執行後臺獲取數據的過程 
     */  
    void onFootLoading();  
}

<3>. 定義實現分頁加載功能的PagingLoadListView

public class PagingLoadListView extends ListView {
	public int mDataTotalSize = 0;  	 	//數據集的所有條數
	private int mVisibleLastIndex;   		//最後的可視項索引
	private int mTotalItemCount;			//ListView已經加載的數據項(若是有HeaderView,要-1。若是有FooterView,也要-1)。
	private View mFooterView;
	private boolean mIsFootLoading = false; 	//是否正在加載底部數據
	private OnFootLoadingListener mFootLoadingListener = null;
	
	public PagingLoadListView(Context context) {
		super(context);
		init();
	}
	
	 public PagingLoadListView(Context context, AttributeSet attrs) {
		 super(context, attrs);
		 init();
	}

	public PagingLoadListView(Context context, AttributeSet attrs, int defStyle) {
	    super(context, attrs, defStyle);
	    init();
	}
	
	public void setOnFootLoadingListener(OnFootLoadingListener listener) {
		mFootLoadingListener = listener;
	}
	
	public void onFootLoadingComplete(){
		mIsFootLoading = false;
	}
	
	private void init() {
		// 動態加載底部View
		LayoutInflater flater = LayoutInflater.from(getContext());
		mFooterView = flater.inflate(R.layout.paging_load_list_footer, null);
		this.addFooterView(mFooterView);
		// 設置透明背景
		this.setCacheColorHint(0x00000000);
		
		this.setOnScrollListener(new OnScrollListener(){

			@Override
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {
				mVisibleLastIndex = getLastVisiblePosition();
				mTotalItemCount = totalItemCount - 1; //要減去FooterView
				
				if( mVisibleLastIndex == mTotalItemCount && mVisibleLastIndex != -1 && //滑動到底部
						mDataTotalSize == mTotalItemCount ) { //所有數據都加載完了
					ProgressBar pb = (ProgressBar) mFooterView.findViewById(R.id.pb_refresh);
					pb.setVisibility(View.GONE);
					TextView tv = (TextView) mFooterView.findViewById(R.id.tv_title);
					if ( mDataTotalSize != 0 ) {
						tv.setText("數據加載完畢");
					} else {
						tv.setText("沒有數據");
					}
				}  

			}

			@Override
			public void onScrollStateChanged(AbsListView view, int scrollState) {  
				if( mIsFootLoading == false && scrollState == SCROLL_STATE_IDLE &&
						mFootLoadingListener != null  && mVisibleLastIndex > 0  && 
						mVisibleLastIndex == mTotalItemCount  && mVisibleLastIndex != mDataTotalSize ) {
					//執行底部加載
					mIsFootLoading = true;
					mFootLoadingListener.onFootLoading();
				}  
			}
		});
	}
}

<4>. 使用PagingLoadListView,設置OnFootLoadingListener,在回調函數中先從新加載數據,而後調用onFootLoadingComplete()。

5.ScrollView嵌套ListView只顯示一行的解決方案: <1>. 重寫ListView:

@Override
	public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)   
    {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,   
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }

<2>. 從新計算ListView的高度,解決ScrollView和ListView兩個View都有滾動的效果,在嵌套使用時起衝突的問題,設置scrollView中的ListView內容所有顯示,不能滑動,將滑動交給scrollView去作. 注意:ListView的每一個Item必須是LinearLayout,在設置ListView的Adapter後調用此方法,而且ListView的高度要指定,這樣才能從新計算,不要設成wrapcontent.

public static void setListViewHeightBasedOnChildren(ListView listView) { 
	    if(listView == null) return;
	    ListAdapter listAdapter = listView.getAdapter(); 
	    if (listAdapter == null) { 
	        return; 
	    } 

	    int totalHeight = 0; 
	    for (int i = 0; i < listAdapter.getCount(); i++) { 
	        View listItem = listAdapter.getView(i, null, listView);
	        int desiredWidth = MeasureSpec.makeMeasureSpec(
	        		listView.getWidth(), MeasureSpec.AT_MOST);
	        listItem.measure(desiredWidth, 0);
	        totalHeight += listItem.getMeasuredHeight(); 
	    } 

	    ViewGroup.LayoutParams params = listView.getLayoutParams(); 
	    params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); 
	    listView.setLayoutParams(params);
	}
相關文章
相關標籤/搜索