自定义加载网络图片工具: 三级缓存
该类中主要是在 请求网络加载图片之前设置一个,可以理解为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" />