Android中ListView异步加载图片错位、重复、闪烁问题分析及解决方案

Android ListView异步加载图片错位、重复、闪烁分析以及解决方案,具体问题分析以及解决方案请看下文。

我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图片错位、重复、闪烁等问题,其实这些问题总结起来就是一个问题,我们需要对这些问题进行ListView的优化。

比如ListView上有100个Item,一屏只显示10个Item,我们知道getView()中convertView是用来复用View对象的,因为一个Item的对应一个View对象,而ImageView控件就是View对象通过findViewById()获得的,而我们在复用View对象时,同时这个ImageView对象也被复用了。比如第11个Item的View复用了第1个Item View对象,那么ImageView就同时被复用了,所以当图片没下载出来,这个ImageView(第11个Item)显示的数据就是复用(第1个Item)的数据。

1:Item图片显示重复

这个显示重复是指当前行Item显示了之前某行Item的图片。

比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中ListView已经滑动到了第14行,且滑动过程中该图片加载结束。第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的View对象可能被第14行复用,这样我们看到的就是第14行显示了本该属于第2行的图片,造成显示重复。

2. Item图片显示错乱

这个显示错乱是指某行Item显示了不属于该行Item的图片。

跟上面的原因一样。

3. Item图片显示闪烁

上面介绍的另外一种情况,如果第14行图片又很快加载结束,所以我们看到第14行先显示了复用的第2行的图片,立马又显示了自己的图片进行覆盖造成闪烁错乱。

解决方案:

通过上面的分析我们知道了出现错乱的原因是异步加载及对象被复用造成的,如果每次getView能给对象一个标识,在异步加载完成时比较标识与当前行Item的标识是否一致,一致则显示,否则不做处理即可。

原理:首先给ImageView设置一个Tag,这个Tag中设置的是图片的url,然后在加载的时候取得这个url和要加载那position中的url对比,如果不相同就加载,相同就是复用以前的就不加载了。

Android在ListView显示图片(重复错乱闪烁问题)

1、原因分析

ListView item缓存机制:

为了使得性能更优,ListView会缓存行item(某行对应的View)。

ListView通过adapter的getView函数获得每行的item。

滑动过程中

a. 如果某行item已经滑出屏幕,若该item不在缓存内,则put进缓存,否则更新缓存;

b. 获取滑入屏幕的行item之前会先判断缓存中是否有可用的item,如果有,做为convertView参数传递给adapter的getView。

这样,如下的getView写法就可以充分利用缓存大大提升ListView的性能。即便上万个行item,最多inflate的次数为n,

n为一屏最多显示ListView 行item的个数。

@Override
public View getView ( int position , View convertView , ViewGroup parent ) {
  ViewHolder holder ;
  if ( convertView == null ) {
   convertView = inflater . inflate ( R . layout . list_item , null ) ;
   holder = new ViewHolder ( ) ;
   ……
   convertView . setTag ( holder ) ;
  } else {
   holder = ( ViewHolder ) convertView . getTag ( ) ;
  }
}

这样提升了性能,但同时也会造成另外一些问题:

a. 行item图片显示重复

这个显示重复是指当前行item显示了之前某行item的图片。

比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中listView已经滑动到了第14行,且滑动过程中该图片加载结束,

第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的view可能被第14行复用,这样我们看到的就是第14行显示了本该属于第2行的图片,

造成显示重复。

b. 行item图片显示错乱

这个显示错乱是指某行item显示了不属于该行item的图片。

比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中listView已经滑动到了第14行,第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的view可能被第14行复用,第14行显示了第2行的View,这时之前的图片加载结束,就会显示在第14行,造成错乱。

c. 行item图片显示闪烁

上面b的情况,第14行图片又很快加载结束,所以我们看到第14行先显示了第2行的图片,立马又显示了自己的图片进行覆盖造成闪烁错乱。

2、解决方法

通过上面的分析我们知道了出现错乱的原因是异步加载及对象被复用造成的,如果每次getView能给对象一个标识,在异步加载完成时比较标识与当前行item的标识是否一致,一致则显示,否则不做处理即可。

andbase中的实现代码:

