Android ListView 加载图片如何解决多张地址一样会导致部分图片加载不了的问题

导致图片无法显示的根本原因在于多个同样地址的图片由于采用异步形式,导致耗时长去请求的线程回调时通过imageview 绑定的tag 没等找到 对应的imageview ,所以出现了部分图没加载的现象;

所以为了解决此问题,添加了一个类似缓存的map 存储当时发出请求没能在缓存找到的imageview 按照 地址:images 形式临时存储;等线程取到图片后取出对应的images 一一复制,清空缓存;

下面为解决的代码

1.图片加载工具类:

AsyncImageLoaderForDrawable


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.widget.ImageView;
import basic.param.SysParam;
/**
 * 
 * @author wgl
 *
 */
public class AsyncImageLoaderForDrawable {
	 Handler handler;
	private TreeMap<String, SoftReference<Drawable>> imageCache;
	private Activity ct;
	public static final String SD_PATH = SysParam.LOCAL_PATH;
	public static final String IMG_CACHE_PATH =SD_PATH+File.separator+"cache"; //网络图片默认缓存路径
	public static final String IMG_PATH =SD_PATH+File.separator+"image";       //拍照、相册选择的压缩图片存储路径
	public static final String BASE_PATH = SD_PATH;                            //应用根目录
	/**
	 * 等待被渲染的图片队列,key:加载图片的地址 value:为地址为key的imageview的集合;
	 */
	private HashMap<String, List<ImageView>> waitCache ;
	public AsyncImageLoaderForDrawable(Activity ct) {
		// TODO Auto-generated constructor stub
		imageCache = new TreeMap<String, SoftReference<Drawable>>();
		waitCache = new HashMap<String, List<ImageView>>();
		this.ct = ct;
		/*
		 * 初始化cache、image 文件夹
		 */
		File cacheD = new File(IMG_CACHE_PATH);
		if(!cacheD.exists()){
			cacheD.mkdirs();
		}
		cacheD = new File(IMG_PATH);
		if(!cacheD.exists()){
			cacheD.mkdirs();
		}
	}
	
	/**
	 * 异步请求图片并加入缓存, (加载多个相同图片时,耗时长的线程,如果调用者通过tag查找原来的imageview 可能会找不到对应的image,需要单独处理找到image)
	 * @param imageUrl可以使网络地址或者本地缓存下的文件名
	 * @param saveName 如果出入的值不为null ,将imageUrl是网络地址的图片保存在 缓存目录下 命名为 saveName
	 * @param imageCallback
	 * @return
	 */
	public Drawable loadDrawable(final String imageUrl,final String saveName,final ImageCallback imageCallback){
		if(imageCache.containsKey(imageUrl)){
			SoftReference<Drawable> softReference = imageCache.get(imageUrl);
			if(softReference.get()!=null){//如果没有被回收,使用内存缓存的对象
				return softReference.get();
			}else{
				imageCache.remove(imageUrl);
			}
		}
		final Handler handler2 = new Handler(){
			public void handleMessage(final Message message){
				ct.runOnUiThread(new Runnable() {
					@Override
					public void run() {
						// TODO Auto-generated method stub
						Map<String, Object> data = (Map<String, Object>) message.obj;
						imageCallback.imageLoaded((Drawable)data.get("draw"), (String)data.get("url"));
					}
				});
				
			}
		};
		new Thread(){
			@Override
			public void run() {
				Drawable drawable = loadImageFromUrl(imageUrl,saveName==null? null:IMG_CACHE_PATH+File.separator+saveName);
				Map<String , Object> obj = new HashMap<String, Object>();
				obj.put("draw", drawable);
				obj.put("url", imageUrl);
				Message message = handler2.obtainMessage(0, obj);
				handler2.handleMessage(message);//线程通信
			}
		}.start();
		
		return null;
	}
	
