zoukankan      html  css  js  c++  java
  • 又优化了一下 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
            
        }
    };
    package cindy.android.test.synclistview;
    Syncimageloader代码
    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卡缓存)

    很多同学说在这里一直new Thread可能会造成资源浪费的问题,针对这个问题我后来又做了优化,请看这篇文章:

    又优化了一下Android ListView 异步加载图片(续)

     
  • 相关阅读:
    学习总结
    HBase简要介绍
    分布式文件系统HDFS
    Hadoop基础安装和使用
    大数据的学习基础
    学习总结
    Python-爬取豆瓣电影信息并存到mysql
    python爬虫回顾与总结
    正则表达式-爬虫获取豆瓣电影超链接
    BeautifulSoup相关学习
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/5377010.html
Copyright © 2011-2022 走看看