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

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

这是自定义适配器时重用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;
    }



}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值