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);
	}
相关文章
相关标签/搜索