虽然在Android 5.X时代,RecyclerView在很多地方都在逐渐取代ListView,但是毕竟ListView要成熟很多,所以还是有必要熟练掌握的。
ListView常用优化技巧
1.使用ViewHolder模式提高效率
ViewHolder模式是提高ListView效率的一个很重要的方法。ViewHolder模式充分利用了ListView的视图缓存机制,避免了每次在调用getView()的时候都去通过findViewById实例化控件。
/**
* 自定义Adapter
* Created by LGL on 2016/3/10.
*/
public class MyAdapter extends BaseAdapter {
private List<String> mData;
private LayoutInflater mInflater;
//构造方法
public MyAdapter(Context context, List<String> mData) {
this.mData = mData;
mInflater = LayoutInflater.from(context);
}
//返回长度
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
//判断是否有缓存
if (convertView == null) {
viewHolder = new ViewHolder();
//通过LayoutInflater去实例化布局
convertView = mInflater.inflate(R.layout.item, null);
viewHolder.img = (ImageView) convertView.findViewById(R.id.img);
viewHolder.tv = (TextView) convertView.findViewById(R.id.tv);
convertView.setTag(viewHolder);
} else {
//通过TAG找到缓存的布局
viewHolder = (ViewHolder) convertView.getTag();
}
//设置布局中要显示的东西‘
viewHolder.tv.setText(mData.get(position));
return convertView;
}
public final class ViewHolder {
public ImageView img;
public TextView tv;
}
}
2.设置项目间分隔线
ListView的各个item之间,可以通过设置分割线来进行区分,系统提供了divider和dividerHeight这样两个属性来帮助我们实现这一功能。通过这两个属性,也可以控制ListView之间的分隔线和它的高度。当然,分隔线不仅仅可以设置为一个颜色,同样也可以设置为一个图片资源。
android:dividerHeight="10dp"
android:divider="@android:color/holo_blue_bright"
但是如果你是需要去掉这个分割线的话,其实也好办
android:divider="@null"
3.隐藏ListView的滚动条
android:scrollbars="none"
4.取消ListView的Item点击效果
当点击ListView中的一项时,系统默认会出现一个点击效果,在Android5.x上是一个波纹效果,而在Android5.X之下的版本则是一个改变背景颜色的效果,但可以通过listSelector属性来取消掉点击后的回馈效果。
android:listSelector="@android:color/transparent"
或你也可以直接填#00000000
5.设置ListView需要显示在第几页
ListView以Item为单位进行显示,默认显示在第一个Item,当需要指定具体显示的Item时,可以通过:
listview.setSelection(N);//N表示第几个item
当然这个方法类似scrollTo,是瞬间完成的移动。除此之外,还可以进行平滑的移动:
listview.smoothScrollBy(distance,duration);
listview.smoothScrollByOffset(offset);
listview.smoothScrollToPosition(index);
6.动态修改ListView
list.add("我是新增加的数据");
//通知刷新
myAdapter.notifyDataSetChanged();
7.遍历ListView中的所有Iitem
ListView作为一个ViewGroup,为什么提供了操纵子View的各种方法,最常用的就是通过getChildAt()来获取第i个子View
for (int j = 0; j < listview.getChildCount();j++){
View v = listview.getChildAt(12);
}
8.处理空ListView
ListView用于展示列表数据,但当列表中无数据时,ListView不会显示任何数据或提示,按照完善用户体验的需求,这个应该给以无数据的提示。ListView提供了一个方法---------setEmptyView(),通过这个方法我们可以给ListView设置一个空数据下显示的默认提示。
listview.setEmptyView(findViewById(R.id.tv_null));
9.ListView滑动监听
ListView的滑动监听,是ListView中最重要的技巧,很多重写ListView,基本上都是在滑动事件上下功夫,通过判断滑动事件进行不同的逻辑处理。而为了更加精确地监听滑动事件,开发者通常还需要使用GestureDetector手势识别、VelocityTracker滑动速度检测等辅助类来完成更好地监听。这里介绍两种监听ListView滑动事件的方法,一个是通过onTouchListener来实现监听,另一个是使用OnScrollListener来实现监听。
OnTouchListener
OnTouchListener是View的监听事件,通过监听ACTION_DOWN,UP,MOVE这三个事件发生时的坐标,就可以根据坐标判断用户滑动的方向,并在不同的事件中进行相应的逻辑处理,这种方式的使用代码:
listview.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
//触摸时操作
break;
case MotionEvent.ACTION_MOVE:
//移动时操作
break;
case MotionEvent.ACTION_UP:
//离开时操作
break;
}
return true;
}
});
OnScrollListener
OnScrollListener是AbsListView的监听事件,他封装了很多ListView的相关信息,所以用起来很灵活
listview.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int i) {
switch (i) {
case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
//滚动停止
break;
case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
//正在滚动
break;
case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
//手指抛动时,即手指用力滑动的时候
break;
}
}
@Override
public void onScroll(AbsListView absListView, int i, int i1, int i2) {
//滚动的时候一直在调用
}
});
OnScrollListener中有两个回调方法onScrollStateChanged()和onScroll
- OnScrollListener.SCROLL_STATE_IDLE://滚动停止
- OnScrollListener.SCROLL_STATE_TOUCH_SCROLL://正在滚动
- OnScrollListener.SCROLL_STATE_FLING://手指抛动时,即手指用力滑动的时候
当用户没有做手指抛动的动作时,这个方法只会调用2次,否则就调用三次,差别就在于手指抛动的这个状态,通常情况下,我们会在这个方法中通过不同的状态来标注一些FLAG,我们来看下onScrill()这个方法
/**
* firstVisibleItem:表示在现时屏幕第一个ListItem(部分显示的ListItem也算)在整个ListView的位置(下标从0开始)
* visibleItemCount:表示在现时屏幕可以见到的ListItem(部分显示的ListItem也算)总数
* totalItemCount:表示ListView的ListItem总数
* listView.getFirstVisiblePosition()表示在现时屏幕第一个ListItem(第一个ListItem部分显示也算)在整个ListView的位置(下标从0开始)
* listView.getLastVisiblePosition()表示在现时屏幕最后一个ListItem(最后ListItem要完全显示出来才算)在整个ListView的位置(下标从0开始)
*/
if(i+i1 == i2 &&i2>0){
//滚动到最后一行
}
int i3 = i;
if(i>i3){
//上滑
}else if(i<i3){
//下滑
}
//获取可视区域内最后一个item的id
listview.getLastVisiblePosition();
//获取可视区域内第一个item的id
listview.getFirstVisiblePosition();
ListView常用拓展
具有弹性的ListView
Android默认的ListView在滚动到顶端或者低端的时候,并没有很好的提示。在Android 5.X中,Google为这样的行为只添加了一个半月形的阴影效果。
而在IOS系统中,列表都是具有弹性的,即滚动到底端或者顶端后会继续往下或者往上滑动一段距离。不得不说,这样的设计的确更加的友好,虽然不知道Google为什么不模仿这样的设计,但我们可以自己修改ListView。
我们在查看ListView的源码的时候会发现一个控制滑动到边缘的处理方法
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
我們可以看到这样一个参数maxOverScrollY,就是他负责控制滑动的个数的,默认是0,我们重写ListView
/**
* 弹性ListView
* Created by lgl on 16/3/20.
*/
public class ListViewScroll extends ListView {
private int mMaxOverdistance =100;
public ListViewScroll(Context context, AttributeSet attrs) {
super(context, attrs);
//通过分辨率来调节滑动尺度
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
float density = metrics.density;
mMaxOverdistance = (int)(density*mMaxOverdistance);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, mMaxOverdistance, maxOverScrollY, isTouchEvent);
}
}