Android 如何提高GridView渲染许多大型图像的性能(首次加载时间、滚动平滑度)?
“我的应用程序”允许用户使用其设备camera app拍照,并将其保存到特定于应用程序的文件夹中 然后,用户可以选择使用自己选择的设备电子邮件应用程序将自己选择的任何图像发送给其中一个联系人 遗憾的是,“选择要发送的图像”对话框最初需要很长时间才能加载,而且在滚动时非常“不稳定”(频繁地出现明显的停顿)。我怀疑这是因为图像非常大——用我的相机拍摄的照片每幅大约2.3MB。因此,初始屏幕(显示15幅图像)需要从磁盘上拉出约34.5MB,然后才能渲染 GridView layout.xmlAndroid 如何提高GridView渲染许多大型图像的性能(首次加载时间、滚动平滑度)?,android,performance,Android,Performance,“我的应用程序”允许用户使用其设备camera app拍照,并将其保存到特定于应用程序的文件夹中 然后,用户可以选择使用自己选择的设备电子邮件应用程序将自己选择的任何图像发送给其中一个联系人 遗憾的是,“选择要发送的图像”对话框最初需要很长时间才能加载,而且在滚动时非常“不稳定”(频繁地出现明显的停顿)。我怀疑这是因为图像非常大——用我的相机拍摄的照片每幅大约2.3MB。因此,初始屏幕(显示15幅图像)需要从磁盘上拉出约34.5MB,然后才能渲染 GridView layout.xml get
getBitmapFromFile()
仅根据中给出的答案进行了改编。我注意到那里的代码调用了两次decodeStream()
,可能会使我的I/O增加一倍,但我认为即使我将渲染所需的时间减半,对话框仍然会太慢、太急促
如果相关的话,我的测试手机是标准的三星Galaxy S II。我怀疑较旧的设备会更慢/更复杂
提高性能的最佳方法是什么?我猜是加载每个图像的异步任务(这样对话框可以在渲染所有图像之前加载)和某种缓存(这样滚动不需要重新渲染图像,也不会出现抖动)的组合,但我不知道如何将这些片段与我的其余拼图配合起来。您将需要使用它们。从SD卡加载整个图像总是很慢。只加载他们真正想要发送的完整版本
这并不是说缓存和异步加载是个坏主意。我会先将代码更改为使用缩略图,重新检查性能,然后执行异步路径。您可以在当前加载的缩略图上显示一个小微调器,以指示还有更多内容
以下是一个帮助您检索图像的方法。尝试将带有样本大小的图像加载到GridView中:
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 7;
Bitmap bitmap = BitmapFactory.decodeFile(f.getPath(), opts);
有关选项的更多信息
您还可以将图像加载交换到线程或异步任务中。我正在使用的
getBitmapFromFile()
函数已经使用inSampleSize
选项来缩放图像,不过还是要谢谢。然后将getBitmapFromFile放入异步任务中。谢谢您的回答,明天我在课桌旁的时候,我会和那班同学一起玩。即使我使用.nomedia文件从媒体扫描程序中隐藏我的应用程序图像,MediaStore.Images.Thumbnails类是否仍然有效?使用该应用程序拍摄的照片往往是特定于应用程序的,因此我不希望它们与所有用户和其他图像一起显示在图库中,因此我使用.nomedia来隐藏它们。origId参数(“与感兴趣的缩略图关联的原始图像id”)-我假设它来自媒体商店内容提供商?好的,所以我使用媒体商店查询从媒体商店获取图像id,看起来我无法获取图像id(即,查询不返回图像)如果已使用.nomedia文件从媒体扫描程序中阻止它们。默认情况下,它看起来像/sdcard/Android/data/(我的应用程序数据所在的位置)包含一个.nomedia文件,所以如果我想要自动生成缩略图,我需要在该层次结构之外创建一个文件夹。好吧,我得重写很多东西,但现在都能用了。谢谢你的回答。对不起,我有一段时间不在家了。对于希望媒体商店忽略您的图像但仍然生成缩略图的问题,您是否找到了解决方法?如果是这样的话,请编辑我的答案,包括你的发现:)不,我只是不得不让步,让我的应用程序图像显示在图库下(等等)。我将它们放在/sdcard/Pictures/下(使用getExternalStorageDirectory(Pictures)
)。
@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;
}
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 7;
Bitmap bitmap = BitmapFactory.decodeFile(f.getPath(), opts);