当使用自定义Adapter来建立ListView时,注意如下几点,能够起到优化的做用:android
在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
在getView()函数中,经过convertView != null的判断来复用convertView。函数
定义一个静态内部类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); }