/**
  * 显示这个图片,解决了列表问题.
  * 列表问题:滑动过程中,getView的imageView会重复利用,导致图片会串位
  * @param imageView 显得的View
  * @param url the url
  * @return
  */
  public void display( final ImageView imageView,String url) {
   if (AbStrUtil.isEmpty(url)){
    if (noImage != null ){
     if (loadingView != null ){
      loadingView.setVisibility(View.INVISIBLE);
      imageView.setVisibility(View.VISIBLE);
     }
     imageView.setImageDrawable(noImage);
    }
    return ;
   }
   //设置下载项
   final AbImageDownloadItem item = new AbImageDownloadItem();
   //设置显示的大小
   item.width = width;
   item.height = height;
   //设置为缩放
   item.type = type;
   item.imageUrl = url;
   final String cacheKey = AbImageCache
   .getCacheKey(item.imageUrl, item.width, item.height, item.type);
   item.bitmap = AbImageCache.getBitmapFromCache(cacheKey);
   //if(D) Log.d(TAG, "缓存中获取的"+cacheKey+":"+item.bitmap);
   //设置标记
   imageView.setTag(url);
   if (item.bitmap == null ){
    //先显示加载中
    if (loadingView!= null ){
     loadingView.setVisibility(View.VISIBLE);
     imageView.setVisibility(View.INVISIBLE);
    } else if (loadingImage != null ){
     imageView.setImageDrawable(loadingImage);
    }
    //下载完成后更新界面
    item.setListener( new AbImageDownloadListener() {
     @Override
     public void update(Bitmap bitmap, String imageUrl) {
      //未设置加载中的图片,并且设置了隐藏的View
      if (loadingView != null && imageUrl.equals(imageView.getTag())){
       loadingView.setVisibility(View.INVISIBLE);
       imageView.setVisibility(View.VISIBLE);
      }
      //要判断这个imageView的url有变化,如果没有变化才set,
      //有变化就取消,解决列表的重复利用View的问题
      if(bitmap!=null&& imageUrl.equals(imageView.getTag())){
       if (D) Log.d(TAG, "图片下载,设置:" +imageUrl);
       imageView.setImageBitmap(bitmap);
      } else {
       if (errorImage != null && imageUrl.equals(imageView.getTag())){
        imageView.setImageDrawable(errorImage);
       }
      }
     }
    });
    if (D) Log.d(TAG, "图片下载,执行:" +url);
    mAbImageDownloadPool.execute(item);
   } else {
    if (loadingView != null ){
     loadingView.setVisibility(View.INVISIBLE);
     imageView.setVisibility(View.VISIBLE);
    }
    imageView.setImageBitmap(item.bitmap);
   }
  }

以上内容就是Android中ListView异步加载图片错位、重复、闪烁问题分析及解决方案,希望对大家今后的工作和学习有所帮助。

时间: 2015-08-19

android WebView加载html5介绍

Android设备多分辨率的问题 Android浏览器默认预览模式浏览 会缩小页面 WebView中则会以原始大小显示 Android浏览器和WebView默认为mdpi.hdpi相当于mdpi的1.5倍 ldpi相当于0.75倍 三种解决方式:1 viewport属性 2 CSS控制 3 JS控制 1 viewport属性放在HTML的<meta>中 Html代码 复制代码 代码如下: <SPANstyle="FONT-SIZE: x-small"> <

android异步加载图片并缓存到本地实现方法

在android项目中访问网络图片是非常普遍性的事情,如果我们每次请求都要访问网络来获取图片,会非常耗费流量,而且图片占用内存空间也比较大,图片过多且不释放的话很容易造成内存溢出.针对上面遇到的两个问题,首先耗费流量我们可以将图片第一次加载上面缓存到本地,以后如果本地有就直接从本地加载.图片过多造成内存溢出,这个是最不容易解决的,要想一些好的缓存策略,比如大图片使用LRU缓存策略或懒加载缓存策略.今天首先介绍一下本地缓存图片. 首先看一下异步加载缓存本地代码: 复制代码 代码如下: public

Android加载大分辨率图片到手机内存中的实例方法

