为什么在 ListView 中显示时异步调整大小的图像最初太大?
Why are images that are resized asynchronously initially too large when displayed in a ListView?
我正在从 parse.com 下载 Parse 数据字段并异步调整每个字段中包含的图像的大小。当视图膨胀时,第一页图像不会调整大小。回收视图时,它们会正确显示。
我首先尝试调整GetDataCallback
的done
部分的图片,看到了大图。接下来,我自己实现了线程,结果相同:图像的第一屏最初显示不正确,但当它们离开屏幕并返回时修复。
作为测试,我接下来尝试直接获取数据(不是 getDataInBackground
)并调整大小,效果很好,但显然处理时间增加了。由于此测试,我认为该问题与线程有关。
一些代码:
@Override
public void done(List<Listing> listingList, ParseException e) {
/* scale and save the image back to parse object */
final float scale = getResources().getDisplayMetrics().density;
if (e == null) {
Log.d("listing", "Retrieved " + listingList.size() + " listings");
/* clear adapter before populating */
adapter.clear();
/* iterate through listings and create listing objects */
for (Listing listingObject : listingList) {
//listingObject.resizeImage(scale);
new ResizeImages().execute(listingObject);
listings.add(listingObject);
}
}
else {
Log.d("listing", "Error: " + e.getMessage());
}
}
});
异步调整大小:
private class ResizeImages extends AsyncTask<Listing, Void, Listing> {
protected Listing doInBackground(Listing... params) {
Listing listing = params[0];
final float scale = getResources().getDisplayMetrics().density;
try {
byte[] data = listing.getFile().getData();
/* Decode the byte array into BitMap to be displayed on device */
Bitmap bitmap = BitmapFactory
.decodeByteArray(
data, 0,
data.length);
/* scale image and put back into parse object */
if (scale <= 1.0) {
bitmap = Bitmap.createScaledBitmap(bitmap, 134, 134, false);
} else if (scale <= 1.5) {
bitmap = Bitmap.createScaledBitmap(bitmap, 200, 200, false);
}
...
/* put image back into byte data and put back in "this" */
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] scaledData = stream.toByteArray();
bitmap.recycle();
Log.d("Resizer", "Image resized.");
ParseFile photoFile = new ParseFile("image.jpeg", scaledData);
listing.setFile(photoFile);
} catch (ParseException e) {
e.printStackTrace();
}
return listing;
}
protected void onPostExecute(Listing listing) {
Log.d("Resizer: ", "Resizing done");
}
}
编辑
我的修复涉及将“onPostExecute”更改为:
protected void onPostExecute(Listing listing) {
Log.d("Resizer: ", "Resizing done");
listings.add(listing); // this line from UI thread moved here
adapter.notifyDataSetChanged(); // this line was new
}
我在下面的回答中提供了一种更通用的异步调整位图大小的方法。
原来问题是适配器没有收到数据改变的通知。所以当ListView拉取前几张图片的时候,都是原来大小的。为了使它更通用并希望在将来对某人有所帮助,以下是异步调整位图大小的方法:
// in your UI thread
new ResizeImages().execute(mBitmap);
// in your class
private class ResizeImages extends AsyncTask<Bitmap, Void, Listing> {
protected Listing doInBackground(Bitmap... params) {
Bitmap bitmap = params[0]; // grab the first bitmap.. could grab more and
// do multiple per thread
try {
/* scale image however you wish. here we scale to 200x200 px */
bitmap = Bitmap.createScaledBitmap(bitmap, 200, 200, false);
/* put image back into byte data if you need to */
//ByteArrayOutputStream stream = new ByteArrayOutputStream();
//bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
//byte[] scaledData = stream.toByteArray();
/* put data back into bitmap if you need to */
//Bitmap scaledBitmap = BitmapFactory
// .decodeByteArray(
// scaledData, 0,
// scaledData.length);
/* recycle unneeded bitmap */
bitmap.recycle();
Log.d("Resizer", "Image resized.");
} catch (ParseException e) {
e.printStackTrace();
}
protected void onPostExecute(Bitmap bitmap) {
Log.d("Resizer: ", "Resizing done");
resizedBitmap = bitmap; // save resized bitmap into your class
// variable
bitmaps.add(bitmap); // here bitmaps is an list of bitmaps,
// like List<Listing> listingList
/* notify adapter that the data has changed */
adapter.notifyDataSetChanged();
}
}
我正在从 parse.com 下载 Parse 数据字段并异步调整每个字段中包含的图像的大小。当视图膨胀时,第一页图像不会调整大小。回收视图时,它们会正确显示。
我首先尝试调整GetDataCallback
的done
部分的图片,看到了大图。接下来,我自己实现了线程,结果相同:图像的第一屏最初显示不正确,但当它们离开屏幕并返回时修复。
作为测试,我接下来尝试直接获取数据(不是 getDataInBackground
)并调整大小,效果很好,但显然处理时间增加了。由于此测试,我认为该问题与线程有关。
一些代码:
@Override
public void done(List<Listing> listingList, ParseException e) {
/* scale and save the image back to parse object */
final float scale = getResources().getDisplayMetrics().density;
if (e == null) {
Log.d("listing", "Retrieved " + listingList.size() + " listings");
/* clear adapter before populating */
adapter.clear();
/* iterate through listings and create listing objects */
for (Listing listingObject : listingList) {
//listingObject.resizeImage(scale);
new ResizeImages().execute(listingObject);
listings.add(listingObject);
}
}
else {
Log.d("listing", "Error: " + e.getMessage());
}
}
});
异步调整大小:
private class ResizeImages extends AsyncTask<Listing, Void, Listing> {
protected Listing doInBackground(Listing... params) {
Listing listing = params[0];
final float scale = getResources().getDisplayMetrics().density;
try {
byte[] data = listing.getFile().getData();
/* Decode the byte array into BitMap to be displayed on device */
Bitmap bitmap = BitmapFactory
.decodeByteArray(
data, 0,
data.length);
/* scale image and put back into parse object */
if (scale <= 1.0) {
bitmap = Bitmap.createScaledBitmap(bitmap, 134, 134, false);
} else if (scale <= 1.5) {
bitmap = Bitmap.createScaledBitmap(bitmap, 200, 200, false);
}
...
/* put image back into byte data and put back in "this" */
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] scaledData = stream.toByteArray();
bitmap.recycle();
Log.d("Resizer", "Image resized.");
ParseFile photoFile = new ParseFile("image.jpeg", scaledData);
listing.setFile(photoFile);
} catch (ParseException e) {
e.printStackTrace();
}
return listing;
}
protected void onPostExecute(Listing listing) {
Log.d("Resizer: ", "Resizing done");
}
}
编辑
我的修复涉及将“onPostExecute”更改为:
protected void onPostExecute(Listing listing) {
Log.d("Resizer: ", "Resizing done");
listings.add(listing); // this line from UI thread moved here
adapter.notifyDataSetChanged(); // this line was new
}
我在下面的回答中提供了一种更通用的异步调整位图大小的方法。
原来问题是适配器没有收到数据改变的通知。所以当ListView拉取前几张图片的时候,都是原来大小的。为了使它更通用并希望在将来对某人有所帮助,以下是异步调整位图大小的方法:
// in your UI thread
new ResizeImages().execute(mBitmap);
// in your class
private class ResizeImages extends AsyncTask<Bitmap, Void, Listing> {
protected Listing doInBackground(Bitmap... params) {
Bitmap bitmap = params[0]; // grab the first bitmap.. could grab more and
// do multiple per thread
try {
/* scale image however you wish. here we scale to 200x200 px */
bitmap = Bitmap.createScaledBitmap(bitmap, 200, 200, false);
/* put image back into byte data if you need to */
//ByteArrayOutputStream stream = new ByteArrayOutputStream();
//bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
//byte[] scaledData = stream.toByteArray();
/* put data back into bitmap if you need to */
//Bitmap scaledBitmap = BitmapFactory
// .decodeByteArray(
// scaledData, 0,
// scaledData.length);
/* recycle unneeded bitmap */
bitmap.recycle();
Log.d("Resizer", "Image resized.");
} catch (ParseException e) {
e.printStackTrace();
}
protected void onPostExecute(Bitmap bitmap) {
Log.d("Resizer: ", "Resizing done");
resizedBitmap = bitmap; // save resized bitmap into your class
// variable
bitmaps.add(bitmap); // here bitmaps is an list of bitmaps,
// like List<Listing> listingList
/* notify adapter that the data has changed */
adapter.notifyDataSetChanged();
}
}