- 浏览: 109859 次
- 性别:
- 来自: 武汉人在北京
文章分类
最新评论
-
cossetjt:
[color=green][color=darkred][/c ...
又优化了一下 Android ListView 异步加载图片 -
陌夏想念你:
为什么只有一个界面,显示这是第一个Activiyt,然后按什么 ...
Android两侧推出导航菜单的实现 -
cometowilling:
谢谢谢谢谢谢
Android左侧推出导航的简单实现 -
wangyi891223:
北京工作是不好找啊 我找了一个多月也是没有找到 哎
现在的行情很不好?顺便求职 -
醒太迟:
郁闷,我用楼主的方法,第一次进入列表界面,显示都比较正常,第二 ...
又优化了一下 Android ListView 异步加载图片
写这篇文章并不是教大家怎么样用listview异步加载图片,因为这样的文章在网上已经有很多了,比如这位仁兄写的就很好:
http://www.iteye.com/topic/685986
我也是因为看了这篇文章而受到了启发。
先说说这篇文章的优点把,开启线程异步加载图片,然后刷新UI显示图片,而且通过弱引用缓存网络加载的图片,节省了再次连接网络的开销。
这样做无疑是非常可取的方法,但是加载图片时仍然会感觉到轻微的卡屏现象,特别是listview里的item在进行快速滑动的时候。
我找了一下原因,可能是在listview快速滑动屏幕的时候划过的item太多 而且每次调用getView方法后就会异步的在过去某个时间内用handler刷新一下UI,
如果在同一时间调用handler刷新UI次数多了就会造成这样的卡屏现象。
后来又一想,其实我们完全没有必要在listview正在滑动的时候去后台加载图片(不管这是图片是在缓存里还是在网络上),这样无疑造成了很大的资源浪费。
我们只需要在listview滑动停止之后再去加载listview里面显示的几个item里面的图片就好了。
根据以上想法,我做了一些设计改造:
1.在adapter 的 getview方法里面启动加载图片的thread,如果listview在滑动则wait
2.监听listview滑动停止事件,获得listview显示的item的最上面和最下面的序号,并唤醒所有加载图片的thread,判断加载图片的序号是否是在范围内,如果是则继续加载,如果不是则结束thread
@Override public View getView(int position, View convertView, ViewGroup parent) { if(convertView == null){ convertView = mInflater.inflate(R.layout.book_item_adapter, null); } BookModel model = mModels.get(position); convertView.setTag(position); ImageView iv = (ImageView) convertView.findViewById(R.id.sItemIcon); TextView sItemTitle = (TextView) convertView.findViewById(R.id.sItemTitle); TextView sItemInfo = (TextView) convertView.findViewById(R.id.sItemInfo); sItemTitle.setText(model.book_name); sItemInfo.setText(model.out_book_url); iv.setBackgroundResource(R.drawable.rc_item_bg); syncImageLoader.loadImage(position,model.out_book_pic,imageLoadListener); return convertView; } SyncImageLoader.OnImageLoadListener imageLoadListener = new SyncImageLoader.OnImageLoadListener(){ @Override public void onImageLoad(Integer t, Drawable drawable) { //BookModel model = (BookModel) getItem(t); View view = mListView.findViewWithTag(t); if(view != null){ ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon); iv.setBackgroundDrawable(drawable); } } @Override public void onError(Integer t) { BookModel model = (BookModel) getItem(t); View view = mListView.findViewWithTag(model); if(view != null){ ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon); iv.setBackgroundResource(R.drawable.rc_item_bg); } } }; public void loadImage(){ int start = mListView.getFirstVisiblePosition(); int end =mListView.getLastVisiblePosition(); if(end >= getCount()){ end = getCount() -1; } syncImageLoader.setLoadLimit(start, end); syncImageLoader.unlock(); } AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { case AbsListView.OnScrollListener.SCROLL_STATE_FLING: DebugUtil.debug("SCROLL_STATE_FLING"); syncImageLoader.lock(); break; case AbsListView.OnScrollListener.SCROLL_STATE_IDLE: DebugUtil.debug("SCROLL_STATE_IDLE"); loadImage(); //loadImage(); break; case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: syncImageLoader.lock(); break; default: break; } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // TODO Auto-generated method stub } };
import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReference; import java.net.URL; import java.util.HashMap; import android.graphics.drawable.Drawable; import android.os.Environment; import android.os.Handler; public class SyncImageLoader { private Object lock = new Object(); private boolean mAllowLoad = true; private boolean firstLoad = true; private int mStartLoadLimit = 0; private int mStopLoadLimit = 0; final Handler handler = new Handler(); private HashMap<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>(); public interface OnImageLoadListener { public void onImageLoad(Integer t, Drawable drawable); public void onError(Integer t); } public void setLoadLimit(int startLoadLimit,int stopLoadLimit){ if(startLoadLimit > stopLoadLimit){ return; } mStartLoadLimit = startLoadLimit; mStopLoadLimit = stopLoadLimit; } public void restore(){ mAllowLoad = true; firstLoad = true; } public void lock(){ mAllowLoad = false; firstLoad = false; } public void unlock(){ mAllowLoad = true; synchronized (lock) { lock.notifyAll(); } } public void loadImage(Integer t, String imageUrl, OnImageLoadListener listener) { final OnImageLoadListener mListener = listener; final String mImageUrl = imageUrl; final Integer mt = t; new Thread(new Runnable() { @Override public void run() { if(!mAllowLoad){ DebugUtil.debug("prepare to load"); synchronized (lock) { try { lock.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } if(mAllowLoad && firstLoad){ loadImage(mImageUrl, mt, mListener); } if(mAllowLoad && mt <= mStopLoadLimit && mt >= mStartLoadLimit){ loadImage(mImageUrl, mt, mListener); } } }).start(); } private void loadImage(final String mImageUrl,final Integer mt,final OnImageLoadListener mListener){ if (imageCache.containsKey(mImageUrl)) { SoftReference<Drawable> softReference = imageCache.get(mImageUrl); final Drawable d = softReference.get(); if (d != null) { handler.post(new Runnable() { @Override public void run() { if(mAllowLoad){ mListener.onImageLoad(mt, d); } } }); return; } } try { final Drawable d = loadImageFromUrl(mImageUrl); if(d != null){ imageCache.put(mImageUrl, new SoftReference<Drawable>(d)); } handler.post(new Runnable() { @Override public void run() { if(mAllowLoad){ mListener.onImageLoad(mt, d); } } }); } catch (IOException e) { handler.post(new Runnable() { @Override public void run() { mListener.onError(mt); } }); e.printStackTrace(); } } public static Drawable loadImageFromUrl(String url) throws IOException { DebugUtil.debug(url); if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ File f = new File(Environment.getExternalStorageDirectory()+"/TestSyncListView/"+MD5.getMD5(url)); if(f.exists()){ FileInputStream fis = new FileInputStream(f); Drawable d = Drawable.createFromStream(fis, "src"); return d; } URL m = new URL(url); InputStream i = (InputStream) m.getContent(); DataInputStream in = new DataInputStream(i); FileOutputStream out = new FileOutputStream(f); byte[] buffer = new byte[1024]; int byteread=0; while ((byteread = in.read(buffer)) != -1) { out.write(buffer, 0, byteread); } in.close(); out.close(); Drawable d = Drawable.createFromStream(i, "src"); return loadImageFromUrl(url); }else{ URL m = new URL(url); InputStream i = (InputStream) m.getContent(); Drawable d = Drawable.createFromStream(i, "src"); return d; } } }
为了让大家更好的理解,我添加了源代码例子,还特地美化了一下UI
除了本身已有的弱引用缓存图片,我还添加了本地SD卡缓存图片(这两种缓存方法各有好处,如果图片经常变化建议内存缓存图片,如果是不经常修改的图片建议SD卡缓存)
用真机测试了一下,感觉无比流畅(测试机是U8500非常垃圾的一种低端机)
欢迎大家拍砖讨论
评论
1.滑动的时候不会加载,滑动停止才加载
2.默认添加了软引用,先从网上下载,然后保存在本地sd卡里,内存里也有一份,内存里有就先从内存里拿,内存里没有就从本地sd卡里拿,sd卡里没有就从网上下
3.你提的问题我已经回答很多遍了,我可以肯断定你对ListView的运行机制不了解,如果你有所了解就不会问这些问题
4.我做这个并不是直接给别人用的,主要是给某些人思路,然后可以自行修改,做成真正自己适用的,组件本身也有很多问题,之后我也做了多次修改只是没有发布。
1、第一次加载listview的时候,firstLoad==true,所以不滑动ListView的话回调onImageLoad时又会触发getView方法,然后无限循环。
2、如果用户一直滑动滚动条(非常暴力的那种)时,会一直调用loadImage,进而会一直new Thread,但创建过多线程是一种非常占用CPU以及消耗资源的做法,能不能有方法使ListView只有在停止滑动时才创建子线程并加载图片,或者只创建一个子线程顺序加载当前可视区域内的图片。
我使用了java的ExecutorService中的newCachedThreadPool() 和newFixedThreadPool(int nThreads)但效果又不是很好。
后来又尝试了HandlerThread在该线程的消息队列中加载图片,显示图片又出现了问题,而且消耗的内存与你的方法比较也是一样多。
不是标题党
发表评论
-
android 编译 记录
2014-10-14 11:19 01. 取消odex编译 build/core/package. ... -
android Dialog 全屏的方法
2014-07-25 12:09 1653最近有人问我Android 里面的dialog怎么全屏,他说他 ... -
又优化了一下Android ListView 异步加载图片(续)
2012-11-23 15:07 1947之前发表过一篇文章: 又优化了一下 Android List ... -
Android Cocos2dx 之用eclipse开发调试c++
2012-11-07 18:46 2499最近在对cocos2dx非常敢兴趣,但对于我这个搞java的小 ... -
Android两侧推出导航菜单的实现
2012-08-28 11:56 6252前几天发表了 这篇文章:Android左侧推出导航的简单实现 ... -
Android左侧推出导航的简单实现
2012-08-24 18:07 3406前段时间有朋友像我问起类似于qq通讯录那种手势滑动出现左侧菜单 ... -
个人对于即时通讯(推送)系统构建的一些拙见
2012-07-27 13:19 4175今天有一个哥们在群里 ... -
发布了一个小说阅读的应用-幻想阅读
2012-04-27 12:24 1570闲着没事就做了这么一个应用,不是很完善,bug很多,不过已经勉 ... -
Android 屏蔽 activity 按键音
2012-02-09 16:58 2967只需要在activity里面如下设置: setVolumeC ... -
初步还原了一下 翻页卷曲效果
2011-07-26 11:03 1813这篇blog主要是还原了一下 http://www.iteye ... -
3D星球旋转效果
2011-07-19 12:04 2160昨天花了1个多小时做的,现在还有点粗糙,等完善后再开源分享一下 ... -
又见3D旋转效果
2011-07-19 11:59 1541以前做过一个类似的后来完善了一下,做成了公用的组件,现在还有些 ... -
Android 备忘录
2010-01-10 22:17 5283android中如何改变AudioButt ... -
类似与oms 3D转屏 效果实现 (更新)
2009-12-29 10:50 2188现在差不多是oms 3D 转屏 的效果了,做的还不是很完善 ... -
android 模拟视角转换
2009-12-27 00:39 1366做了个android下面模拟的3D 视角转换效果 不过没有用到 ...
相关推荐
Android 异步加载图片,对ListView的异步加载图片的功能演示,主要根据url读取图片返回流的方法。为了方便演示,将请求图片的链接先固定,每读取好一个图片就更新,界面比较简单,当然你可以做成比较好的,像很多好...
Android Listview异步加载图片,图片错位解决方案
android listview异步加载图片实例 用到了线程池 下载的图片会保存到本地 并在数据库中保留记录 再次加载时会直接从本地读取
Android实现ListView异步加载图片
NULL 博文链接:https://zjingye.iteye.com/blog/1936268
Android ListView异步加载图片,优化滚动效果,不卡顿、流畅显示。主要给新人了解ListView和AsyncTask、Json等使用。
android listView图片异步加载(拖动时不加载,双缓存)
Android ListView 异步加载图片,一点也不卡,使用AsyncTask和WeakReference,注释详尽
android listview 异步加载网络图片
AystnPicture_Android ListView异步加载图片.rar
android中listView的优化,同时listView中的列表内容来源于网络,从网络上下载图片图片显示在listViw中,测试用的是1000个item,测试listView的优化!
AsyncTask的使用及ListView的常见优化 asyncTask异步加载数据 使用了LruCache优化图片加载 通过滑动监听提高ListView滑动流畅度.rar,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习...
android ListView如果从网络加载图片,直接getView,肯定会阻塞UI导致滑动很不流畅,本实例优化了getView中图片的获取。
AndroidListView异步加载图片乱序问题,原因分析及解决方案.docx
①ListView异步加载图片的方式 ②给ImageView设置Tag,解决图片覆盖问题 ③采用LruCache缓存已经加载过的图片 ④当ListView滚动时不加载图片,滚动停止时才加载图片,从而达到ListView滑动很流畅的效果 ⑤当...
android中ListView异步加载图片时的图片错位问题解决方案
在项目里面使用ListView,并要求ListView的条目中有图片显示,而且这个图片是通过网络动态获取的。 这时候,会发现ListView加载很慢,半天才显示出来,影响了用户的体验。...应该另外开辟线程异步下载图片。