當使用自定義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); }