使用AsyncTask错误将位图缩略图加载到RecyclerView

时间:2016-03-03 17:00:42

标签: android bitmap android-asynctask

我正在创建一个应用程序,其文件目录包含图片,视频,pdf等。我目前正在处理显示图片的缩略图。我正在使用RecyclerView和ViewHolder来显示每个代表照片项目的列表项。然后我使用AsyncTask一次下载一个Bitmaps并将它们存储在Hashmap中。一切正常,除非我很快向下滚动一大堆照片。列表底部随机项的占位符图像将替换为已在列表顶部加载的缩略图。当背景线程到达底部的图像时,正确的图像将替换错误的图像。加载完所有缩略图后,一切都按预期工作。

这是AsyncTask的代码。我认为问题与我传递给构造函数的位置整数有关。 position变量表示适配器中的位置。也许有办法确保图像正在加载onPreExecute()中的占位符图像?

/**
 * AsyncTask to download the thumbnails in the RecyclerView list.
 */

private class CreateThumbnail extends AsyncTask<Void, Void, android.graphics.Bitmap> {

    // ******
    // FIELDS
    // ******

    private ImageView mPreviewInstance;
    private File mFile;
    private RelativeLayout.LayoutParams lp;
    private FileHolder mFileHolder;
    private int mPosition;
    private UUID mId;
    private FolderFile mFolderFile;

    // ***********
    // Constructor
    // ***********

    /**
     * @param holder - ViewHolder passed for the list item.
     * @param position - position in the Adapter.
     * @param id - id for list item stored in database.
     */

    private CreateThumbnail(FileHolder holder, int position, UUID id) {
        mPosition = position;
        mFileHolder = holder;
        mPreviewInstance = mFileHolder.mImagePreview;
        mId = id;
        mFolderFile = FolderFileLab.get(getContext()).getFolderFile(mId);
    }

    // ****************
    // OVERRIDE METHODS
    // ****************

    @Override
    protected void onPreExecute() {

    }

    @Override
    protected Bitmap doInBackground(Void... params) {

        FolderFileLab lab = FolderFileLab.get(getContext());

        if (!lab.getCurrentMap().containsKey(mId)) {
            mFile = lab.getPhotoFile(mFolderFile);

            // Create Bitmap (Biggest use of memory and reason this background thread exists)
            Bitmap bitmap = PictureUtils.getScaledBitmap(
                    mFile.getPath(), getActivity());

            // Scales Bitmap down for thumbnail.
            Bitmap scaledBitmap;
            if (bitmap.getWidth() >= bitmap.getHeight()) {
                scaledBitmap = Bitmap.createBitmap(bitmap, bitmap.getWidth() / 2
                                - bitmap.getHeight() / 2,
                        0, bitmap.getHeight(), bitmap.getHeight());
            } else {
                scaledBitmap = Bitmap.createBitmap(bitmap, 0, bitmap.getHeight() / 2
                                - bitmap.getWidth() / 2,
                        bitmap.getWidth(), bitmap.getWidth());
            }

            // Cache bitmap
            HashMap<UUID, Bitmap> map = lab.getCurrentMap();
            map.put(mId, scaledBitmap);
            lab.updateMap(map);

            return scaledBitmap;
        } else {
            // If Hashmap already contains the id get the Bitmap.
            return lab.getCurrentMap().get(mId);
        }
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {

        // Checks to see if the bitmap is still displayed in the list. If not nothing happens.
        // If it is then it displays the image.
        if (mPreviewInstance.getVisibility() == View.VISIBLE && mFileHolder.getPosition()
                == mPosition && bitmap != null) {

            // Formatting for thumbnail
            lp = new RelativeLayout.LayoutParams(
                    RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout
                    .LayoutParams.WRAP_CONTENT);
            lp.setMargins(7, 7, 7, 0);

            // Displaying thumbnail on UI thread.
            mPreviewInstance.setLayoutParams(lp);
            mPreviewInstance.setBackground(null);
            mPreviewInstance.setImageBitmap(bitmap);
        }
    }

}

以下是启动AsyncTask的一些相关适配器代码。

@Override
    public void onBindViewHolder(FileHolder holder, int position) {
        FolderFile file = mFiles.get(position);
        holder.bindFile(file);

        if (file.isPhoto()) {
            createThumbnail = new CreateThumbnail(holder, position,file.getId());
            createThumbnail.execute();
        }
    }

2 个答案:

答案 0 :(得分:1)

想出来! 我添加了代码,以便在每次绑定后将照片更改为占位符图像。这是我在适配器中更改的内容。

 @Override
    public void onBindViewHolder(FileHolder holder, int position) {
        FolderFile file = mFiles.get(position);
        holder.bindFile(file);

        if (file.isPhoto()) {
            Drawable placeholder = getResources().getDrawable(R.mipmap.picture_blu);
            holder.mImagePreview.setBackground(placeholder);
            holder.mImagePreview.setImageBitmap(null);


            createThumbnail = new CreateThumbnail(holder, position, file.getId());
            createThumbnail.execute();
        }
    }

答案 1 :(得分:0)

您的视图已被回收,因此在异步任务完成时,imageView已被重复使用,并为其分配了新图像。

您可以做的是为imageView分配一个标记,该标记是您尝试加载到其中的文件的文件名。您可以在异步任务中跟踪相同的文件名。然后在您的AsyncTask中,在onPostExecute中,检查imageView具有的标记是否与您刚刚加载的文件名相同。如果是,则继续将位图设置为imageView。如果不是,则视图已被回收,您只需删除刚刚创建的位图;另一个异步任务将加载正确的位图。