还原堆内存溢出的错误首先来还原一下堆内存溢出的错误.首先在SD卡上放一张照片,分辨率为(3776 X 2520),大小为3.88MB,是我自己用相机拍的一张照片.应用的布局很简单,一个Button一个ImageView,然后按照常规的方式,使用BitmapFactory加载一张照片并使用一个ImageView展示. 代码如下: 复制代码 代码如下: btn_loadimage.setOnClickListener(new View.OnClickListener() { @Override   

Android XRecyclerView实现多条目加载

本文实例为大家分享了Android实现多条目加载展示的具体代码,供大家参考,具体内容如下 展示效果 这里写图片描述 依赖 testCompile 'junit:junit:4.12' compile 'com.hjm:BottomTabBar:1.1.1' compile 'com.android.support:design:23.4.0' compile 'com.android.support:mediarouter-v7:25.0.0' compile 'com.android.supp

Android下拉刷新上拉加载控件(适用于所有View)

前面写过一篇关于下拉刷新控件的文章下拉刷新控件终结者:PullToRefreshLayout,后来看到好多人还有上拉加载更多的需求,于是就在前面下拉刷新控件的基础上进行了改进,加了上拉加载的功能.不仅如此,我已经把它改成了对所有View都通用!可以随心所欲使用这两个功能~~ 我做了一个大集合的demo,实现了ListView.GridView.ExpandableListView.ScrollView.WebView.ImageView.TextView的下拉刷新和上拉加载.后面会提供demo的

Android实现加载广告图片和倒计时的开屏布局

这是一个android开屏布局的实例,可以用于加载广告图片和倒计时的布局.程序中设置的LayoutParams,划分额外空间比例为6分之5,具体权重比例可根据用户自己需求来自定义,异步加载广告图片,相关的Android代码. 具体实现代码如下: package cn.waps.extend; import android.app.Activity; import android.content.Context; import android.content.res.Configuration;

android动态加载布局文件示例

一.布局文件part.xml: 复制代码 代码如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="

Android中Glide加载库的图片缓存配置究极指南

零.选择Glide 为什么图片加载我首先推荐Glide? 图片加载框架用了不少,从afinal框架的afinalBitmap,Xutils的BitmapUtils,老牌框架universalImageLoader,著名开源组织square的picasso,google推荐的glide到FaceBook推出的fresco.这些我前前后后都体验过,那么面对这么多的框架,该如何选择呢?下面简单分析下我的看法. afinal和Xuils在github上作者已经停止维护了,开源社区最新的框架要属KJFra

Android关于Glide的使用(高斯模糊、加载监听、圆角图片)

高斯模糊.加载监听.圆角图片这些相信大家都很熟悉,那如何实现这些效果,请大家参考本文进行学习. 1.引用 compile 'com.github.bumptech.glide:glide:3.7.0' 2.加载图片 2.1 基本加载 Glide.with(context)     .load(url)     .into(imageView); 2.2 设置加载中和加载失败的情况 Glide.with(context) .load(url) .placeholder(R.drawable.loa

Android中使用RecyclerView实现下拉刷新和上拉加载

推荐阅读:使用RecyclerView添加Header和Footer的方法                       RecyclerView的使用之HelloWorld RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍如何为RecyclerView添加下拉刷新和上拉加载,过去在ListView当中添加下拉刷新和上拉加载是非常方便的利用addHeaderView和addFooterVie

基于iScroll实现下拉刷新和上滑加载效果

本文实例为大家分享了iScroll下拉刷新上滑加载展示的具体代码,供大家参考,具体内容如下 html代码: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=devi

Android仿XListView支持下拉刷新和上划加载更多的自定义RecyclerView

首先给大家展示下效果图,感觉还不错,请继续往下阅读: 下拉刷新:        上划加载        在项目更新的过程中,遇到了一个将XListView换成recyclerView的需求,而且更换完之后大体效果不能变,但是对于下拉刷新这样的效果,谷歌给出的解决方案是把RecyclerView放在一个SwipeRefreshLayout中,但是这样其实是拉下一个小圆形控件实现的,和XListView的header效果不同.在网上找了很多的别人代码,都没有实现我想要的效果,于是自己动手写了一个.

Android RecyclerView实现下拉刷新和上拉加载

RecyclerView已经出来很久了,许许多多的项目都开始从ListView转战RecyclerView,那么,上拉加载和下拉刷新是一件很有必要的事情. 在ListView上,我们可以通过自己添加addHeadView和addFootView去添加头布局和底部局实现自定义的上拉和下拉,或者使用一些第三方库来简单的集成,例如Android-pulltorefresh或者android-Ultra-Pull-to-Refresh,后者的自定义更强,但需要自己实现上拉加载. 而在下面我们将用两种方式

Android RecyclerView下拉刷新和上拉加载更多

今天终于有点时间,来写了一下: 为RecyclerView实现下拉刷新和上拉加载更多.今天会在前面的两篇文章的基础上: RecyclerView系列之(1):为RecyclerView添加Header和Footer RecyclerView系列之(2):为RecyclerView添加分隔线 继续讲述RecyclerView中一些常用组件的实现下拉刷新和上拉加载更多的功能. 在现在的Android手机应用中,几乎每一个APP都有下拉刷新和上拉加载更多的功能,它们的重要性不言而喻. 先不多说,先看效

Android中Listview下拉刷新和上拉加载更多的多种实现方案

listview经常结合下来刷新和上拉加载更多使用,本文总结了三种常用到的方案分别作出说明. 方案一:添加头布局和脚布局 android系统为listview提供了addfootview和addheadview两个API.这样可以直接自定义一个View,以添加视图的形式实现下来刷新和上拉加载. 实现步骤    1.创建一个类继承ListView:class PullToRefreshListView extends ListView: 2.在构造方法中添加HeadView:addHeaderVi

Android手把手教大家制作APP首页(下拉刷新、自动加载)

一.概述 作为一名三年Android开发经验的程序员,今天和大家一起实战一款APP的首页功能,这个首页在我们平时接触中还是很常见的,虽然页面简单,但是里面涉及的功能点还是挺多的.代码如有不足的还望各路同仁指点一二. 页面中使用的开发库: 整个首页架构使用的是LRecyclerView,包含下拉刷新和自动加载功能 compile 'com.github.jdsjlzx:LRecyclerView:1.3.3' 无限循环轮播图使用的是convenientbanner,效果还是很顺畅的,还可以根据自己

android使用PullToRefresh实现下拉刷新和上拉加载

PullToRefresh是一套实现非常好的下拉刷新库,它支持: 1.ListView 2.ExpandableListView 3.GridView 4.WebView 等多种常用的需要刷新的View类型,而且使用起来也十分方便. demo实例下载 下载完成,将它导入到eclipse中,作为一个library导入到你的工程中就好了. 一.废话少说,下拉刷新Go. 1.在你的布局文件中加上你想用的View就好了,比如这儿我想用一个支持下拉 刷新的ExpandableListView <com.h

Android程序开发之使用PullToRefresh实现下拉刷新和上拉加载

PullToRefresh是一套实现非常好的下拉刷新库,它支持: 1.ListView 2.ExpandableListView 3.GridView 4.WebView 等多种常用的需要刷新的View类型,而且使用起来也十分方便. (下载地址:https://github.com/chrisbanes/Android-PullToRefresh) 下载完成,将它导入到eclipse中,作为一个library导入到你的工程中就好了. 一.废话少说,下拉刷新go. 1.在你的布局文件中加上你想用的

Android控件PullRefreshViewGroup实现下拉刷新和上拉加载

本文实例为大家分享了Android实现下拉刷新和上拉加载更多的具体代码,供大家参考,具体内容如下 先分享下源码:Android实现下拉刷新和上拉加载更多 实现思路:由PullRefreshViewGroup控件来接管标准控件(比如RecyclerView.ListView等)的滑动,调用标准控件的内部方法进行短距离滑动,不再由标准控件自己来处理事件,而完全由PullRefreshViewGroup控件来处理触摸事件.标准控件内部的滑动距离等属性,通过反射获得computeVerticalScro