最近项目不是太忙 正好自己对图片缓存的接触很少 然后自己就按照自己的逻辑写了一个网络图片缓存的框架 没想到运行一下 居然实现了 当然中间肯定遇到很多问题 咱们言归正传
我现在有个需求使用ListView去浏览网络图片 我首先想到的是去下载图片 下载以后做缓存(如果不做缓存,,,呵呵 getView刷新起来还是非常可怕的 亲自测试过 ) 那么接下来 做缓存 又有人问了 做哪些缓存 我就根据我的实际情况 然后我就做了一个内存缓存和一个SD卡缓存 (个人感觉 这个二级缓存应该够用了 其他的没考虑) 因为只是一个Demo 我就简单的说下思路 上面就是我开始的逻辑 具体怎么实现的还是的自己动手去写代码 我加单的把思路写一下
获取Bitmap----->从内存缓存中查找图片(内存中取数据的速度最快)
|
|
|
内存有 -------------------内存没有
| |
直接返回Bitmap 去文件缓存(SD卡)查找
| |
显示 |
|
SD卡有 ---------------SD卡没有
| |
直接返回Bitmao显示 请求网络下载
|
|
下载成功(同时保存到内存和SD卡中)显示
整个的逻辑思路就是这样 其实做起来里面的小细节 还有很多要注意的
首先先看一下我的MainActivity
bitmapList
.add("http://pic.nipic.com/2007-10-06/2007106142456555_2.jpg");
bitmapList
.add("http://pic.nipic.com/2007-11-09/2007119122519868_2.jpg");
bitmapList
.add("http://pic14.nipic.com/20110522/7411759_164157418126_2.jpg");
bitmapList
.add("http://img2.3lian.com/img2007/19/33/005.jpg");
bitmapList
.add("http://pic2.ooopic.com/01/03/51/25b1OOOPIC19.jpg");
bitmapList
.add("http://down.tutu001.com/d/file/20101129/2f5ca0f1c9b6d02ea87df74fcc_560.jpg");
bitmapList
.add("http://img.taopic.com/uploads/allimg/130501/240451-13050106450911.jpg");
bitmapList
.add("http://pica.nipic.com/2008-03-19/2008319183523380_2.jpg");
bitmapList
.add("http://baike.soso.com/p/20090711/20090711101754-314944703.jpg");
bitmapList
.add("http://pic.nipic.com/2007-11-09/200711912230489_2.jpg");
bitmapList
.add("http://imgk.zol.com.cn/dcbbs/2342/a2341460.jpg");
bitmapList
.add("http://pic24.nipic.com/20121022/9252150_193011306000_2.jpg");
adapter = new MyAdapter(MainActivity.this, bitmapList);
mListView.setAdapter(adapter);
这个就不解释了 (listView三要素 数据源 适配器 listview)
接着我的代码看 适配器
public class MyAdapter extends BaseAdapter {
private Context mContext;
public static List<String> urlStrings;
private Viewholder viewholder;
private ImageManager iManager;
public MyAdapter(Context context, List<String> urlStrings) {
this.mContext = context;
this.urlStrings = urlStrings;
iManager = ImageManager.getImageManager(mContext, urlStrings);
}
@Override
public int getCount() {
return urlStrings.size();
}
@Override
public Object getItem(int position) {
return urlStrings.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item,
null);
viewholder = new Viewholder();
viewholder.imageView = (ImageView) convertView
.findViewById(R.id.imageView1);
convertView.setTag(viewholder);
} else {
viewholder = (Viewholder) convertView.getTag();
}
Log.i("bbb", position + "");
viewholder.imageView.setTag(urlStrings.get(position));
iManager.getBitmap(urlStrings.get(position), viewholder.imageView,
new CallBackImage() {
@Override
public void getBitmapFromImage(Bitmap bitmap, String url,
ImageView iamView) {
Log.i("uu", bitmap + "");
iamView.setImageBitmap(bitmap);
}
});
return convertView;
}
class Viewholder {
ImageView imageView;
}
ImageManager是我自己定义的一个类 待会给大家 现在大家需要知道的就是 我是在getview的时候去获取的图片 然后对ImageManager初始化了一下
咱们看一下获取图片的具体实现
// 得到图片
/* *
* 图片的唯一路径 Item对应的图片 文件缓存的路径 文件缓存的名字 回调
*/
@SuppressWarnings("static-access")
public void getBitmap(String urString, ImageView imageView,
CallBackImage caImage) {
cBack = caImage;
// 1.先去内存取图片
if (!TextUtils.isEmpty(urString)) {
mBitmap = mMemoryCach.getBitmapFromMemCache(urString);
if (caImage != null && mBitmap != null) {
caImage.getBitmapFromImage(mBitmap, urString, imageView);
}
if (mBitmap == null) {
// 判断是否正在下载或者是否存在正在下载的任务
downBitmap(imageView, urString);
}
}
}
// 网上下载图片
public void downBitmap(ImageView imageView, String urString) {
int index = listBitmapUrl.size();
dRunable = new DownBitmapRunable(imageView, urString, cBack);
cachedThreadPool.execute(dRunable);
}
我这里有一个回调接口 请求到图片以后回调回去 上面的注释很详细了 我要说的是 我去网络下载图片的时候用线程池实现的 因为我们listview加载的不是一张图片 用线程池便于控制(主要是不用我们手动的去控制 线程池为我们自动管理) 下载图片我用了下面这个类
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import com.example.bitmapcachdemo.ImageManager.CallBackImage;
import android.R.integer;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Message;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.util.Log;
import android.widget.ImageView;
/**
* 下载图片
*
* @author lvyanbo
*
*/
public class DownBitmapRunable implements Runnable {
public String bitmapUrl;
public byte[] bitmapByte;
private ImageView imageView;
// 从文件缓存中取图片
Bitmap bitmap = null;
private CallBackImage callBackImage;
Activity mActivity;
public DownBitmapRunable(ImageView imageView, String bitUrl,
CallBackImage ca) {
this.bitmapUrl = bitUrl;
this.imageView = imageView;
this.callBackImage = ca;
mActivity = (Activity) imageView.getContext();
}
@Override
public void run() {
try {
//从文件缓存中取图片
bitmap = FileCach.getBitmapFromFileCach(FileCach.SDPath,
getPhotoName(bitmapUrl));
if (bitmap != null) {
mActivity.runOnUiThread(new Runnable() {
public void run() {
callBackImage.getBitmapFromImage(bitmap, bitmapUrl,
imageView);
}
});
} else {
String tag = ImageManager.map.get(imageView);
Log.i("vvv", imageView.toString());
if (ImageManager.map.containsKey(imageView)) {
return;
} else {
ImageManager.map.put(imageView, bitmapUrl);
bitmapByte = getImage(bitmapUrl);
if (bitmapByte != null) {
bitmap = Bytes2Bimap(bitmapByte);
// 压缩成300*300的图片
bitmap = FileCach.decodeSampledBitmapFromNet(bitmap,
300, 300);
// 缓存到内存
ImageMemoryCach.addBitmapToMemoryCache(bitmapUrl,
bitmap);
// 缓存到File
FileCach.saveBitmap2SD(bitmap, FileCach.SDPath,
getPhotoName(bitmapUrl));
// 发送到主线程
ImageManager.map.remove(imageView);
mActivity.runOnUiThread(new Runnable() {
public void run() {
callBackImage.getBitmapFromImage(bitmap,
bitmapUrl, imageView);
bitmapByte = null;
bitmap = null;
}
});
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public byte[] getImage(String path) throws Exception {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5 * 1000);
conn.setRequestMethod("GET");
InputStream inStream = conn.getInputStream();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
return readStream(inStream);
}
return null;
}
public Bitmap Bytes2Bimap(byte[] b) {
if (b.length != 0) {
return BitmapFactory.decodeByteArray(b, 0, b.length);
} else {
return null;
}
}
public static byte[] readStream(InputStream inStream) throws Exception {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
outStream.close();
inStream.close();
return outStream.toByteArray();
}
// 截取字符串
public String getPhotoName(String urlString) {
String uString = null;
int index = urlString.lastIndexOf("/");
uString = urlString.substring(index + 1, urlString.length());
return uString;
}
}
我这里DownBitmapRunable 实现了Runable接口(我们都知道安卓4.0以后主线程是不能访问网络的 当然也是为了我们 用户体验)
我这里把请求SD卡的操作放到这里 大家想过为什么 吗 比如我下载了1张图片 我完全可以在主线程中去取 但是如果我缓存到sd卡中有1000张图片,如果我在主线程中取出这1000张图片。。。。。。是吧 你懂得 那样太可怕了 用户体验就不说了 你自己都不想用这个框架了 所以 我还是把取sd卡中的操作放到这了 大家可能会问 那内存取的呢 你为什么不放在这 我想这个问题我就不说了 (内存获取数据是最快的 所以我们的内存有多么宝贵 你应该懂的) 还有我这里给他压缩了一下 压缩成300*300的 如果一张图片太大 我们必须要压缩 不然肯定会OOM的 这样也能节约我们的存储空间 还有就是SD卡缓存我们肯定要给图片一个名字 我这里是用的图片的url截取了后面一部分 如果你用完整路径作为图片的名字 你想过没有 如果图片1000张 2000张 你存到SD卡中的完整路径就得占多少空间 (文字也得占空间啊) 文字虽然占的不大 但是禁不住你图片多啊 对吧 所以有些小细节我们还是要注意的
获取到图片以后 我们知道 只有我们请求内存的时候 是在主线程中 我们文件请求和网络请求都是在子线程中进行的 所以我们获取到图片以后一定要回调给主线程(子线程是不能更新UI的) 这样我们才能更新UI 关于这个具体的下载我就说这么多 这里也是核心 然后我们看一下ImageManager(里面有的方法没用到我写在这了 )
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.text.TextUtils;
import android.widget.ImageView;
/**
* 图片管理类
*
* @author lvyanbo
*
*/
@SuppressLint("HandlerLeak")
public class ImageManager {
private static ImageManager manager;
public ImageMemoryCach mMemoryCach;
private static Context mContext;
private FileCach mFileCach;
// 线程池
public ExecutorService cachedThreadPool;
public static CallBackImage cBack;
public List<String> listBitmapUrl;
// 请求大量图片
private DownBitmapRunable dRunable;
private Bitmap mBitmap;
// 返回一个线程安全的 实现了Map接口的集合
public static Map<ImageView, String> map = Collections
.synchronizedMap(new WeakHashMap<ImageView, String>());
public ImageManager(Context context, List<String> imageUrl) {
this.mContext = context;
listBitmapUrl = new ArrayList<String>();
// 初始化
mMemoryCach = ImageMemoryCach.getImageCach(mContext);
mFileCach = FileCach.getFileCach(mContext);
// 默认三条线程
cachedThreadPool = Executors.newFixedThreadPool(3);
this.listBitmapUrl = imageUrl;
}
// 单例获取
public static ImageManager getImageManager(Context context,
List<String> bitmapList) {
if (manager == null) {
manager = new ImageManager(context, bitmapList);
}
return manager;
}
// 得到图片
/* *
* 图片的唯一路径 Item对应的图片 文件缓存的路径 文件缓存的名字 回调
*/
@SuppressWarnings("static-access")
public void getBitmap(String urString, ImageView imageView,
CallBackImage caImage) {
cBack = caImage;
// 1.先去内存取图片
if (!TextUtils.isEmpty(urString)) {
mBitmap = mMemoryCach.getBitmapFromMemCache(urString);
if (caImage != null && mBitmap != null) {
caImage.getBitmapFromImage(mBitmap, urString, imageView);
}
if (mBitmap == null) {
// 判断是否正在下载或者是否存在正在下载的任务
downBitmap(imageView, urString);
}
}
}
// 网上下载图片
public void downBitmap(ImageView imageView, String urString) {
int index = listBitmapUrl.size();
dRunable = new DownBitmapRunable(imageView, urString, cBack);
cachedThreadPool.execute(dRunable);
}
/**
* 缓存到内存
*
* @param key
* @param bitmap
*/
@SuppressWarnings("static-access")
public void saveBitmap2Memory(String key, Bitmap bitmap) {
mMemoryCach.addBitmapToMemoryCache(key, bitmap);
}
/**
* 缓存到文件
*
* @param bitmap
* @param path
* @param bitmapName
* @param w
* @param h
*/
@SuppressWarnings("static-access")
public void saveBitmap2File(Bitmap bitmap, String path, String bitmapName) {
mFileCach.saveBitmap2SD(bitmap, path, bitmapName);
}
@SuppressWarnings("static-access")
public void saveBitmap2FileByByte(byte[] bitmap, String path,
String bitmapName) {
mFileCach.saveBitmap2SDByte(bitmap, path, bitmapName);
}
public static Bitmap Bytes2Bimap(byte[] b) {
if (b.length != 0) {
return BitmapFactory.decodeByteArray(b, 0, b.length);
} else {
return null;
}
}
public void shutDownExecutorService() {
}
public interface CallBackImage {
public void getBitmapFromImage(Bitmap bitmap, String key,
ImageView imageView);
}
}
首先说一下那个map 我们想一个问题 我们这里用线程池控制任务(DownBitmapRunable),如果某个任务正在请求 比如 ur;是http://a.png 那么我们就不能再去请求这张图片了 因为已经有任务去请求他了 所以这时候就需要我们手动去控制 同时我们还要确保listview的图片显示没有错位 Item的显示必须保持一致性 唯一性 这时候我们就想了 我们要用某一个唯一的东西去进行控制 就像用户ID一样 每一个Item都要有一个ID 不然我怎么知道请求的图片去加载到哪一个Item去显示 这里我用的item对应的imageview 当然用图片的路径也行都是唯一的 但是为了方便我还是用的的imageview 因为我可以把这个url对应的imageview给回调回来 这样永远不会错位 我这里的map之所以这么写 是为了线程安全
我们看代码的最后一行是我自定义的一个接口 用来回调下载的图片 以及图片的url 和item对应的ImageView 这个就没什么好说的了
下面我把我的内存缓存和文件缓存写一下
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
/**
* 图片缓存 (缓存到内存)
*
* @author lvyanbo
*
*/
public class ImageMemoryCach {
public static ImageMemoryCach mImageCach;
public Context mcontext;
public static LruCache<String, Bitmap> mLruCache;
// 最大可用内存的八分之一做内存缓存
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
public ImageMemoryCach(Context context) {
this.mcontext = context;
mLruCache = new LruCache<String, Bitmap>(cacheSize) {
@SuppressLint("NewApi")
protected int sizeOf(String key, Bitmap bitmap) {
// 重写此方法来衡量每张图片的大小,默认返回图片数量。
return bitmap.getByteCount() / 1024;
};
};
}
public static ImageMemoryCach getImageCach(Context context) {
if (mImageCach == null) {
mImageCach = new ImageMemoryCach(context);
return mImageCach;
}
return mImageCach;
}
// 将图片缓存到内存
public static void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mLruCache.put(key, bitmap);
}
}
// 从内存中获取图片
public static Bitmap getBitmapFromMemCache(String key) {
if (key != null) {
// 如果内存中取到了图片直接返回
if (mLruCache.get(key) != null) {
return mLruCache.get(key);
}
}
return null;
}
// 清除缓存(指定的图片)
public static void clearImageFromMemoryCache(String key) {
if (key != null) {
mLruCache.remove(key);
}
}
// 清除缓存
public static void clearMemoryCach() {
mLruCache.evictAll();
}
}
我这里是用的LruCache大家都应该知道这个我就不多解释了
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Xml.Encoding;
/**
* 文件缓存
*
* @author lvyanbo
*
*/
public class FileCach {
// 文件缓存的路径
public static String SDPath = SDCardUtils.getSDCardPath()
+ "MyFileCachBitmap/";
public static File file;
public static FileCach mCach;
public static Context mContext;
public static byte[] bitmapByte;
public FileCach(Context context) {
this.mContext = context;
}
// 单例
public static FileCach getFileCach(Context context) {
if (mCach == null) {
mCach = new FileCach(context);
return mCach;
}
return mCach;
}
// 从文件缓存中读取图片
@SuppressWarnings("resource")
public static Bitmap getBitmapFromFileCach(String filePath, String fileName) {
Bitmap fileBitmap = null;
file = new File(filePath, fileName);
if (file.exists()) {
try {
@SuppressWarnings("unused")
BufferedInputStream inputStream = new BufferedInputStream(
new FileInputStream(file));
fileBitmap = BitmapFactory.decodeFile(filePath+fileName);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return fileBitmap;
}
// 文件缓存图片
public static void saveBitmap2SD(Bitmap bitmap, String filePath,
String fileName) {
if (SDCardUtils.isSDCardEnable()) {
file = new File(filePath);
if (!file.exists()) {
file.mkdir();
}
File files = new File(filePath, fileName);
if (!files.exists()) {
// 将下载下来的图片进行压缩然后转化为byte
bitmapByte = Bitmap2Byte(bitmap);
try {
@SuppressWarnings({ "unused", "resource" })
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(files));
bos.write(bitmapByte);
bos.flush();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} else {
AllToast.showShort(mContext, "SD卡不可用");
}
}
// 文件缓存图片(根据字节缓存)
public static void saveBitmap2SDByte(byte[] bs, String filePath,
String fileName) {
if (SDCardUtils.isSDCardEnable()) {
file = new File(filePath, fileName);
if (!file.exists()) {
try {
@SuppressWarnings({ "unused", "resource" })
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(file));
bos.write(bs);
bos.flush();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
} else {
file.delete();
}
} else {
AllToast.showShort(mContext, "SD卡不可用");
}
}
// 将bitmap转化为byte(转化的时候要压缩)
public static byte[] Bitmap2Byte(Bitmap bitmap) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
}
/**
* 根据计算的inSampleSize,得到压缩后图片 (网上的图片下载下来以后是Bitmap)
*
* @param pathName
* @param reqWidth
* @param reqHeight
* @return
*/
@SuppressWarnings("unused")
public static Bitmap decodeSampledBitmapFromNet(Bitmap bitmap,
int reqWidth, int reqHeight) {
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(Bitmap2Byte(bitmap), 0,
Bitmap2Byte(bitmap).length, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
Bitmap b = BitmapFactory.decodeByteArray(Bitmap2Byte(bitmap), 0,
Bitmap2Byte(bitmap).length, options);
return b;
}
/**
* 根据计算的inSampleSize,得到压缩后图片 (本地的图片)
*
* @param pathName
* @param reqWidth
* @param reqHeight
* @return
*/
@SuppressWarnings("unused")
public Bitmap decodeSampledBitmapFromResource(String pathName,
int reqWidth, int reqHeight) {
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(pathName, options);
return bitmap;
}
/**
* 计算inSampleSize,用于压缩图片
*
* @param options
* @param reqWidth
* @param reqHeight
* @return
*/
private static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// 源图片的宽度
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1;
if (width > reqWidth && height > reqHeight) {
// 计算出实际宽度和目标宽度的比率
int widthRatio = Math.round((float) width / (float) reqWidth);
int heightRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = Math.max(widthRatio, heightRatio);
}
return inSampleSize;
}
/**
* 清除所有的缓存图片
*/
public static void clearFileAllCach() {
file = new File(SDPath.replace("/", ""));
if (file.exists()) {
file.delete();
} else {
AllToast.showShort(mContext, "文件缓存已清除");
}
}
// 清除指定的图片文件
public static void clearFileBitmapCach(String filePath, String fileName) {
file = new File(filePath, fileName);
if (file.exists()) {
file.delete();
} else {
AllToast.showShort(mContext, "图片已移除");
}
}
}
文件缓存我就不多说了 都是一些简单的文件操作
我们再看一下这个这个适配器
public class MyAdapter extends BaseAdapter {
private Context mContext;
public static List<String> urlStrings;
private Viewholder viewholder;
private ImageManager iManager;
public MyAdapter(Context context, List<String> urlStrings) {
this.mContext = context;
this.urlStrings = urlStrings;
iManager = ImageManager.getImageManager(mContext, urlStrings);
}
@Override
public int getCount() {
return urlStrings.size();
}
@Override
public Object getItem(int position) {
return urlStrings.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item,
null);
viewholder = new Viewholder();
viewholder.imageView = (ImageView) convertView
.findViewById(R.id.imageView1);
convertView.setTag(viewholder);
} else {
viewholder = (Viewholder) convertView.getTag();
}
Log.i("bbb", position + "");
viewholder.imageView.setTag(urlStrings.get(position));
iManager.getBitmap(urlStrings.get(position), viewholder.imageView,
new CallBackImage() {
@Override
public void getBitmapFromImage(Bitmap bitmap, String url,
ImageView iamView) {
Log.i("uu", bitmap + "");
iamView.setImageBitmap(bitmap);
}
});
return convertView;
}
class Viewholder {
ImageView imageView;
}
getView方法中的iamgeview和bitmap 我们可以直接用 这个不会错位的因为我返回的都是Item对应的Imageview 它和Bitmap是一一对应的
最后我想说得是 闲着没事可以多写写代码 按照自己的思路去写写 能收货很多东西 可能你看着能明白 但是你自己真正写起来的时候你会发现 你收获的远不止这些东西 我把这个Demo放到百度云盘 了 给他家一个私密吧 链接:http://pan.baidu.com/s/1qWqxQbU 密码:ry5m
大家自己有兴趣的可以看一下