重用convertView时防止图片不断刷新

来源:互联网 发布:hugo这家淘宝店正吗 编辑:程序博客网 时间:2024/04/16 21:50

在写适配器时从网络加载图片会遇到当停止滑动滑动列表框之后,列表框中的图片会不断的切换。

这是自定义适配器时重用convertView要注意的地方,这里的解决办法通常是在getView(…)方法中重用convertView时给ImageView设置一个标签setTag(…),然后在异步任务执行完之后,即在获取到图片数据之后,给ImageView设置图片数据之前判断一下此时的tag还是不是最后设置的tag了。是则显示。


下面的类在用调用loadImage(…)之前要先进行初始化init(…)。

public class ImageLoader {    //线程池中线程的数量,为了保证最好的并发性    //这个数量与设备CPU的盒数一样    private static int threadCount;    //线程池    private static ExecutorService exec;    //上下文对象    private static Context context;    //当线程池中的工作线程获得图像后    //需要将图像通过uiHandler提交到主线程    //进而在ImageView中进行展示    private static Handler uiHandler;    //当生产者向任务队列中添加了需要执行的任务后    //生产者会向该poolHandler发送一个Message    //通知它区域任务队列中取任务放到线程池中执行    private static Handler pollHandler;    //与pollHandler相依相偎的一个工作线程    //pollHandler把收到的Message都提交到该线程    //该线程的looper从MessagQueue中吧消息取出再返回给pollHandler执行    private static Thread pollThread;    //任务队列    //生产者将任务放到该队列中    //消费者从该队列中取任务执行    private static LinkedBlockingDeque<Runnable> tasks;    //为下载图片提供内存缓存    //其中键为图片的url地址转的MD5字符串,值为图片本身    private static LruCache<String, Bitmap> memCache;    //如果所有相关属性都未做初始化,则isFirst为true    //一旦做了初始化,isFirst的值就为false    private static boolean isFirst = true;    //信号量,用来控制线程池可取任务量    private static Semaphore pollLock;    //当主线程加载加载图像时,向pollhandler发送消息    //为了    private static Semaphore pollHandlerLock = new Semaphore(0);    //磁盘缓存对象    private static DiskLruCache diskCache;    /**     * ImageLoaderd 初始化方法     * 把上述所有属性都要进行赋值     * @param c     */    public static void init(Context c){        if(!isFirst){            return;        }        isFirst = false;        context = c;        tasks = new LinkedBlockingDeque<Runnable>();        threadCount = getCoreNumbers();        //创建与线程池中任务数量相同        pollLock = new Semaphore(threadCount);        //创建线程池        exec = Executors.newFixedThreadPool(threadCount);        pollThread = new Thread(){            @Override            public void run() {                Looper.prepare();                pollHandler = new Handler(){                    @Override                    public void handleMessage(Message msg) {                        //一旦该pollHandler收到消息就意味着任务队列中有了任务,就去取任务放到线程池中执行                        try {                            Runnable task = tasks.getLast();                            exec.execute(task);                            pollLock.acquire();                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                    }                };                //释放许可                pollHandlerLock.release();                Looper.loop();            }        };        Log.i("TAg", "pollThread = " + pollThread);        pollThread.start();        //构建主线程的handler        uiHandler = new Handler(Looper.getMainLooper()){            @Override            public void handleMessage(Message msg) {                //将获得的图片放到ImageView中显示                //需要解决一个“反复”显示的问题                //TODO                TaskBean bean = (TaskBean) msg.obj;                if(bean.tag.equals((String)bean.iv.getTag())){                    bean.iv.setImageBitmap(bean.bitMap);                }            }        };        //初始化内存缓存        memCache = new LruCache<String, Bitmap>((int) (Runtime.getRuntime().maxMemory()/8)){            @Override            protected int sizeOf(String key, Bitmap value) {                return value.getHeight()*value.getRowBytes();            }        };        //初始化磁盘缓存        try {            diskCache = DiskLruCache.open(getCacheDir(), 1, 1, 1024*1024*8);        } catch (IOException e) {            e.printStackTrace();        }    }    private static File getCacheDir() {        String dir = context.getCacheDir().getPath();        String name = "imageloadcache";        return new File(dir,name);    }    /**     * 加载指定位置的图形到imageview中显示     * @param iv     * @param url     */    public static void loadImage(final ImageView iv,final String url){        Bitmap result = null;        final String tag = getMD5(url);        result = memCache.get(tag);        iv.setTag(tag);        if(result!=null){            Log.d("TAG", "图片从内存缓存中加载");            iv.setImageBitmap(result);            return;        }        try {            Snapshot snap = diskCache.get(tag);            if(snap!=null){                //说明磁盘缓存中y有                Log.d("TAG", "图片从磁盘缓存中取");                InputStream in = snap.getInputStream(0);                result = BitmapFactory.decodeStream(in);                memCache.put(tag, result);                iv.setImageBitmap(result);                in.close();                return;            }        } catch (IOException e1) {            e1.printStackTrace();        }        //如果缓存中么有,添加到任务队列中,去做下载        tasks.add(new Runnable() {            @Override            public void run() {                //去指定url指定下载图片                try {                    URL u = new URL(url);                    HttpURLConnection connect = (HttpURLConnection) u.openConnection();                    connect.setRequestMethod("GET");                    connect.setDoInput(true);                    connect.connect();                    InputStream in = connect.getInputStream();                    Bitmap bitmap = compress(iv,in);                    in.close();                    //将下载的图片放到缓存中缓存                    memCache.put(tag, bitmap);                    //将下载的图片放到磁盘缓存中进行缓存                    Editor editor = diskCache.edit(tag);                    OutputStream out = editor.newOutputStream(0);                    bitmap.compress(CompressFormat.JPEG, 100, out);                    //提交                    editor.commit();                    //写diskLruCache的日志文件                    diskCache.flush();                    //用一个bean ,两个属性,一个属性引用Bitmap,一个引用imageView                    TaskBean bean = new TaskBean();                    bean.bitMap = bitmap;                    bean.iv = iv;                    bean.tag = tag;                    Message.obtain(uiHandler, 101, bean).sendToTarget();                    //释放一个许可,允许线程池继续去取任务                    pollLock.release();                } catch (Exception e) {                }            }        });        //通知pollHandler去取任务        if(pollHandler==null){            //等待            //获取一个“许可”            try {                pollHandlerLock.acquire();            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }        Message.obtain(pollHandler).sendToTarget();    }    /**     * 根据ImageView的大小对图片进行压缩并显示     * @param iv     * @param in     * @return     */    protected static Bitmap compress(ImageView iv, InputStream in) {        //先尝试获得ImageView的大小        int width = iv.getWidth();//有可能得到0,当iv的宽高设定为warpcontent时        int height = iv.getHeight();//同上        if(width==0 || height==0){            //怎么办            //折中方式1)用固定尺寸100dp?150dp?            //     2)用设备屏幕的宽/高            //第一种方式,是使用TypedValue类,用法参考友录            //第二种方法            width = context.getResources().getDisplayMetrics().widthPixels;            height = context.getResources().getDisplayMetrics().heightPixels;        }        Bitmap bitMap = null;        try {            //获得图像实际的宽/高            ByteArrayOutputStream out = new ByteArrayOutputStream();            int len = -1;            while((len = in.read())!=-1){                out.write(len);            }            byte[] bytes = out.toByteArray();            Options opts = new Options();            opts.inJustDecodeBounds = true;            out.close();            bitMap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts);            int bitMapWidth = opts.outWidth;            int bitMapHeight = opts.outHeight;            //压缩的比例就取决于图片的宽高与前面计算的比值            int sampleSize = 1;            //如果图形的宽或图形高大于我希望的宽或者高就进行压缩            if(bitMapWidth*1.0/width>1 || bitMapHeight*1.0/height>1){                sampleSize = (int) Math.ceil(Math.max(bitMapHeight*1.0/height, bitMapWidth*1.0/width));            }            opts.inSampleSize = sampleSize;            opts.inJustDecodeBounds = false;            bitMap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts);        } catch (Exception e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        return bitMap;    }    /**     * 把一个普通的字符串转成MD5格式的字符串     * @param url     * @return     */    private static String getMD5(String str) {        StringBuffer sb = new StringBuffer();        try{            //获得摘要对象            MessageDigest md = MessageDigest.getInstance("MD5");            //转换str-->md5            md.update(str.getBytes());            byte[] bytes = md.digest();            for (byte b : bytes) {                //把每一个byte数据做一下格式化                String temp = Integer.toHexString(b & 0xFF);                if(temp.length()==1){                    sb.append("0");                }                sb.append(temp);            }        }catch(Exception e){            e.printStackTrace();        }        return sb.toString();    }    /*     *安卓系统有一个路径/sys/devices/system     *该路径下有N多个文件来描述系统的资源     *其中与CPU相关的描述文件都在     * /sys/devices/system/cpu路径下面      * 如果设备CPU是一个核,他的描述文件就是     * /sys/devices/system/cpu/cpu0/xxxx     * 有多少cpux文件夹就有多少个cpu个数     * @return     */    private static int getCoreNumbers() {        File[] files;        try {            File file = new File("/sys/devices/system");            FilenameFilter filter = new FilenameFilter() {                @Override                public boolean accept(File dir, String filename) {                    if(filename.contains("cpu"))                        return true;                    return false;                }            };            files = file.listFiles(filter);        } catch (Exception e) {            e.printStackTrace();            return 1;        }        return files.length;    }    private static class TaskBean{        Bitmap bitMap;        ImageView iv;        String tag;    }     /**     * 如果返回true,意味着初始化未完成     *      *      * @return     */    public static boolean isFirst(){        return isFirst;    }}
0 0