	/**
	 * 异步请求图片并加入缓存, 用于请求处理存在多个地址一样的问题
	 * @param imageUrl可以使网络地址或者本地缓存下的文件名
	 * @param saveName 如果出入的值不为null ,将imageUrl是网络地址的图片保存在 缓存目录下 命名为 saveName
	 * @param imageCallback
	 * @return
	 */
	public Drawable loadDrawable(final String imageUrl,final String saveName,ImageView image){
		if(imageCache.containsKey(imageUrl)){
			SoftReference<Drawable> softReference = imageCache.get(imageUrl);
			if(softReference.get()!=null){//如果没有被回收,使用内存缓存的对象
				return softReference.get();
			}else{
				imageCache.remove(imageUrl);
			}
		}
		List<ImageView> images= waitCache.get(imageUrl);
		if(images!=null){
			images.add(image);
			return null;
		}else{
			images = new ArrayList<ImageView>();
			images.add(image);
			waitCache.put(imageUrl, images);
		}
		final Handler handler2 = new Handler(){
			public void handleMessage(final Message message){
				ct.runOnUiThread(new Runnable() {
					@Override
					public void run() {
						// TODO Auto-generated method stub
						Map<String, Object> data = (Map<String, Object>) message.obj;
						List<ImageView> images = waitCache.get((String)data.get("url"));
						waitCache.remove(imageUrl);
						if(images!=null&&!images.isEmpty()){
							for(ImageView img:images){
								if(img!=null){
									img.setImageDrawable((Drawable)data.get("draw"));
								}
							}
						}
						images= null;
						data = null;
					}
				});
				
			}
		};
		new Thread(){
			@Override
			public void run() {
				Drawable drawable = loadImageFromUrl(imageUrl,saveName==null? null:IMG_CACHE_PATH+File.separator+saveName);
				Map<String , Object> obj = new HashMap<String, Object>();
				obj.put("draw", drawable);
				obj.put("url", imageUrl);
				Message message = handler2.obtainMessage(0, obj);
				handler2.handleMessage(message);//线程通信
			}
		}.start();
		
		return null;
	}
	/**
	 * 从网络地址获取的inputStrean 获取 drawable 对象
	 * @param url 网络地址 或者本地图片路径 或者指定目录下 IMG_PATH 下的文件名
	 * @param savename 网络图片要保存到本地的文件名 缓存路径为IMG_CACHE_PATH下
	 * @return
	 */
	public Drawable loadImageFromUrl(String url,String savename) {
		if(imageCache.containsKey(url)){
			SoftReference<Drawable> softReference = imageCache.get(url);
			if(softReference.get()!=null){//如果没有被回收,使用内存缓存的对象
				return softReference.get();
			}
		}
		imageCache.put(url, null);
		InputStream i = null; 
		Drawable drawable=null ;
		if(url.startsWith("http:")){
			try {
				String name = MD5Util.string2MD5(url);
				File img = new File(IMG_CACHE_PATH+File.separator+name);
				if(img.exists()){ //查找本地缓存有没有
					i = new FileInputStream(img);
					drawable = Drawable.createFromStream(i, "src");
				}else{
					URL m;  
					m = new URL(url);
					URLConnection connection = m.openConnection();
					i=connection.getInputStream();
//					i = (InputStream) m.getContent();
					if(i==null){
						return null;
					}
					if(savename==null){
						savename = IMG_CACHE_PATH+File.separator+name;
					}
					if(savename!=null){
						int byteread = 0;
						File file =new File(savename);
						FileOutputStream fs = new FileOutputStream(file);
						byte[] buffer = new byte[1024]; 
						while ( (byteread = i.read(buffer)) != -1) {
							fs.write(buffer, 0, byteread); 
						}
						fs.flush();
						fs.close();
						i.close();
						i = new FileInputStream(file);
						drawable = Drawable.createFromStream(i, "src");
					}
				}
			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}else{
			String path= url;
			if(url.startsWith(File.separator)||url.startsWith("file:")){//绝对路径
				path= url;
				if(url.startsWith("file:")){
					path =path.substring(path.indexOf("file:")+"file:".length());
				}
			}else{
				path =IMG_PATH+File.separator+url;
			}
			File img = new File(path);
			if(!img.exists()){
				return null;
			}
			try {
				i = new FileInputStream(img);
				drawable = Drawable.createFromStream(i, "src");
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(drawable!=null){
			imageCache.put(url, new SoftReference<Drawable>(drawable));//放入缓存drawable的软引用
		}
		
		return drawable;
	}
	
	public interface ImageCallback {  
		public void imageLoaded(Drawable imageDrawable, String imageUrl);  
	}  
	
	public HashMap<String, List<ImageView>> getWaitCache() {
		return waitCache;
	}

}

2.调用形式

public Drawable getImageDrawble(String url,ImageView image){
		Drawable drawable = imageLoader.loadDrawable(url, null, image);
		if(drawable==null){//解决
			return ct.getResources().getDrawable(R.drawable.defalt_image);
		}
		return drawable;
	}

img.setImageDrawable(getImageDrawble(httpHost,img));//异步加载图片

如果存在bug,欢迎指正

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现 Android ListView 上拉加载更多的常用方法是使用下拉刷新框架,如 SwipeRefreshLayout 和 SmartRefreshLayout。这些框架提供了方便的 API 来实现下拉刷新和上拉加载更多。 这里提供一种基于 SmartRefreshLayout 的实现方法: 1. 在布局文件中添加 SmartRefreshLayout 和 ListView 组件: ``` <com.scwang.smartrefresh.layout.SmartRefreshLayout android:id="@+id/refreshLayout" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent" /> </com.scwang.smartrefresh.layout.SmartRefreshLayout> ``` 2. 在 Activity 或 Fragment 中初始化 SmartRefreshLayout 和 ListView: ``` SmartRefreshLayout refreshLayout = findViewById(R.id.refreshLayout); ListView listView = findViewById(R.id.listView); // 设置下拉刷新监听器 refreshLayout.setOnRefreshListener(new OnRefreshListener() { @Override public void onRefresh(@NonNull RefreshLayout refreshLayout) { // 下拉刷新操作 // 刷新完成后调用 refreshLayout.finishRefresh() 方法 } }); // 设置上拉加载更多监听器 refreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() { @Override public void onLoadMore(@NonNull RefreshLayout refreshLayout) { // 上拉加载更多操作 // 加载完成后调用 refreshLayout.finishLoadMore() 方法 } }); ``` 3. 当需要触发上拉加载更多时,调用 SmartRefreshLayout 的 `autoLoadMore()` 方法即可触发加载更多。 ``` refreshLayout.autoLoadMore(); ``` 在上面的代码中,你需要在上拉加载更多操作完成后手动调用 `refreshLayout.finishLoadMore()` 方法来通知 SmartRefreshLayout 完成加载更多操作。同样,在下拉刷新操作完成后,你也需要调用 `refreshLayout.finishRefresh()` 方法来通知 SmartRefreshLayout 完成下拉刷新操作。 总体来说,以上是一种基于 SmartRefreshLayout 的实现上拉加载更多的方法,你也可以使用其他类似框架来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值