您的位置:首页 > 理论基础 > 计算机网络

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

2016-07-15 16:40 766 查看

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

该类中主要是在 请求网络加载图片之前设置一个,可以理解为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 图片