粗略搜了一下,网站较少android listview的错位解决方法,有些解决方法通过修改缓存来实现,比较复杂,而且看得不是很明白。参考了小米自带相册的实现,通过用默认纯色空白图片代替处理中的图片,经过几天的努力,终于研究出多线程解决android ListView图片错位的问题。
主要的解决思路如下:
1.图片信息通过ContentResolver获取,由于图片不符合规格,需要自己写一个decodeBitmap()方法进行压缩
2.每次调用Adapter中的getView()方法才检测该图片是否已经被压缩,如果没有压缩则开启压缩线程进行压缩,然后将图片保存到本地,并将压缩图片的信息保存到本地的Sqlite
3.如果调用getView方法时,图片已经压缩,则通过BitmapFactory.decodeFile(pathName)的方法调用本地压缩后的图片
4.图片错位问题,用默认的空白图片对view进行初始化
布局非常简单:MainActivity.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<GridView
android:numColumns="3"
android:id="@+id/gridView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</RelativeLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imgView"
android:layout_width="120dp"
android:layout_height="120dp"
android:scaleType="centerCrop" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
public static final int DECODE_OK = 0x000011;
private static final int defNum = 14800000;
GridView gridView;
// 这个SparseArray是android的集合类型,本来想测试从内存中load图片会不会比本地快,但后来没测
SparseArray<String> imgInfos;
static MyAdapter adapter;
DBHelper helper;
SQLiteDatabase db;
ContentResolver resolver;
int count;
String thumbDir;
@Override
protected void onStart() {
super.onStart();
helper = new DBHelper(this);
// 测试时,为了观察效果,每次打开程序都清空本地压缩图
File delFile = new File(this.getFilesDir() + "/thumbnails");
if (delFile.isDirectory()) {
File[] files = delFile.listFiles();
for (File file : files) {
file.delete();
}
}
initDate();
// Context context = this.getApplicationContext();
// Log.v("fileDir",""+context.getFilesDir());
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gridView = (GridView) findViewById(R.id.gridView);
adapter = new MyAdapter();
gridView.setAdapter(adapter);
}
@Override
protected void onStop() {
super.onStop();
db.close();
}
// 初始化数据
private void initDate() {
thumbDir = this.getFilesDir() + "/" + "thumbnails" + "/";
db = helper.getWritableDatabase();
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
imgInfos = new SparseArray<>();
resolver = this.getContentResolver();
String[] counts = new String[]{"count(*)"};
Cursor cursor = resolver.query(uri, counts, null, null, null);
if (cursor != null && cursor.moveToNext()) {
count = cursor.getInt(0);
cursor.close();
}
// 测试时,为了观察效果,每次打开程序都清空压缩图信息
db.execSQL("DELETE FROM thumbnails");
Log.v("count", "found " + count + " record");
}
private class MyAdapter extends BaseAdapter {
ViewHolder holder;
final Drawable defDrawable = MainActivity.this.getResources().getDrawable(R.drawable.default_drawable);
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] queryColumns =queryColumns = new String[]{
MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DATE_MODIFIED,
};
ContentResolver resolver = MainActivity.this.getContentResolver();
Cursor cursor = resolver.query(uri,queryColumns,null,null,"date_modified DESC");
@Override
public int getCount() {
return count;
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(final int i, View view, ViewGroup viewGroup) {
final MyHandler handler;
holder = new ViewHolder();
if (view == null) {
LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
view = inflater.inflate(R.layout.item, viewGroup, false);
holder.imageView = (ImageView) view.findViewById(R.id.imgView);
holder.imageView.setImageDrawable(defDrawable);
view.setTag(holder);
} else {
// holder.imageView = (ImageView) view.findViewById(R.id.imgView);
holder = (ViewHolder) view.getTag();
holder.imageView.setImageDrawable(defDrawable);
}
handler = new MyHandler(holder);
if (cursor != null) {
cursor.moveToPosition(i);
String displayName = String.valueOf(defNum + cursor.getInt(0)) + ".jpg";
// 将cursor信息保存到ContentValues中去,方便下一步查询,不用每次都执行query方法
final ContentValues values = new ContentValues();
values.put("parent_id", cursor.getInt(0));
values.put("parent_data", cursor.getString(1));
values.put("_data",thumbDir+displayName);
values.put("displayName", displayName);
values.put("parent_date_modified", cursor.getInt(2));
// Log.v("position", "position:" + i);
Cursor tCursor = db.rawQuery("SELECT _data,parent_date_modified,parent_id FROM thumbnails " +
"WHERE parent_id = ?", new String[]{values.getAsString("parent_id")});
if (!tCursor.moveToNext() || tCursor.getInt(1) != values.getAsInteger("parent_date_modified")) {
// Log.v("situation", "one");
new Thread(new Runnable() {
@Override
public void run() {
zipAndStore(values,handler);
}
}).start();
tCursor.close();
} else {
// Log.v("situation", "two");
Bitmap bitmap = BitmapFactory.decodeFile(tCursor.getString(0));
holder.imageView.setImageBitmap(bitmap);
// Message msg = Message.obtain();
// msg.what = DECODE_OK;
// msg.obj = bitmap;
// handler.sendMessage(msg);
tCursor.close();
}
} else {
Log.v("cursor", "null");
}
return view;
}
}
// 储存本地图片的方法
private void storeThumbnail(Bitmap bitmap, String filePath, String display_name) {
File file = new File(filePath);
// 删除重名的本地文件
if (!file.exists()) {
if (file.mkdir()) {
Log.v("dir_make", "dir maked");
}
}
file = new File(filePath, display_name);
if (file.exists()) {
if (file.delete()) {
Log.v("file_delete", "found same file " + display_name + " and deleted");
}
}
try {
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 压缩图片的方法,由于在小米note测试的时候遇到各种图片,所以算法多了一点
private Bitmap decodeBitmap(String path) {
Log.v("decode_path",path);
Bitmap bitmap;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
int realWidth = options.outWidth;
int realHeight = options.outHeight;
Log.v("width&height", options.outWidth + "|" + options.outHeight);
int scale = (realWidth > realHeight ? realHeight : realWidth) / 100;
if (scale <= 1) {
scale = 1;
}
options.inSampleSize = scale;
options.inJustDecodeBounds = false;
options.outMimeType = "mime/jpeg";
bitmap = BitmapFactory.decodeFile(path, options);
// 裁切出中间方形的bitmap
int rectWidth;
int rectHeight;
int x;
int y;
if (options.outWidth < 100) {
rectWidth = options.outWidth;
x = 0;
} else {
rectWidth = 100;
x = (options.outWidth - 100) / 2;
}
if (options.outHeight < 100) {
rectHeight = options.outHeight;
y = 0;
} else {
rectHeight = 100;
y = (options.outHeight - 100) / 2;
}
bitmap = Bitmap.createBitmap(bitmap, x, y, rectWidth, rectHeight, null, false);
// Log.v("decoding", path + " done");
return bitmap;
}
private static class ViewHolder {
ImageView imageView;
}
// 这个类本来打算用来测试线程池,不用管
public class MyDecodeTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
Log.v("init_state", "init done");
}
}
private static class MyHandler extends Handler {
ViewHolder holder;
public MyHandler(ViewHolder holder) {
this.holder = holder;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DECODE_OK:
holder.imageView.setImageBitmap((Bitmap) msg.obj);
break;
}
}
}
// 压缩图片,保存到本地,将压缩图片信息保存到本地数据库
public void zipAndStore(ContentValues values,Handler handler){
Log.v("situation", "one");
Bitmap bitmap = decodeBitmap(values.getAsString("parent_data"));
Message msg = Message.obtain();
msg.what = DECODE_OK;
msg.obj = bitmap;
handler.sendMessage(msg);
storeThumbnail(bitmap, thumbDir, values.getAsString("displayName"));
values.put("width", bitmap.getWidth());
values.put("height", bitmap.getHeight());
values.remove("parent_data");
// 更新本地数据库记录
synchronized(this){
db.delete("thumbnails", "parent_id=?", new String[]{values.getAsString("parent_id")});
db.insert("thumbnails", null, values);
}
}
}
由于篇幅太大,就不把数据库类和model类都放上来,那些都比较简单而且不用管。小弟菜鸟,如果有更好的解决方法,希望大神们指点一下!