前言

相信Android开发者对ListView不会陌生,使用ListView需要设置相应的Adapter才能展示数据。Adapter到底是什么东西?让我们来一探究竟。

Adapter

p1.png

通过图1我们可以看出Adapter是View和数据之间的桥梁,并为每一个数据项生成相应的View。Adapter是个接口,定义了子类需要实现的方法,最常见的

方法有:

  • getCount(),总共有多少数据项

  • getItem(),获取对应position 中的item

  • getView(),返回需要展示在屏幕中的View

    一般在自定义Adapter中,只需要实现上述三个方法即可。

Adapter优化前

class ImageAdapter extends BaseAdapter {
private Context mContext ;
private String[] mList ; public ImageAdapter(Context context, String[] list) {
mList = list ;
mContext = context ;
} @Override
public int getCount() {
return mList.length;
} @Override
public Object getItem(int position) {
return mList[position];
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = LayoutInflater.from(mContext).inflate(R.layout.gridview_item, parent, false) ;
TextView tv = view.findViewById(R.id.text) ;
tv.setText(mList[position]);
return view;
}
}

上述大概就是一个最简单的Adapter的实现了吧。通过在getView函数中inflate一个新布局,给相应position设置data,然后将view返回给父控件。咋一看没啥问题,运行也不会有错。但是会有严重的性能问题。想象一下,如果有mList中有100万条数据,我们有必要每次都重新inflate一个新layout,生成一个新view吗?显然是没有必要的,我们的手机屏幕就那么小,可见的View其实也就那几个。那些看不见的View其实是没必要保存的。

View复用原理

p2.jpg

图片来源于此博客。上图清晰展现了View的复用原理。手机屏幕一共能够展示七个item,继续往上滑动,item 1从我们视线消失,此时ListView 会调用Adaper中的getView函数来生成第八个item。此时getView函数中参数convertView就是item 1,我们只需把convertView(item 1)上的数据项全部设置成item 8的数据项即可,这样就不用再重新inflate一个新View出来了。不管是有mList有多大,内存中保存的View的个数永远只是可见的几个。这对程序性能有很大提升。

Adapter优化后

class ImageAdapter extends BaseAdapter {
private Context mContext ;
private String[] mList ; public ImageAdapter(Context context, String[] list) {
mList = list ;
mContext = context ;
} @Override
public int getCount() {
return mList.length;
} @Override
public Object getItem(int position) {
return mList[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();
convertView = LayoutInflater.from(mContext).inflate(R.layout.gridview_item, parent, false) ;
viewHolder.textview = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
// 绑定对应position数据
bindViewData(position, viewHolder) ; return convertView;
} private void bindViewData(int position, ViewHolder viewHolder) {
viewHolder.textview.setText(mList[position]);
} private static class ViewHolder {
public TextView textview ;
}
}

以图2为例,每个item都是由View(视图)和Data(数据)组成的。前7个item的convertView都是为空的,因此inflate出了7个新View,当第8个item变成可见时此时convertView 为item 1,再通过bindViewData函数把第八个mList中的数据设置给item 1对应的View,然后直接返回convertView。此时我们看到的item 8其实是由这两个东西组成的:

  • View ------ item 1 的View

  • Data ------ mList 中的第八项

因为item 8和View 和item 1的View结构是一样的(使用同样layout),所以不会造成错误。

View复用导致的问题

问题1:item的状态错乱

p3.png

如图3所示,6跟帖这个View不是所有item都有的,还是拿图2来举例。假设item 1是图3的第一项,item8是图3的第二项。item 1中跟帖这个View是显示的,因为item 8使用的是item 1的View,所以item 8中跟帖的View的状态也是显示的。但是在item 8中跟帖这个View是不应该显示的。这就是复用View导致item状态错乱的问题。解决方法之一就是在bindViewData函数中给layout中的View先全部还原成默认状态即可。

问题2:多线程导致图片加载位置错乱

图片加载往往涉及到网络操作,因此ListView中加载图片时一般都会开启新线程去加载图片。如果不使用View复用方法,直接使用优化前的Adapter,是不会有任何问题的。但如果使用的View复用,就不一定了。

 @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null ;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(R.layout.gridview_item, parent, false) ;
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.square_image_view);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
String url = mList[position] ;
ImageView imageView = viewHolder.imageView ;
ImageLoader.loadBitmap(imageView, url); return convertView;
}

还是拿图2为例。如上述代码所示,我们使用了复用View方法,并且异步加载图片。由于网络比较慢,item 1到item 7的图片还没加载出来,我们滑动到了item 8(复用了item 1的ImageView),突然网络变好了,item 8 的图片加载完成,我们给Item 8设置了图片。过了一会,item 1的图片也下载完成了,我们又给item 1设置了图片,由于item 1和item 8共用同一个ImageView,因此item 8的图片立马变成了item 1的图片。此时item 8上显示的竟然是item 1对应的图片!解决办法之一就是给ImageView setTag。代码如下:

 ImageView imageView = viewHolder.imageView ;
