一、使用ViewHolder模式提高效率
ViewHolder模式充分利用ListView的视图缓存机制,避免了每次在调用getView()方法的时候都去通过findViewById()方法实例化控件。使用ViewHolder模式,需要在自定义Adapter中定义一个内部类ViewHolder,并将布局中的控件作为成员变量。起始时,ListView创建的Cell条数量是当前屏幕显示的Cell条数,在向上滚动时,新显示的Cell是滚出屏幕的Cell的复用。与iOS的TableView的Cell优化一样。
1. 示例
1 public class ViewHolderAdapter extends BaseAdapter 2 { 3 private List<String> mData; 4 private LayoutInflater mInflater; 5 6 public ViewHolderAdapter(Context context, List<String> data) 7 { 8 mData = data; 9 mInflater = LayoutInflater.from(context); 10 } 11 12 @Override 13 public int getCount() 14 { 15 return mData.size(); 16 } 17 18 @Override 19 public Object getItem(int position) 20 { 21 return mData.get(position); 22 } 23 24 @Override 25 public long getItemId(int position) 26 { 27 return position; 28 } 29 30 @Override 31 public View getView(int position, View convertView, ViewGroup parent) 32 { 33 ViewHolder holder = null; 34 // 判断是否缓存 35 if (convertView == null) 36 { 37 holder = new ViewHolder(); 38 // 通过LayoutInflater实例化布局 39 convertView = mInflater.inflate(R.layout.viewholder_item, null); 40 holder.mImgView = (ImageView) convertView.findViewById(R.id.icon); 41 holder.mTitle = (TextView) convertView.findViewById(R.id.title); 42 convertView.setTag(holder); 43 } 44 else 45 { 46 holder = (ViewHolder) convertView.getTag(); 47 } 48 49 holder.mImgView.setBackgroundResource(R.mipmap.ic_launcher); 50 holder.mTitle.setText(mData.get(position)); 51 52 return convertView; 53 } 54 55 public final class ViewHolder 56 { 57 public ImageView mImgView; 58 public TextView mTitle; 59 } 60 }
二、设置项目间分隔线
系统提供了divider(颜色)和dividerHeight(高度)这两个属性实例ListView项目间分隔线。
1 android:divider="@android:color/darker_gray" 2 android:dividerHeight="10dp"
设置透明分隔线:
1 android:divider="@null"
三、隐藏ListView的滚动条
ListView在滚动时,默认在右侧显示滚动条,可以通过scrollbars属性设置不显示滚动条:
1 android:scrollbars="none"
四、取消ListView的Item点击效果
通过listSelector属性设置Cell选中后的颜色:
1 android:listSelector="#000000"
使用系统自带的透明色来实现:
1 android:listSelector="@android:color/transparent"
五、动态修改ListView
ListView中的数据在某些情况下是需要变化的,当然可以通过重新设置ListView的Adapter来更新ListView的显示,但是,这需要重新获取数据,相当于重新创建ListView。可以通过以下方法实现动态更新ListView数据显示:
1 mData.add("test"); 2 mHolderAdapter.notifyDataSetChanged();
PS:当修改了传递给Adapter的映射List之后,只需要通过调用Adapter的notifyDataSetChanged()方法,通过ListView更新数据源即可完成对ListView的动态修改。
六、遍历ListView中所有的Item
ListView作为一个ViewGroup,提供了各种操纵子View的方法,最常用的就是通过getChildAt()方法获取第i个子View,代码如下所示:
1 for (int idx = 0; idx < mListView.getChildCount(); idx++) 2 { 3 View view = mListView.getChildAt(idx); 4 }
七、处理空ListView
在列表无数据时,ListView不显示数据或者提示,ListView提供了setEmptyView()方法,通过这个方法可以给ListView设置一个空数据下显示的默认提示。代码如下所示:
1 // list view 2 mListView = (ListView) findViewById(R.id.listview); 3 mHolderAdapter = new ViewHolderAdapter(this, mData); 4 mListView.setAdapter(mHolderAdapter); 5 // 获取在空List时,显示的View 6 mNotDataView = (TextView) findViewById(R.id.emptyListview); 7 // 设置ListView在空数据时,显示的View 8 mListView.setEmptyView(mNotDataView);
八、ListView滑动监听
1. OnTouchListener是View中的监听事件,通过监听ACTION_DOWN、ACTION_MOVE、ACTION_UP这三个事件发生时的坐标,就可以根据坐标判断用户滑动的方向:
1 mListView.setOnTouchListener(new View.OnTouchListener() 2 { 3 @Override 4 public boolean onTouch(View v, MotionEvent event) 5 { 6 switch (event.getAction()) 7 { 8 case MotionEvent.ACTION_DOWN: 9 { 10 // 触摸时操作 11 Log.d(TAG, "Down"); 12 break; 13 } 14 case MotionEvent.ACTION_MOVE: 15 { 16 // 移动时操作 17 Log.d(TAG, "Move"); 18 break; 19 } 20 case MotionEvent.ACTION_UP: 21 { 22 // 手指离开屏幕时操作 23 Log.d(TAG, "Up"); 24 break; 25 } 26 } 27 return false; 28 } 29 });
2. OnScrollListener是AbsListView中的监听事件,一般常用方法, 如下代码所示:
1 mListView.setOnScrollListener(new AbsListView.OnScrollListener() 2 { 3 @Override 4 public void onScrollStateChanged(AbsListView view, int scrollState) 5 { 6 switch (scrollState) 7 { 8 case AbsListView.OnScrollListener.SCROLL_STATE_IDLE: 9 { 10 // 滑动停止时 11 Log.d(TAG, "SCROLL_STATE_IDLE"); 12 break; 13 } 14 case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 15 { 16 // 正在滚动 17 Log.d(TAG, "SCROLL_STATE_TOUCH_SCROLL"); 18 break; 19 } 20 case AbsListView.OnScrollListener.SCROLL_STATE_FLING: 21 { 22 // 手指抛开时,即手指用力滑动,在离开后ListView由于惯性继续滑动 23 Log.d(TAG, "SCROLL_STATE_FLING"); 24 break; 25 } 26 } 27 } 28 29 int lastVisibleItemPosition = 0; 30 @Override 31 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) 32 { 33 // 滚动时,一直调用 34 Log.d(TAG, "onScroll"); 35 36 // 判断滚动方向 37 if (firstVisibleItem > lastVisibleItemPosition) 38 { 39 // 向上滚动 40 Log.d(TAG, "Scroll Up"); 41 } 42 else if (firstVisibleItem < lastVisibleItemPosition) 43 { 44 // 向下滚动 45 Log.d(TAG, "Scroll Down"); 46 } 47 lastVisibleItemPosition = firstVisibleItem; 48 } 49 });
- AbsListView.OnScrollListener.SCROLL_STATE_IDLE:滚动停止时
- AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:正在滚动;
- AbsListView.OnScrollListener.SCROLL_STATE_FLING:手指抛开时,即手指用力滑动,在离开后ListView由于惯性继续滑动;
在ListView滚动时会一直回调,而方法中的后三个int类型的参数,则非常精确的显示了当前ListView滚动的状态,这三个参数如下所示:
- firstVisibleItem:当前能看见的第一个Item的id(从0开始);
- visibleItemCount:当前能看见的Item个数;
- totalItemCount:当前ListView的Item总数;
在ListView中还有一些其它的方法,如下代码所示:
1 // 获取可视区域内最后一个Item的id 2 mListView.getLastVisiblePosition(); 3 // 获取可视区域内的第一个Item的id 4 mListView.getFirstVisiblePosition();