Android:列表视图滚动期间的 setImageBitmap 与 setImageURI

Android: setImageBitmap vs setImageURI during listview scrolling

我有一个包含未知数量自定义项目的列表视图,在滚动过程中 - 在 getView 中)我检查本地是否存在与 aConvertView 相关的图像并将其设置为列表视图项目 (aConvertView):

    // out of getView(...)

    Bitmap mBitmap;
    BitmapFactory.Options mOptions = new BitmapFactory.Options();
    mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;


    // Inside getView(...)

    File file = new File(mContext.getFilesDir(), picture_file_name);
    if(file.exists())
    {
        **// Which one has less impact on the UI thread?**

        mBitmap = BitmapFactory.decodeFile(file.getPath(), mOptions);
        aImageView.setImageBitmap(mBitmap);

        // OR

        aImageView.setImageURI(Uri.fromFile(file));
    }

当然我有滚动延迟的已知问题,我想知道在滚动期间设置项目的 ImageView 的最佳方法/方法。

setImageBitmap 对 UI 线程的影响是否小于 setImageURI? 或者两者都在 UI 上工作并导致延迟。

正在使用 new File ,File.exists() 对 UI 线程有影响吗?

不用说,我为我的列表视图项目使用了一个 holder,并为我的列表视图使用了以下参数:

        android:smoothScrollbar="true"
        android:scrollingCache="false"
        android:animationCache="false"

两个 运行 在 UI 线程上。我会说第一个可能比第二个快一点点。第二个实际上取决于 Uri 资源的来源(例如,uri 可能指向甚至未存储在 phone 上的远程文件)。

我认为这里的问题是您是否应该在 UI 线程上加载位图,答案是否定的,尤其是在列表视图中。 Google 在 developers.android.com 上发布了关于从 UI 线程加载位图的精彩教程:http://developer.android.com/training/displaying-bitmaps/process-bitmap.html

您有延迟问题,因为您在 UI 线程上执行文件 IO,主要是 decodeFile 这里的问题,您实际访问光盘上的文件数据(尽管没有理由从 UI 线程调用 File.exists())。

Quanturium 的答案是正确的

那里的示例解释了如何在后台加载位图并使用 WeakReferenceImageView 将位图指定为背景可绘制对象。请记住,所有文件 IO 工作都应该在 doInBackground 内,onPostExecute 实际上发生在 UI 线程上。

编辑:如何在 AsyncTask

中使用 File.Exists ()
// Decode image in background. 
    @Override 
    protected Bitmap doInBackground(Integer... params) {
        File file = new File(mContext.getFilesDir(), picture_file_name);
        if(file.exists())
        { 
           return BitmapFactory.decodeFile(file.getPath(), mOptions);
        }
        else
        {
           return null;
        }
   }

注意:我无法访问我的开发机器,所以我不确定这是否会构建。

回答关于从另一个 AsyncTask 启动一个 AsyncTask 的问题 - 我从来没有这样做过,我想这是可能的。就我而言,我将所有流程都放在一个 doInBackground:

  1. 首先下载数据(我猜您正在使用一些 REST 调用来执行此操作)。
  2. 从您的回复中获取内容
  3. 如果文件存在,覆盖
  4. 如果文件不存在,则创建

对我来说,最重要的是 运行 在后台线程中进行 REST 调用和文件 IO - 我不在乎它是一个任务还是两个任务。

我之前遇到过同样的问题。我试图在 ListView 中加载 6 个本地大图像(使用 Uri)。为了使您的应用 运行 顺利进行,您可能需要做两件事。

  1. 不要在主线程上加载图像。这是一个 android 文档,说明如何在单独的线程上加载图像:link.There are many libraries already implement this for you. If you are loading image from internet, server, etc. i would recommend Picasso

2.Scale 使图像更适合 ImageView 的图像大小。这是一个 android 文档,说明如何缩放图像大小。 link

毕竟这是我的 AsyncTask class 在新线程上加载图像并根据 imageview 缩放图像:

class BitmapWorkerTask extends AsyncTask<Uri, Void, Bitmap> {
    private final WeakReference<ImageView> imageViewReference;
    private Uri data = null;

    public BitmapWorkerTask(ImageView imageView) {
        // Use a WeakReference to ensure the ImageView can be garbage collected
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    // Decode image in background.
    @Override
    protected Bitmap doInBackground(Uri... params) {
        data = params[0];
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeFile(data.toString(),options);
        options.inSampleSize= calculateInSampleSize(options,300,300);
        options.inJustDecodeBounds = false;
        return  BitmapFactory.decodeFile(data.toString(),options);

    }

    // Once complete, see if ImageView is still around and set bitmap.
    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}
public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

300*300 是我的图像视图尺寸