String tag = (String) imageView.getTag();
String url = mList[position];
if ( !url.equals(tag) ) {
//default drawable
imageView.setImageDrawable(null);
}
// set tag to image view
imageView.setTag(url);
// load bitmap
ImageLoader.loadBitmap(imageView, url);

延伸阅读

关于 ScrapView 和 ActiveView

Performance Tips for Android’s ListView

Handling ListView Recycle on Android

Android-Adapter-View复用机制的更多相关文章

  1. Android AdapterView View的复用机制 分析

    对于ListView.GridView相信大家都不陌生,重写个BaseView,实现对于的几个方法,然后就完毕了我们的界面展示.而且在大部分情况下,我们载入特别多的Item也不会发生OOM,大家也都明 ...

  2. Android学习笔记之ListView复用机制

    PS:满打满算,差不多三个月没写博客了...前一阵忙的不可开交...总算是可以抽出时间研究研究其他事情了... 学习内容: 1.ListView的复用机制 2.ViewHolder的概念 1.List ...

  3. 【朝花夕拾】Android自定义View篇之(七)Android事件分发机制(下)滑动冲突解决方案总结

    前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/11072989.html],谢谢! 前面两篇文章,花了很大篇幅讲解了Android的事件分发机制 ...

  4. Android View事件机制 21问21答

    原文: http://www.cnblogs.com/punkisnotdead/p/5179115.html#3358859 1.View的坐标参数 主要有哪些?分别有什么注意的要点? 答:Left ...

  5. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  6. 浅析Android中的消息机制-解决:Only the original thread that created a view hierarchy can touch its views.

    在分析Android消息机制之前,我们先来看一段代码: public class MainActivity extends Activity implements View.OnClickListen ...

  7. Android View事件机制一些事

    本文主要讲述: 自己对View事件机制的一些理解 在项目中遇到的一些坑,解决方案 收集了一些View的事件机制问题 事件的分发原理图 对于一个root viewgroup来说,如果接受了一个点击事件, ...

  8. Android 中View的绘制机制源代码分析 一

    尊重原创: http://blog.csdn.net/yuanzeyao/article/details/46765113 差点儿相同半年没有写博客了,一是由于工作比較忙,二是认为没有什么内容值得写, ...

  9. Android 中View的绘制机制源代码分析 二

    尊重原创:http://blog.csdn.net/yuanzeyao/article/details/46842891 本篇文章接着上篇文章的内容来继续讨论View的绘制机制,上篇文章中我们主要解说 ...

  10. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

随机推荐

  1. 个人支付宝监控并自动获取交易记录对接系统API

    我们都知道,支付宝支付API接口只有企业才能使用,但有一部分业务,可能我们不方便使用企业收款,但又想做到自动化,那怎么办呢 于是一个支付宝交易记录自动监控软件诞生了. 支付宝都有一个收款二维码,收款提 ...

  2. 第六章 系统配置:DHCP和自动配置

    系统配置:DHCP和自动配置 写在开头:今天和导师见了个面,抛给我一堆材料以及论文,感觉自己学业更加繁重.有些知识现阶段我可能没办法掌握,但是至少在我需要进一步理解它的时候,要知道在哪个地方能够找到. ...

  3. HDU 3033 组合背包变形 I love sneakers!

    I love sneakers! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tot ...

  4. (总结)Nginx与Apache、Tomcat、Resin动静分离核心配置

    PS:近来有几个刚使用nginx的新童鞋老问我,nginx+fastcgi不够稳定,偶尔出现502错误,怎么解决?本人使用nginx也有3年多了,也认为php-fpm模块不够稳定,在访问量不大的时候没 ...

  5. 网站前后台分离 图片 flash 视频 等文件的共享问题

    在网上找了,没有说到点子上的,不详细 问了有经验的同事,要建立 文件服务器,就是一个IIS 下的新网站,网站是共享图片 文件使用的专用网站 后台上传的图片保存在 文件服务器即 文件共享专用的网站目录地 ...

  6. JAVA 基础开发环境 vscode 搭建 Windows下VSCode编译运行简单java

    JAVA 基础开发环境 vscode 搭建 来源 https://www.cnblogs.com/freewsf/p/7744728.html 对于使用 Visual Studio Code 的 Ja ...

  7. BZOJ4032 [HEOI2015]最短不公共子串 【后缀自动机 + 序列自动机 + dp】

    题目链接 BZOJ4032 题解 首先膜\(hb\) 空手切神题 一问\(hash\),二问枚举 三问\(trie\)树,四问\(dp\) 南二巨佬神\(hb\) 空手吊打自动机 \(orz orz ...

  8. Codeforces Round #324 (Div. 2) A

    A. Olesya and Rodion time limit per test 1 second memory limit per test 256 megabytes input standard ...

  9. java 符号引用与直接引用

    简单来说: 符号引用就是字符串,这个字符串包含足够的信息,以供实际使用时可以找到相应的位置.你比如说某个方法的符号引用,如:“java/io/PrintStream.println:(Ljava/la ...

  10. HDU2824 The Euler function

    Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u Description The Eule ...