自定义加载网络图片工具: 三级缓存 解决图片显示错位的问题

自定义加载网络图片工具: 三级缓存

该类中主要是在 请求网络加载图片之前设置一个,可以理解为listview的条目请求网络的位置,然后在请求网络的方法中展示图片之前 设置一个 显示在屏幕上位置 判断二者是否相等,如果相等就直接显示图片

public class MyBitmapUtils {
    private final Context context;

    private LruCache<String, Bitmap> mLruCache; // key 代表 url地址, value 代表显示的图片

    private File rootFile; //本地缓存的根目录

    private ExecutorService mExecutorService; //线程池

    public MyBitmapUtils(Context context) {
        this.context = context;

        // maxMemory : mLruCache能管理的最大内存

        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / 8;
        mLruCache = new LruCache<String, Bitmap>(cacheSize) {
            /**
             * 表示当前存储的对象,
             * @param key
             * @param bitmap
             * @return
             */
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight(); //每一行的字节个数*高度 = bitmap的大小
            }
        };

        //本地缓存的路径 /data/data
        rootFile = context.getFilesDir();

        //创建线程池的实例化对象 创建固定大小的线程池 内部维护了4个线程
        mExecutorService = Executors.newFixedThreadPool(4);
    }

    /**
     * 展示网络图片
     *
     * @param image    展示的图片
     * @param imageUrl 图片的url地址
     */
    public void display(ImageView image, String imageUrl) {
        //1 .从内存中获取图片
        Bitmap cacheBitmap = mLruCache.get(imageUrl);
        if (cacheBitmap != null) {
            image.setImageBitmap(cacheBitmap);

            Log.d("MyBitmapUtils", "--从内存中获取");
            return; //表示能从内存获取到,就直接展示了.不再继续往下执行
        }
//2.从本地获取图片
        String imageName = MD5Encoder.encode(imageUrl);// url+Md5加密
        File cacheFile = new File(rootFile, imageName);

        if (cacheFile.exists() && cacheFile.length() > 0) {
            // 加载到内存中
            Bitmap decodeFileBitmap = BitmapFactory.decodeFile(cacheFile.getAbsolutePath());
            mLruCache.put(imageUrl, decodeFileBitmap);

            image.setImageBitmap(decodeFileBitmap);
            Log.d("MyBitmapUtils", "--从本地获取");
            return;

        }
        /**
         * 在请求网络之前 背上一个 标记 tag
         */
        int position = (int) image.getTag(); //可以理解为 请求的位置

        //3. 从网络中获取 需要开启子线程去加载图片
        mExecutorService.execute(new DownloadRunnable(image, imageUrl, position));
        Log.d("MyBitmapUtils", "--从网络获取");
    }
    private class DownloadRunnable implements Runnable {
        private final String imageUrl;
        private final ImageView image;
        private final int position;

        /**
         * @param image
         * @param imageUrl
         * @param position 为了解决 listview加载图片显示错乱的问题  该处的 position 可以理解为 请求时候的位置
         */
        public DownloadRunnable(ImageView image, String imageUrl, int position) {
            this.position = position;
            this.image = image;
            this.imageUrl = imageUrl;
        }

        @Override
        public void run() {
            Bitmap bitmap = null;
            HttpURLConnection urlConnection = null;
            try {
                URL url = new URL(imageUrl);
                urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setConnectTimeout(20 * 1000);
                urlConnection.setReadTimeout(10 * 1000);

                //获取响应码
                int responseCode = urlConnection.getResponseCode();
                if (responseCode == 200) {
                    InputStream is = urlConnection.getInputStream();
                    bitmap = BitmapFactory.decodeStream(is);

                    //保存到内存中
                    mLruCache.put(imageUrl, bitmap);

                    //保存到本地 (bitmap 如何转换成本地图片)

                    /**
                     * compress : 压缩图片
                     * format : 压缩的格式
                     * quality : 压缩质量  范围 0--100 0,不压缩; 100,完全压缩
                     * OutputStream stream  输出流 写到本地
                     */
                    File cacheFile = new File(rootFile, MD5Encoder.encode(imageUrl));
                    OutputStream out = new FileOutputStream(cacheFile);
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);

                    //展示图片
                    //获取上下文对象
                    MainActivity mainActivity = (MainActivity) context;
                    final Bitmap finalBitmap = bitmap;
                    mainActivity.runOnUiThread(new Runnable() { //使用 runOnUiThread 从子线程切换到主线程
                        @Override
                        public void run() {
                            int currentPos = (int) image.getTag();
                            //请求的位置 如果等于 显示在屏幕上的位置 就显示图片
                            if (position==currentPos){
                                image.setImageBitmap(finalBitmap);
                            }
                        }
                    });
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
            }
        }
    }
}

该类中是 在显示图片之前设置一个 tag viewHolder.image.setTag(position);

/**
 * 自定义 Adapter
 */
public class ImgAdapter extends ArrayAdapter<String> {

    private final MyBitmapUtils mBitmapUtils;

    public ImgAdapter(Context context, int resource, String[] objects) {
        super(context, resource, objects);
        mBitmapUtils = new MyBitmapUtils(context);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        String url = getItem(position); //根据当前的位置获取到图片的URL地址

        ViewHolder viewHolder = null;
        if (convertView == null) {
            viewHolder = new ViewHolder();
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_image, null);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        viewHolder.image = (ImageView) convertView.findViewById(R.id.image);

//为了解决图片显示错位乱跳的问题 我们这里处理的方法是 为 image 绑定一个 tag
        viewHolder.image.setTag(position);

        mBitmapUtils.display(viewHolder.image, url);

        return convertView;
    }

    public class ViewHolder {
        ImageView image;
    }
}

主界面

public class MainActivity extends AppCompatActivity {
    ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.list_view);

        ImgAdapter adapter = new ImgAdapter(this, 0, ImageId.imageUrl);
        listView.setAdapter(adapter);

    }
}

MD5 加密的工具类

package com.gaoo.listviewtest;

import java.security.MessageDigest;

public class MD5Encoder {

    public static String encode(String string)  {
        try {
            byte[] hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
            StringBuilder hex = new StringBuilder(hash.length * 2);
            for (byte b : hash) {
                if ((b & 0xFF) < 0x10) {
                    hex.append("0");
                }
                hex.append(Integer.toHexString(b & 0xFF));
            }
            return hex.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }
}

另外记得在清单文件中加上权限 <uses-permission android:name="android.permission.INTERNET" />

Listview显示错乱位置 我的理解

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值