- 论坛徽章:
- 0
|
又优化了一下 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
部分代码如下:
Java代码- 1.@Override
- 2.public View getView(int position, View convertView, ViewGroup parent) {
- 3. if(convertView == null){
- 4. convertView = mInflater.inflate(R.layout.book_item_adapter, null);
- 5. }
- 6. BookModel model = mModels.get(position);
- 7. convertView.setTag(position);
- 8. ImageView iv = (ImageView) convertView.findViewById(R.id.sItemIcon);
- 9. TextView sItemTitle = (TextView) convertView.findViewById(R.id.sItemTitle);
- 10. TextView sItemInfo = (TextView) convertView.findViewById(R.id.sItemInfo);
- 11. sItemTitle.setText(model.book_name);
- 12. sItemInfo.setText(model.out_book_url);
- 13. iv.setBackgroundResource(R.drawable.rc_item_bg);
- 14. syncImageLoader.loadImage(position,model.out_book_pic,imageLoadListener);
- 15. return convertView;
- 16.}
- 17.
- 18.SyncImageLoader.OnImageLoadListener imageLoadListener = new SyncImageLoader.OnImageLoadListener(){
- 19.
- 20. @Override
- 21. public void onImageLoad(Integer t, Drawable drawable) {
- 22. //BookModel model = (BookModel) getItem(t);
- 23. View view = mListView.findViewWithTag(t);
- 24. if(view != null){
- 25. ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);
- 26. iv.setBackgroundDrawable(drawable);
- 27. }
- 28. }
- 29. @Override
- 30. public void onError(Integer t) {
- 31. BookModel model = (BookModel) getItem(t);
- 32. View view = mListView.findViewWithTag(model);
- 33. if(view != null){
- 34. ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);
- 35. iv.setBackgroundResource(R.drawable.rc_item_bg);
- 36. }
- 37. }
- 38.
- 39.};
- 40.
- 41.public void loadImage(){
- 42. int start = mListView.getFirstVisiblePosition();
- 43. int end =mListView.getLastVisiblePosition();
- 44. if(end >= getCount()){
- 45. end = getCount() -1;
- 46. }
- 47. syncImageLoader.setLoadLimit(start, end);
- 48. syncImageLoader.unlock();
- 49.}
- 50.
- 51.AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {
- 52.
- 53. @Override
- 54. public void onScrollStateChanged(AbsListView view, int scrollState) {
- 55. switch (scrollState) {
- 56. case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
- 57. DebugUtil.debug("SCROLL_STATE_FLING");
- 58. syncImageLoader.lock();
- 59. break;
- 60. case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
- 61. DebugUtil.debug("SCROLL_STATE_IDLE");
- 62. loadImage();
- 63. //loadImage();
- 64. break;
- 65. case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
- 66. syncImageLoader.lock();
- 67. break;
- 68.
- 69. default:
- 70. break;
- 71. }
- 72.
- 73. }
- 74.
- 75. @Override
- 76. public void onScroll(AbsListView view, int firstVisibleItem,
- 77. int visibleItemCount, int totalItemCount) {
- 78. // TODO Auto-generated method stub
- 79.
- 80. }
- 81.};
复制代码 Syncimageloader代码- 1.import java.io.DataInputStream;
- 2.import java.io.File;
- 3.import java.io.FileInputStream;
- 4.import java.io.FileOutputStream;
- 5.import java.io.IOException;
- 6.import java.io.InputStream;
- 7.import java.lang.ref.SoftReference;
- 8.import java.net.URL;
- 9.import java.util.HashMap;
- 10.
- 11.import android.graphics.drawable.Drawable;
- 12.import android.os.Environment;
- 13.import android.os.Handler;
- 14.
- 15.public class SyncImageLoader {
- 16.
- 17. private Object lock = new Object();
- 18.
- 19. private boolean mAllowLoad = true;
- 20.
- 21. private boolean firstLoad = true;
- 22.
- 23. private int mStartLoadLimit = 0;
- 24.
- 25. private int mStopLoadLimit = 0;
- 26.
- 27. final Handler handler = new Handler();
- 28.
- 29. private HashMap<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
- 30.
- 31. public interface OnImageLoadListener {
- 32. public void onImageLoad(Integer t, Drawable drawable);
- 33. public void onError(Integer t);
- 34. }
- 35.
- 36. public void setLoadLimit(int startLoadLimit,int stopLoadLimit){
- 37. if(startLoadLimit > stopLoadLimit){
- 38. return;
- 39. }
- 40. mStartLoadLimit = startLoadLimit;
- 41. mStopLoadLimit = stopLoadLimit;
- 42. }
- 43.
- 44. public void restore(){
- 45. mAllowLoad = true;
- 46. firstLoad = true;
- 47. }
- 48.
- 49. public void lock(){
- 50. mAllowLoad = false;
- 51. firstLoad = false;
- 52. }
- 53.
- 54. public void unlock(){
- 55. mAllowLoad = true;
- 56. synchronized (lock) {
- 57. lock.notifyAll();
- 58. }
- 59. }
- 60.
- 61. public void loadImage(Integer t, String imageUrl,
- 62. OnImageLoadListener listener) {
- 63. final OnImageLoadListener mListener = listener;
- 64. final String mImageUrl = imageUrl;
- 65. final Integer mt = t;
- 66.
- 67. new Thread(new Runnable() {
- 68.
- 69. @Override
- 70. public void run() {
- 71. if(!mAllowLoad){
- 72. DebugUtil.debug("prepare to load");
- 73. synchronized (lock) {
- 74. try {
- 75. lock.wait();
- 76. } catch (InterruptedException e) {
- 77. // TODO Auto-generated catch block
- 78. e.printStackTrace();
- 79. }
- 80. }
- 81. }
- 82.
- 83. if(mAllowLoad && firstLoad){
- 84. loadImage(mImageUrl, mt, mListener);
- 85. }
- 86.
- 87. if(mAllowLoad && mt <= mStopLoadLimit && mt >= mStartLoadLimit){
- 88. loadImage(mImageUrl, mt, mListener);
- 89. }
- 90. }
- 91.
- 92. }).start();
- 93. }
- 94.
- 95. private void loadImage(final String mImageUrl,final Integer mt,final OnImageLoadListener mListener){
- 96.
- 97. if (imageCache.containsKey(mImageUrl)) {
- 98. SoftReference<Drawable> softReference = imageCache.get(mImageUrl);
- 99. final Drawable d = softReference.get();
- 100. if (d != null) {
- 101. handler.post(new Runnable() {
- 102. @Override
- 103. public void run() {
- 104. if(mAllowLoad){
- 105. mListener.onImageLoad(mt, d);
- 106. }
- 107. }
- 108. });
- 109. return;
- 110. }
- 111. }
- 112. try {
- 113. final Drawable d = loadImageFromUrl(mImageUrl);
- 114. if(d != null){
- 115. imageCache.put(mImageUrl, new SoftReference<Drawable>(d));
- 116. }
- 117. handler.post(new Runnable() {
- 118. @Override
- 119. public void run() {
- 120. if(mAllowLoad){
- 121. mListener.onImageLoad(mt, d);
- 122. }
- 123. }
- 124. });
- 125. } catch (IOException e) {
- 126. handler.post(new Runnable() {
- 127. @Override
- 128. public void run() {
- 129. mListener.onError(mt);
- 130. }
- 131. });
- 132. e.printStackTrace();
- 133. }
- 134. }
- 135.
- 136. public static Drawable loadImageFromUrl(String url) throws IOException {
- 137. DebugUtil.debug(url);
- 138. if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
- 139. File f = new File(Environment.getExternalStorageDirectory()+"/TestSyncListView/"+MD5.getMD5(url));
- 140. if(f.exists()){
- 141. FileInputStream fis = new FileInputStream(f);
- 142. Drawable d = Drawable.createFromStream(fis, "src");
- 143. return d;
- 144. }
- 145. URL m = new URL(url);
- 146. InputStream i = (InputStream) m.getContent();
- 147. DataInputStream in = new DataInputStream(i);
- 148. FileOutputStream out = new FileOutputStream(f);
- 149. byte[] buffer = new byte[1024];
- 150. int byteread=0;
- 151. while ((byteread = in.read(buffer)) != -1) {
- 152. out.write(buffer, 0, byteread);
- 153. }
- 154. in.close();
- 155. out.close();
- 156. Drawable d = Drawable.createFromStream(i, "src");
- 157. return loadImageFromUrl(url);
- 158. }else{
- 159. URL m = new URL(url);
- 160. InputStream i = (InputStream) m.getContent();
- 161. Drawable d = Drawable.createFromStream(i, "src");
- 162. return d;
- 163. }
- 164.
- 165. }
- 166.}
-
复制代码 为了让大家更好的理解,我添加了源代码例子,还特地美化了一下UI
除了本身已有的弱引用缓存图片,我还添加了本地SD卡缓存图片(这两种缓存方法各有好处,如果图片经常变化建议内存缓存图片,如果是不经常修改的图片建议SD卡缓存)
用真机测试了一下,感觉无比流畅(测试机是U8500非常垃圾的一种低端机)
欢迎大家拍砖讨论 |
|