浅谈ListView之convertView和viewHolder

来源:互联网 发布:日语翻译发音软件 编辑:程序博客网 时间:2024/05/29 04:57
最近开发相册时,碰到缩略图显示的问题,功能实现后发现滚动时比较的卡顿,
先就ListView优化问题提出几点看法:
1、复用已经生成的convertView;
2、添加viewHolder类;
3、缓存数据(图片缓存);
4、分页加载。

1、convertView的复用

ListView的原理:ListView中的每一个Item显示都需要Adapter调用一次getView的方法,这个方法会传入一个convertView的参数,返回的View就是这个Item显示的View。如果当Item的数量足够大,再为每一个Item都创建一个View对象,必将占用很多内存,创建View对象(mInflater.inflate(R.layout.lv_item, null);从xml中生成View,这是属于IO操作)也是耗时操作,所以必将影响性能。Android提供了一个叫做Recycler(反复循环器)的构件,就是当ListView的Item从上方滚出屏幕视角之外,对应Item的View会被缓存到Recycler中,相应的会从下方生成一个Item,而此时调用的getView中的convertView参数就是滚出屏幕的Item的View,所以说如果能重用这个convertView,就会大大改善性能。

<span style="background-color:rgb(204,204,204)">BaseAdapter适配器里有个getView()需要重写public View getView(int position,View converView,ViewGroup parent)  {  //省略。。。。。。}</span>
这个convertView其实就是最关键的部分  原理上讲 当ListView滑动的过程中 会有item被滑出屏幕 而不再被使用 这时候Android会回收这个条目的view 这个view也就是这里的convertView。<pre name="code" id="best-content-1572584631" class="best-text mb-10" style="margin-top:0px; margin-bottom:10px; padding:0px; font-family:arial,'courier new',courier,宋体,monospace; white-space:pre-wrap; word-wrap:break-word; background-color:rgb(241,254,221)">当item1被移除屏幕的时候 我们会重新new一个View给新显示的item_new 而如果使用了这个convertView 我们其实可以复用它 这样就省去了new View的大量开销。

public View getView(int position, View convertView, ViewGroup parent) {View view = null;if (convertView != null) {view = convertView;//复用了回收的view 只需要直接作内容填充的修改就好了} else {view = new Xxx(...);//没有供复用的view 按一般的做法新建view}return view;}

小demo:

static class ViewHolder{public TextView mSelectAlbumName;public MyImageView mAlbumView;private TextView mImageCounts;}
这里,static关键字的作用就是保证该类在内存中只有一个对象,也就是只有一块内存空间,所以不会ViewHolder holder = new ViewHolder()一次就会开辟一块新地址的,不管new几次,都是指向第一次new时开辟的地址,所有引用共用这一地址,即节省内存、又提高效率。


@Overridepublic View getView(int position, View convertView, ViewGroup parent){    final ViewHolder viewHolder;    String path = albumList.get(position).getPhotoList().get(0).getPath();        if (convertView == null)         {           viewHolder = new ViewHolder();        convertView = mInflater.inflate(R.layout.select_album_grid_item, null);               viewHolder.mSelectAlbumName = (TextView)convertView.findViewById(R.id.txtSelectName);        viewHolder.mAlbumView = (MyImageView)convertView.findViewById(R.id.imgAlbum);            viewHolder.mImageCounts = (TextView)convertView.findViewById(R.id.group_count);               viewHolder.mAlbumView.setOnMeasureListener(new OnMeasureListener() {    @Override  public void onMeasureSize(int width, int height) {  mPoint.set(width, height);  }  });        convertView.setTag(viewHolder);                    }          else {         viewHolder = (ViewHolder) convertView.getTag();         }         viewHolder.mSelectAlbumName.setText(albumList.get(position).getName());        viewHolder.mImageCounts.setText(albumList.get(position).getCount()+" "+context.getString(R.string.pictures));        viewHolder.mAlbumView.setTag(path);               Bitmap bitmap = ImageLoader.getInstance().loadNativeImage(path, mPoint, new NativeImageCallBack()        {@Overridepublic void onImageLoader(Bitmap bitmap, String path) {ImageView mImageView = (ImageView)mGridView.findViewWithTag(path); if(bitmap != null && mImageView != null) {mImageView.setImageBitmap(bitmap);}   }        });                if(bitmap != null) {         viewHolder.mAlbumView.setImageBitmap(bitmap);        }        return convertView; }

2、viewHolder类的使用

        在getView方法中的操作是这样的:先从xml中创建view对象(inflate操作,我们采用了重用convertView方法优化),然后在这个view去findViewById,找到每一个子View,如:一个TextView等。这里的findViewById操作是一个树查找过程,也是一个耗时的操作,所以这里也需要优化,就是使用viewHolder,把每一个子View都放在Holder中,当第一次创建convertView对象时,把这些子view找出来。然后用convertView的setTag将viewHolder设置到Tag中,以便系统第二次绘制ListView时从Tag中取出。当第二次重用convertView时,只需从convertView中getTag取出来就可以了。

setTag()(设置标签)是将view封装起来,以便下次复用,getTag()(获取标签)是得到封装的view。

1 0
原创粉丝点击