android-ListView使用技巧以及优化
Posted hankzhouandroid
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android-ListView使用技巧以及优化相关的知识,希望对你有一定的参考价值。
引子
ListView:android里面经典的列表view,一般我们都会去自定义adapter来达到业务需求,必要的时候还会继承ListView,重写其中某些方法形成 我特有的ListView。本文总结一些ListView的使用技巧,以及性能优化的方法。
使用技巧:
1)ViewHolder 模式提高效率
这里,有一个知识背景需要强调,用过自定义Adapter的同学一定知道,继承BaseAdapter之后,需要重写几个方法,其中代码篇幅最多的就是getView(...)。而 getView()在list滑动的过程中,有可能重复调用。这里,涉及到ListView的View缓存机制,已经生成的item,会在重新显示(从不可见到可见)之后,再次执行getView。所以,如果在getView方法中打印日志,可以看到,position值相同的item 的getView,会被多次执行。具体的机制,我暂时想不起来了。
而重写getView,我们会在里面用LayoutInflater去实例化item的布局,每一次实例化都是需要消耗资源,消耗时间的,为了防止没有意义的资源消耗,我们可以采用“ViewHolder复用模式”;
下面是示例代码: 这样可以保证,就算有多次滑动,多次getView,每一个item的inflate都只执行一次,避免了没必要的资源消耗,提高了效率。
(重点注意红色代码)
1 @Override
2 public View getView(int position, View convertView, ViewGroup parent) {
3 //观察一下这个方法什么时候会被调用
4 Log.d("getViewTag", "*****************************");
5 //我发现,以一个item为准,如果它从不可见到可见,这个getView都会被调用一次
6
7 // 取得bean对象
8 final ViewHolder viewHolder;
9 if (convertView == null) {//参数中的convertView实际上就是 itemView的缓存
10 convertView = layoutInflater.inflate(R.layout.tweet_item, null);//只有当缓存的convertView是空,才进行itemView实例化
11 viewHolder = new ViewHolder();//实例化之后,用viewHolder统筹管理item中的所有view
12 viewHolder.tv_username = convertView.findViewById(R.id.tv_username);
13 viewHolder.tv_content = convertView.findViewById(R.id.tv_content);
14 viewHolder.img_user = convertView.findViewById(R.id.img_user);
15 viewHolder.ll_img_content = convertView.findViewById(R.id.ll_img_content);
16 viewHolder.ll_comments = convertView.findViewById(R.id.ll_comments);
17
18 convertView.setTag(viewHolder);//然后将holder设置到convertView的tag里面去,Tag可以是任何Object
19 } else {
20 viewHolder = (ViewHolder) convertView.getTag();//如果缓存中convertView不是空,那就直接取tag中的holder
21 }
22
23 Tweet tweet = tweetList.get(position);
24
25 //对adapter自定义布局内的元素进行赋值处理
26 if (tweet.sender != null)
27 viewHolder.tv_username.setText(tweet.sender.username);
28 viewHolder.tv_content.setText(tweet.content);
29
30 // 加载用户资料
31 // Picasso.with(context).load(URL_TEST).into(viewHolder.img_user);//测试版的
32 Picasso.with(context).load(tweet.sender.avatar).into(viewHolder.img_user);//正式版的
33
34 //对tweet.images进行遍历,每次加载一张图
35 List<String> imgList = tweet.images;
36 ImageView imageView;
37 viewHolder.ll_img_content.removeAllViews();
38 if (null != imgList) {
39 Log.d("imgListTag", "here:position=" + position + ";viewHolder.ll_img_content.getChildCount():" + viewHolder.ll_img_content.getChildCount());
40 // 每一次都检查ll_img_content中是否有元素,当且仅当内部元素为空时执行添加;否则,不予理会
41 for (int i = 0; i < imgList.size(); i++) {
42 imageView = new ImageView(context);
43
44 //设置图片view的最大尺寸
45 imageView.setAdjustViewBounds(true);
46 imageView.setMaxHeight(100);
47 imageView.setMaxWidth(100);
48
49 Picasso.with(context).load(imgList.get(i)).into(imageView);//正式版的
50 viewHolder.ll_img_content.addView(imageView);
51 }
52 }
53
54 //加载评论区
55 List<Comments> commentsList = tweet.comments;// 这是获取到的评论列表
56 //循环读取列表,每一次在ll_comments中加载一条。
57 TextView textView;
58
59 //由于getView可能被重复绘制,因为 假如说编号为0 的item 从隐藏到显示,必然会调用getView,那么这部分代码就会被多次调用,所以在添加以前,必须移除其中的所有子view
60 viewHolder.ll_comments.removeAllViews();
61 if (commentsList != null && commentsList.size() > 0) {//除非一定有
62 for (int i = 0; i < commentsList.size(); i++) {
63 textView = new TextView(context);
64 String senderName = commentsList.get(i).sender.nick;
65 String content = commentsList.get(i).content;
66
67 Spanned textContent = html.fromHtml("<font color=\"#7FB446\">" + senderName + "</font>" + " : " + content);
68 textView.setText(textContent);
69 viewHolder.ll_comments.addView(textView);
70 }
71 }
72 return convertView;
73 }
74
75
76 static class ViewHolder {//用内部类ViewHolder统筹item中的所有元素
77 TextView tv_username;
78 TextView tv_content;
79 ImageView img_user;
80 LinearLayout ll_img_content;
81 LinearLayout ll_comments;
82 }
2)设置item之间的分隔线,可以设置颜色,宽度,或者设置透明:
android:dividerHeight="10dp"//宽度
android:divider="@color/colorAccent" //颜色
android:divider="@null" //透明
3) 取消滚动条的显示:
android:scrollbars="none"
4)设置ListView显示在第几项(从0开始):
lv_tweet.setSelection(4);//第5项要显示出来
5) 动态修改listView:
adapter.notifyDataSetChanged();//这个操作是基于listView的adapter的
6)遍历ListView的item
for(int i=0;i<listView.getChildCount();i++){
View v = listView.getChildAt(i);
}
7) 处理空ListView,当数据为空时,用一个View暂时顶替ListView的显示,防止数据加载过程中的界面假死.
listView.setEmpty(v);
8) 增加滑动监听事件(这个onTouchEvent是 ListView 的父类 AbsListView 里的):
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN://按下
downY = ev.getY();// 按下的时候的Y轴坐标
break;
case MotionEvent.ACTION_MOVE://按住的过程中移动
break;
case MotionEvent.ACTION_UP://松开
doSomething();
break;
default:
break;
}
return super.onTouchEvent(ev);
}
9) 增加滚动监听事件:
/********************************重写父类的方法******************************/
/**
* 当滚动状态改变时的回调
*
* @param view
* @param scrollState
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE://当list处于静止时
Log.d(TAG, "SCROLL_STATE_IDLE");
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL://当手指按住list,并且在滑动时
Log.d(TAG, "SCROLL_STATE_TOUCH_SCROLL");
break;
case OnScrollListener.SCROLL_STATE_FLING://当手指松开之后,list还在滑动时
Log.d(TAG, "SCROLL_STATE_FLING");
break;
}
}
/**
* 这个方法将会在滚动动作完成之后被的回调
* <p>
* 经过测试,这个方法,在list初始化之后就会首先被调用一次
*
* @param view
* @param firstVisibleItem 显示在列表第一个的item的index
* @param visibleItemCount 当前列表中可见的item的总个数(显示出一半也算在内)
* @param totalItemCount 列表中所有item的总个数
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (totalItemCount == 0)//由于list初始化的时候,这个onScroll也会被调用一次,而打印出来的3个参数都是0,所以这种情况必须排除.只需要判断total是否为0即可,如果为0,下面的都不需要执行了
return;
// 判断当前list的状态(滑动到了顶部,或者底部,或者在中间)
if (firstVisibleItem + visibleItemCount == totalItemCount) {// 通过观察规律,前两者之和等于后者,就是到了底部//到了底部,如果还继续往上滑
} else if (firstVisibleItem == 0) {
Log.d(TAG + "-onScroll", "to the top");//那就 加载数据,并且刷新list
} else {
Log.d(TAG + "-onScroll", "at mid");
}
}
然后
setOnScrollListener(this);//设置滑动回调
10)
以上是关于android-ListView使用技巧以及优化的主要内容,如果未能解决你的问题,请参考以下文章
jvm之年轻代(新生代)老年代永久代以及GC原理详解GC优化
我的Android进阶之旅关于Android使用bindService()绑定服务,onServiceConnected()方法是异步回调的问题以及借鉴NotificationManager来优化(代
我的Android进阶之旅关于Android使用bindService()绑定服务,onServiceConnected()方法是异步回调的问题以及借鉴NotificationManager来优化(代
13.Android-ListView使用BaseAdapter/ArrayAdapter/SimpleAdapter适配器使用