1黑马币
在面试的过程中,listview的优化考察是面试官屡试不爽的一个点
listview可以说是Android开发中最常见的UI控件了,listview能够以列表的方式显示大量同类的数据,这样问题就产生了,既然是大量数据,就会使用到很多布局,给布局绑定数据,listview将占用大量资源还可能会产生卡顿现象。
listview现在最常用也拥有很好的性能的优化方式是在Adapter中使用静态的ViewHolder,具体代码如下:
Activity
- private TestAdapter mAdapter;
- private String[] mArrData;
- private TextView mTV;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mTV = (TextView) findViewById(R.id.tvShow);
- mArrData = new String[1000];
- for (int i = 0; i $amp; mArrData[i] = "Google IO Adapter" + i;
- }
- mAdapter = new TestAdapter(this, mArrData);
- ((ListView) findViewById(android.R.id.list)).setAdapter(mAdapter);
- }
- Adapter
- private int count = 0;
- private long sum = 0L;
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // 开始计时
- long startTime = System.nanoTime();
- ViewHolder holder;
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.list_item_icon_text,
- null);
- holder = new ViewHolder();
- holder.icon1 = (ImageView) convertView.findViewById(R.id.icon1);
- holder.text1 = (TextView) convertView.findViewById(R.id.text1);
- holder.icon2 = (ImageView) convertView.findViewById(R.id.icon2);
- holder.text2 = (TextView) convertView.findViewById(R.id.text2);
- convertView.setTag(holder);
- }
- else{
- holder = (ViewHolder)convertView.getTag();
- }
- holder.icon1.setImageResource(R.drawable.icon);
- holder.text1.setText(mData[position]);
- holder.icon2 .setImageResource(R.drawable.icon);
- holder.text2.setText(mData[position]);
- // 停止计时
- long endTime = System.nanoTime();
- // 计算耗时
- long val = (endTime - startTime) / 1000L;
- Log.e("Test", "Position:" + position + ":" + val);
- if (count $amp; if (val $amp; sum += val;
- count++;
- }
- } else
- mTV.setText(String.valueOf(sum / 100L));// 显示统计结果
- return convertView;
- }
- }
- static class ViewHolder {
- TextView text1;
- ImageView icon1;
- TextView text2;
- ImageView icon2;
- }
复制代码 在Adapter的代码中,在getView方法里首先判断convertView是否为空,若为空则加载相应布局,若不为空则直接使用该布局,这能够很有效的使用Android为listview提供的缓存机制:只加载一屏的布局,之后滑动出来的item使用的是之前已经加载的布局的缓存;
而使用静态的ViewHoulder的目的则是节省了findViewById的时间。如果不使用ViewHolder,每次getView的时候都需要得到一次子布局,而这也是很耗时并且耗资源的;如果使用了ViewHolder作为子布局的缓存,使用View的setTag方法将缓存与每个item绑定,则也可以省去了findViewById的事件;而将ViewHolder设置为static的目的是指在初始化Adapter时初始化一次这个内部类,否则将会在每次创建Adapter时都要初始化一次,而这是没有必要的。
上述方法能够解决大部分listview消耗资源以及卡顿的问题,但对于不同的需求的listview来说还会存在其他让listview卡顿的原因,比如listview的item每次加载时都需要获得图片并设置到imageview中,item加载时需要进行大量的计算,item里的TextView需要设置指定字体;这些耗时的操作都会让listview滑动起来很卡,带来不好的体验;
这几天我一直在研究Android4.0+系统联系人的源码,因为我发现,系统联系人采用的也是listview布局,每一个item都有图片文字,而且使用了fastscroller模式,对联系人以首字母进行了分来,同时Adapter还实现了SectionIndexer接口,能够实现这种效果:
但是却一点都不卡,而我自己做的一个类似的界面,却十分卡顿,很影响用户体验;于是我找来了Android体统联系人的源码,自己建立起来了一个可以运行的程序,然后去研究它是如何实现这么流畅的listview的;
经过研究我发现系统联系人的listview使用的是自定义的listview:PinnedHeaderListView 它的定义是这样的:
- /**
- * A ListView that maintains a header pinned at the top of the list. The
- * pinned header can be pushed up and dissolved as needed.
- */
复制代码
它给listview加了一个header即显示在联系人界面最上方贴着屏幕顶部的那部分,同时它继承自AutoScrollListView 而这个AutoScrollListView继承自listview,对listview进行了一些优化,让listview在互动到指定的item时更流畅;
看到这里,于是我使用了这个AutoScrollListView ,但效果不是很明显,listview还是很卡;在系统联系人使用的listview中没有找到解决方法,我开始研究它的Adapter;研究Adapter发现,系统的Adapter进行了很多层的封装,完全淡化了geiView方法;使用了很多AsyncTask来加载不同的数据,然后使用了CursorLoader来将加载好的数据添加到Adapter里,同时还将图片的加载与数据的加载进行了分离,代码逻辑十分复杂;但通过这个我得出了一个结论:不要将任何的耗时操作放在listview的getView方法里。
在系统联系人的这些Adapter里我发现getView方法中没有任何的耗时操作,在设置图片时图片已经得到,对列表按照字母进行的分类也已经分类好了,存放在一个内部类里;在得出这个结论之前我尝试过很多系统联系人中的代码,但都没有得到明显的效果,经过大量的测试我得出了这个结论,并且在测试中得到了验证。
在我的项目中,listview的每一个item都有一个图片,和很多TextView,而且所有的TextView都要设置非系统的字体;Adapter使用的是ViewHolder优化,在getView中的代码已经很少了,但是还是卡;我的listview中的数据是一个对象的List,在对象里只存放了item需要展示的图片的资源ID,或者是图片的路径,需要通过一些操作才能获得图片,而这些操作其实是很耗时的;于是我将原来的对象进行了更改,将图片对象直接存放在item对应的对象中,然后再Adapter初始化的时候将这些对象初始化,虽然listview展示所需的时间稍微长了一点,但是结果是listview滑动流畅了很多;接着我又将从assets中获得字体TypeFace的操作放在了Adapter初始化的方法中,并且将字体通过静态的变量都存起来,然后再getView中只需为TextView设置一下taptface即可,不需要在从asset中获取字体所花费的时间;通过上面两步操作之后,我的listview一点都不卡了,十分流畅。
综上,listview的优化其实就是去找getView中的耗时操作,然后提取出来,要么使用异步的方式为item的布局设置数据,要是实在需要同步,就只能在Adapter初始化时将数据准备好,然后再getView中只需绑定一下就行。
|
|