为什么在 ListView 中显示时异步调整大小的图像最初太大?

Why are images that are resized asynchronously initially too large when displayed in a ListView?

我正在从 parse.com 下载 Parse 数据字段并异步调整每个字段中包含的图像的大小。当视图膨胀时,第一页图像不会调整大小。回收视图时,它们会正确显示。

我首先尝试调整GetDataCallbackdone部分的图片,看到了大图。接下来,我自己实现了线程,结果相同:图像的第一屏最初显示不正确,但当它们离开屏幕并返回时修复。

作为测试,我接下来尝试直接获取数据(不是 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();
    }
}