【问题标题】:How can I improve the performance (first load time, scroll smoothness) of a GridView rendering many large images?如何提高渲染许多大图像的 GridView 的性能(首次加载时间、滚动平滑度)?
【发布时间】:2012-02-24 21:28:00
【问题描述】:

我的应用程序允许用户使用他们设备的相机应用程序拍照,这些照片保存到应用程序特定的文件夹中。

然后,用户可以选择使用他们选择的设备电子邮件应用程序将他们选择的任何图像发送给他们的联系人之一。

遗憾的是,“选择要发送的图像”对话框最初需要很长时间才能加载,并且在滚动时非常“不稳定”(经常出现明显的停顿)。我怀疑这是因为图像非常大——用我的相机拍摄的照片每张大约 2.3MB。因此,初始屏幕(显示 15 张图像)需要从磁盘上拉出大约 34.5MB 才能渲染。

GridView 布局.xml

<!-- ... -->
<GridView
android:id="@+id/gvSelectImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:numColumns="3"
android:gravity="center" />
<!-- ... -->

适配器 getView()

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    File pic = pics[position];
    View checkedImageView = 
      LayoutInflater.from(context).inflate(R.layout.checked_image, 
                                           parent, false);
    final ImageView imageView = 
      (ImageView) checkedImageView.findViewById(R.id.checkableImage);

    /* LOAD IMAGE TO DISPLAY */
    //imageView.setImageURI(pic.toURI())); // "exceeds VM budget"
    Bitmap image;
    try {
        int imageWidth = 100;
        image = getBitmapFromFile(pic, imageWidth); // TODO: slooow...
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    imageView.setImageBitmap(image);

    /* SET CHECKBOX STATE */
    final CheckBox checkBoxView = 
      (CheckBox) checkedImageView.findViewById(R.id.checkBox);
    checkBoxView.setChecked(isChecked[position]);

    /* ATTACH HANDLERS */
    checkBoxView.setOnCheckedChangeListener(new OnCheckedChangeListener() {
        public void onCheckedChanged(CompoundButton buttonView, 
                                     boolean isChecked) {
            CheckableLocalImageAdapter.this.isChecked[position] = 
              checkBoxView.isChecked();
        }
    });
    imageView.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            checkBoxView.setChecked(!checkBoxView.isChecked());
        }
    });

    return checkedImageView;
}

getBitmapFromFile() 刚刚改编自https://stackoverflow.com/a/823966/633626 给出的答案。我注意到那里的代码调用了两次decodeStream(),可能会使我的 I/O 翻倍,但我认为即使我将渲染时间减半,对话框仍然会太慢而且太生涩。

如果相关的话,我的测试手机是标准的三星 Galaxy S II。我怀疑旧设备会更慢/更不稳定。

在这里提高性能的最佳方法是什么?我猜测 AsyncTask 的组合来加载每个图像(这样对话框可以在所有图像被渲染之前加载)和某种缓存(这样滚动不需要重新渲染图像并且不是生涩),但我我不知道如何将这些碎片与我的拼图的其余部分结合起来。

【问题讨论】:

    标签: android performance


    【解决方案1】:

    您将希望改用thumbnails。从 SD 卡加载整个图像总是很慢。只加载他们真正想要发送的完整版本。

    这并不是说缓存和异步加载是一个坏主意。我会先更改代码以使用缩略图,重新检查性能,然后再走异步路线。您可以在当前加载的缩略图上显示一个小微调器,以表明还有更多内容。

    这里有一个built-in class 可以帮助您找回它们。

    【讨论】:

    • 感谢您的回答,明天我会在我的办公桌旁玩那个班。即使我使用 .nomedia 文件从媒体扫描仪隐藏我的应用程序图像,MediaStore.Images.Thumbnails 类是否仍然有效?使用该应用程序拍摄的照片往往是特定于应用程序的,因此我不希望它们与所有用户的其他图像一起出现在图库中,因此我使用了 .nomedia 来隐藏它们。 origId 参数(“与感兴趣的缩略图相关联的原始图像 id”) - 我假设它来自媒体商店内容提供商?
    • 好的,所以我正在使用媒体存储查询从媒体存储中获取图像 ID,看起来我无法获取图像 ID(即查询未返回图像)如果它们已被 .nomedia 文件从媒体扫描仪中阻止。看起来 /sdcard/Android/data/ (我的应用程序数据所在的位置)默认包含一个 .nomedia 文件,所以如果我想要自动缩略图,我需要在该层次结构之外创建一个文件夹。急。
    • 好吧,不得不重写很多东西,但现在一切正常。感谢您的回答。
    • 抱歉,我离开 SO 有一段时间了。对于希望媒体商店忽略您的图像但仍生成缩略图的问题,您是否找到了解决方法?如果是这样,请编辑我的答案以包括您的发现:)
    • 不,我只需要让步,让我的应用程序图像显示在图库(等)下。我把它们放在 /sdcard/Pictures/(using getExternalStorageDirectory(PICTURES)) 下。
    【解决方案2】:

    尝试将带有 SampleSize 的图像加载到 GridView 中:

    BitmapFactory.Options opts = new BitmapFactory.Options();
    opts.inSampleSize = 7;
    Bitmap bitmap = BitmapFactory.decodeFile(f.getPath(), opts);
    

    有关选项的更多信息here

    您还可以将图像加载交换到线程或异步任务中。

    【讨论】:

    • 我正在使用的getBitmapFromFile() 函数已经使用inSampleSize 选项来缩放图像,但无论如何感谢。
    • 然后将 getBitmapFromFile 放入 AsyncTask 中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-11
    相关资源
    最近更新 更多