文章,参考自: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(); } }
源码下载
时间: 2024-10-14 08:05:49