ListView的优化总结
Posted sshhsun-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ListView的优化总结相关的知识,希望对你有一定的参考价值。
ListView作为开发中最常见的控件,大部分app都会引用ListView来展示数据,用于用户交互,正因为ListView过于基础,这么常用,所以作为app优化中保持高响应度的ListView 界面就显得尤为重要了
本文结构 :
1.ListView的运行工作原理
2.convertView的复用优化
3.ViewHolder类的使用
4.基于当前滑动状态的优化
ListView的运行工作原理
先来一张最经典的ListView工作图。
在解释ListView的工作原理之前,我们需要了解一些ListView中的重要机制:
Recycler机制
Recycler机制使用一结合来储存我们之前已经使用过的所谓”废弃”的子项view。结合上图中的示例,在item1被滑动移开当前的屏幕时,会将该item的view收集在Recycler相应的集合中,当作缓存并从ListView中移除相关的子view。
接着结合这个图我们讨论一下ListView的工作过程:
ListView刚开始实现时会要求每一个子项进行绘制,刚开始占满屏幕的每一子项的view都是从定义的item的xml文件中inflate出来的,多少个item子项就会创建多少个view。
随着手指的滑动,当item1完全移出屏幕时,基于Recycler机制,将item1的view给depatch掉,即从listView中取消掉item1的view,同时将item1的view存放于Recycler的集合中,如果第8个item和放入“Recycler”的item的view一致,那么就会使用”Recycler”里面的Item来显示,从而不用再重复inflate一次,这样大大节省了创建View的工作,在需要显示大量数据时显得尤为重要。
想要了解更多的listView原理知识,可以访问基于listView源码的工作原理分析。
convertView的复用优化
基于第一部分的基础工作原理分析,我们认识到Google在设计ListView时精妙之处,接下来我们看一下基于Recycler机制中关于”废弃的view”的优化,在继承了Adapter后,我们会重写其中的getView()方法。其中的第二个参数convertView就是Recycler中使用过的view.
优化:在getview()中给每一个需要显示的item生成一个view,如果convertView是空的,我们就需要inflate一个新的view并返回,如果convertView不为空就需要新的view对象,直接使用convertView中的view就可以。
代码如下:
@Override
public View getView(int position, View convertView, ViewGroup parent)
Fruit fruit = getItem(position);
View view;
if (convertView == null)
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
else
view = convertView;
ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId());
fruitName.setText(fruit.getName());
return view;
ViewHolder类的使用
在上述的代码中我们会发现,getView中的View的每一个组件都需要通过findViewById()得到,在不太复杂的情况下这样当然也可以,可是当需要在view中需要复杂的逻辑判断。需要大量的findViewById()操作,不仅编码繁琐麻烦容易丢失一些组件,同时大量的findViewById操作也会影响效率。
如果我们将item中的所有组件都放在一个类里面,那这样操作时不但可以省下一个一个空间绑定的时间,并且会很全面的绑定。
基于此,我们可以定义个ViewHolder来收集所有组件。
对上面的代码进行改进后:
@Override
public View getView(int position, View convertView, ViewGroup parent)
Fruit fruit = getItem(position);
View view;
ViewHolder holder;
if (convertView == null)
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
holder = new ViewHolder(view);
view.setTag(holder);
else
view = convertView;
holder=convertView.getTag();
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
return view;
//定义的新的class 作为手机UI 组件的工具,并在构造函数中进行组件的绑定。
private class ViewHolder
private ImageView fruitImage;
private TextView fruitName;
public ViewHolder(View view )
fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
fruitName = (TextView) view.findViewById(R.id.fruit_name);
基于当前滑动状态的优化
getView中操作UI组件的代码工作占用了ListView中的大量工作。
而我们可以判断当前用户对ListView的操作,判断是否要进行ListView的实际更新。
基于什么判断呢?
我们可以给当前的ListView设滑动状态监听器,当ListView处于快速滑动状态时,一般情况下用户不太关心的页面的具体信息。所以我们可以在这个状态下停止UI组件的更新。在组件停止或者点击触动的滑动状态下更新组件的信息。
效果如下:
实现步骤:
1.在Adapter的实现类的getView方法中设置一个状态指示是否在滑动
public View getView(int position, View convertView, ViewGroup parent)
// convertView
Log.i(TAG, position + "-.-.-.-.-.-.-" + convertView);
PicBean pic = lists.get(position);
Viewholder holder;
if (convertView == null)
convertView = inflater.inflate(R.layout.item_listview, null);
holder = new Viewholder(convertView);
convertView.setTag(holder);
else
holder = (Viewholder) convertView.getTag();
if (!isScroll)
holder.name.setText(pic.getName());
Long ldate = Long.valueOf(pic.getDesc());
String date = sDateFormat.format(ldate);
holder.desc.setText(date);
else
holder.name.setText("加载中");
holder.name.setTag(pic.getName());
Long ldate = Long.valueOf(pic.getDesc());
String date = sDateFormat.format(ldate);
holder.desc.setTag(date);
return convertView;
2.在ListView的使用活动中设置listView的滑动监听器OnScrollListener并根据状态设置Adapter实现类中的状态标识,当属于停止状态时,得到当前屏幕中的子项的数目并一一配置UI。
lv_show.setOnScrollListener(new OnScrollListener()
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
switch (scrollState)
case AbsListView.OnScrollListener.SCROLL_STATE_FLING://手指离开屏幕,但屏幕依然滑动
adapter.setScrollState(true);
break;
case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:// 停止滚动
adapter.setScrollState(false);
int count = view.getChildCount();//得到当前屏幕的子项。
//一一为子项设置UI组件
for (int i = 0; i < count; i++)
TextView name = (TextView) view.getChildAt(i)
.findViewById(R.id.name);
TextView date = (TextView) view.getChildAt(i)
.findViewById(R.id.desc);
if (name.getTag() != null)
name.setText((String) name.getTag());
if (date.getTag() != null)
date.setText((String) date.getTag());
break;
case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL://手指触碰滑动
adapter.setScrollState(true);
break;
本篇是关于ListView的初步分析与优化。
关于ListView的原理以及源码,推荐看看ListView工作原理完全解析,带你从源码的角度彻底理解
以上是关于ListView的优化总结的主要内容,如果未能解决你的问题,请参考以下文章
Android学习总结(十三) ———— ListView 简单用法