文章,参考自:http://blog.csdn.net/guolin_blog/article/details/34093441
不过本身自己的代码也也修改了很多。
在网络上找了很多例子,但是很多都是,不十分满意,
1.要不就是图片错乱,
2.第一次运行,如果滚动加载过快,要不就是无限多的线程,
3.要不就是在同一视图里面,如果没被缓冲,而且又有相同链接的话,某些视图即不显示出来。
网络下载,我用的是开源框格volley。
异步下载方式:
1.在页面滚动的时候,中断线程,停止下载,
2.页面停止的时候,开启线程下载图片。
缓冲技术:
1.图片显示,如果内存有,优先从内存取,
2.如果内存没有,从硬盘取,然后保存进内存中
3.如果所有缓冲都没有,即网络下载,然后再缓冲到硬盘和内存中(硬盘和内存里面的并非全部一样,内存有可能会溢出,会删除排在前面的一些缓冲,但是硬盘一直有,除非内存卡爆满)
过滤相同链接的重复下载,
1.在同一视图里,加载所有要下载的图片(key),链接(value)到map
2.map.containvalue(url),过滤重复的链接,防止重复下载
解决重复链接,没被缓冲前,在同一视图里,后面的图片不显示的问题:
1.在下载某个链接的图片后,
2.在map寻找一样的下载链接,
3.把相同的链接的图片显示出来
4.每下载完一个链接图片,在map里面删除key:作用:1.减少内存占用 2.判断是否该停止线程运行(主要)
/***
*
*
* 异步下载方式:
* 1.在页面滚动的时候,中断线程,停止下载,
* 2.页面停止的时候,开启线程下载图片。
*
*
* 缓冲技术:
* 1.图片显示,如果内存有,从内存取,
* 2.如果硬盘有,从硬盘取,然后保存进内存中
* 3.网络下载,缓冲到硬盘和内存中
*
*
* 过滤相同链接的重复下载,
* 1.Hashmap<View, String> ----> 在同一视图里,加载所有要下载的图片(key),链接(value)
* 2.过滤重复的链接,防止重复下载
*
* 解决重复图片不显示,在同一个视图里面,如果有重复的链接图片,并且都还没有被缓冲,以前的版本不显示的
* 1.在下载某个链接的图片后,
* 2.在Hashmap<View, String>寻找一样的下载链接,
* 3.就把相同的显示出来
* 4.每下载完一个链接图片,在map里面删除key,减少内存占用
* 5.在下载完map里面的所有链接,关闭线程
* 6.下次视图在开始启动线程下载图片的时候,先清空map,才加载要下载的图片(key),链接(value)
*/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageRequest;
import com.android.volley.toolbox.Volley;
/**
* GridView的适配器,负责异步从网络上下载图片展示在照片墙上。
*
* @author guolin
*/
public class PhotoWallAdapter extends ArrayAdapter<String> implements
OnScrollListener {
/**
* GridView的实例
*/
private GridView mPhotoWall;
/**
* 第一张可见图片的下标
*/
private int mFirstVisibleItem;
/**
* 一屏有多少张图片可见
*/
private int mVisibleItemCount;
/**
* 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。
*/
private boolean isFirstEnter = true;
/**
* volley的request
*/
private RequestQueue mQueue = null;
/**
* 内存缓冲
*/
private BitmapCache mMemoryCache = null;
/**
* 硬盘缓冲
*/
private FilesUtil mHardDriveCache = null;
/**
* 解决同一屏幕,相同链接图片不显示的问题
*/
private HashMap<View, String> mSameViewUrls = null;
@SuppressLint("NewApi")
public PhotoWallAdapter(Context context, int textViewResourceId,
String[] objects, GridView photoWall) {
super(context, textViewResourceId, objects);
this.mPhotoWall = photoWall;
this.mPhotoWall.setOnScrollListener(this);
// 初始化volley.内存缓冲,硬盘缓冲等相关参数
init(context);
this.mSameViewUrls = new HashMap<View, String>();
}
/**
* 初始化volley osmdroid
*
* @param context
*/
private void init(Context context) {
this.mQueue = Volley.newRequestQueue(context);
this.mMemoryCache = new BitmapCache();
this.mHardDriveCache = new FilesUtil(context);
// this.mHardDriveCache.removeAll();
}
@SuppressLint("NewApi")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final String url = getItem(position);
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(
R.layout.gv_img_wall_item, null);
} else {
view = convertView;
}
final ImageView photo = (ImageView) view.findViewById(R.id.photo);
final TextView tv = (TextView) view.findViewById(R.id.num);
tv.setText((position + 6) + "");
// 给ImageView设置一个Tag,保证异步加载图片时不会乱序
photo.setTag(url);
setImageView(url, photo);
return view;
}
/**
* 给ImageView设置图片。首先从本地硬盘或者内存中取图片,如果没有, 就给ImageView设置一张默认图片。
*
* @param imageUrl
* 图片的URL地址,用于作为LruCache的键。
* @param imageView
* 用于显示图片的控件。
*/
private void setImageView(String imageUrl, ImageView imageView) {
Bitmap bitmap = getBitmapFromCache(imageUrl);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
Log.i("-*--------", "本地硬盘或者内存添加");
} else {
imageView.setImageResource(R.drawable.empty_photo);
Log.i("-*--------", "设置默认图片");
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
mFirstVisibleItem = firstVisibleItem;
mVisibleItemCount = visibleItemCount;
// 下载的任务应该由onScrollStateChanged里调用,但首次进入程序时onScrollStateChanged并不会调用,
// 因此在这里为首次进入程序开启下载任务。
if (isFirstEnter && visibleItemCount > 0) {
loadBitmaps(view, firstVisibleItem, visibleItemCount);
isFirstEnter = false;
}
}
@Override
/**
* 仅当GridView静止时才去下载图片,GridView滑动时取消所有正在下载的任务
*/
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
boolean isDownload = loadBitmaps(view, mFirstVisibleItem,
mVisibleItemCount);
if (isDownload) {
mQueue.start();
Log.i("----------", "开启下载");
} else {
mQueue.stop();
Log.i("----------", "中断停止下载");
}
} else {
mQueue.stop();
}
}
/**
* 从LruCache中获取一张图片,如果不存在就返回null。
*
* @param imageUrl
* LruCache的键,这里传入图片的URL地址。
* @return 对应传入键的Bitmap对象,或者null。
*/
@SuppressLint("NewApi")
public Bitmap getBitmapFromCache(String imageUrl) {
// 先从内存取,看看是否有值
Bitmap bitmap = mMemoryCache.getBitmap(imageUrl);
// 没有的话,再从硬盘里面取
if (bitmap == null) {
bitmap = mHardDriveCache.getBitmap(imageUrl
.replaceAll("[^\\w]", ""));
if (bitmap != null) {
mMemoryCache.putBitmap(imageUrl, bitmap);
}
Log.i("----------", "从硬盘取的");
} else {
Log.i("----------", "从内存取的");
}
return bitmap;
}
/**
* 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象,
* 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。
*
* @param view
* @param firstVisibleItem
* 第一个可见的ImageView的下标
* @param visibleItemCount
* 屏幕中总共可见的元素数
* @return 如果有需要下载的图片,返回true;否则返回false
*/
private boolean loadBitmaps(AbsListView view, int firstVisibleItem,
int visibleItemCount) {
boolean isDownload = false;
mSameViewUrls.clear();
try {
for (int i = firstVisibleItem; i < firstVisibleItem
+ visibleItemCount; i++) {
final String imageUrl = Images.imageThumbUrls[i];
ImageView imageView = (ImageView) mPhotoWall
.findViewWithTag(imageUrl);
// 先从内存和硬盘查找看看,没有的话,再去下载
Bitmap bitmap = getBitmapFromCache(imageUrl);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
View viewItem = view.getChildAt(i - firstVisibleItem)
.findViewById(R.id.photo);
// 过滤重复链接
if (!mSameViewUrls.containsValue(imageUrl)) {
addDownloadTask(imageUrl);
}
mSameViewUrls.put(viewItem, imageUrl);
isDownload = true;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return isDownload;
}
/**
* 下载图片,再把图片放到内存还有硬盘缓冲里面
*
* @param imageUrl
*/
private void addDownloadTask(final String imageUrl) {
final ImageRequest imageRequest = new ImageRequest(imageUrl,
new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
try {
if (response != null) {
// 保存到内存里
mMemoryCache.putBitmap(imageUrl, response);
// 保存到硬盘里
mHardDriveCache.savaBitmap(
imageUrl.replaceAll("[^\\w]", ""),
response);
// 显示图片
showBitmap(response);
}
} catch (Exception e) {
// TODO: handle exception
Log.e("------------", "error", e);
}
}
private void showBitmap(Bitmap response) throws Exception {
Iterator<Entry<View, String>> iterator = mSameViewUrls
.entrySet().iterator();
//搞个列表,删去已经下载的链接,减少下次过滤相同链接的循环。
ArrayList<View> delList = new ArrayList<View>();
while (iterator.hasNext()) {
Entry<View, String> entry = iterator.next();
//设置有相同链接的图片,
if (entry.getValue().equals(imageUrl)) {
ImageView key = (ImageView) entry.getKey();
key.setImageBitmap(response);
//添加已经下载的连接,准备删除
delList.add(key);
}
}
//删去已经下载的链接
for (View view : delList) {
mSameViewUrls.remove(view);
}
//如果全部链接下载完结,中断线程
if (mSameViewUrls.isEmpty()) {
mQueue.stop();
}
}
}, 0, 0, Config.RGB_565, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
try {
ImageView imageView = (ImageView) mPhotoWall
.findViewWithTag(imageUrl);
imageView.setImageResource(R.drawable.ic_launcher);
} catch (Exception e) {
// TODO: handle exception
Log.e("------------", "error", e);
}
}
});
mQueue.add(imageRequest);
}
public void stopNetTasks() {
// TODO Auto-generated method stub
mQueue.stop();
}
}