将许多大型位图图像加载为缩略图 - Android

4
我正在开发一个媒体播放器应用程序,并希望加载专辑封面图像以在ListView中显示。目前,使用我从last.fm自动下载的图像是可以正常工作的,这些图像都是小于500x500像素的png格式。然而,最近我添加了另一个面板到我的应用程序中,允许查看全屏艺术品,因此我已经用大型(1024x1024)的png替换了一些艺术品。现在,当我滚动浏览几个具有高分辨率艺术品的专辑时,我的BitmapFactory会出现java.lang.OutOfMemoryError错误。
    static public Bitmap getAlbumArtFromCache(String artist, String album, Context c)
    {
    Bitmap artwork = null;
    File dirfile = new File(SourceListOperations.getAlbumArtPath(c));
    dirfile.mkdirs();
    String artfilepath = SourceListOperations.getAlbumArtPath(c) + File.separator + SourceListOperations.makeFilename(artist) + "_" + SourceListOperations.makeFilename(album) + ".png";
    File infile = new File(artfilepath);
    try
    {
        artwork = BitmapFactory.decodeFile(infile.getAbsolutePath());
    }catch(Exception e){}
    if(artwork == null)
    {
        try
        {
            artwork = BitmapFactory.decodeResource(c.getResources(), R.drawable.icon);
        }catch(Exception ex){}
    }
    return artwork;
    }

有没有什么方法可以限制生成的 Bitmap 对象的大小,比如说,256x256呢?缩略图只需要这么大,我可以创建一个重复函数或者传递一个参数来获取全尺寸的艺术作品以在全屏中显示。

另外,我将这些位图显示在小的 ImageView 上,大约是150x150到200x200。较小的图像比大的图像更容易缩小。有没有办法应用下采样滤镜以平滑图像(例如抗锯齿)?如果不必要,我不想缓存大量额外的缩略图文件,因为这会使管理艺术作品图像变得更加困难(目前您只需将新图像放入目录中,它们就会在下次加载时自动使用)。

完整代码在http://github.org/CalcProgrammer1/CalcTunes,在 src/com/calcprogrammer1/calctunes/AlbumArtManager.java 中,尽管在其他函数中也没什么不同(如果图像丢失则回退到最后.fm检查)。


使用懒加载列表或通用图像加载器。此外,参考 http://developer.android.com/training/improving-layouts/smooth-scrolling.html。懒加载列表和UIL会加载缩小版本的位图。了解更多信息请访问:https://dev59.com/6WUo5IYBdhLWcg3w-zei - Raghunandan
4个回答

2
我使用这个私有函数来设置我想要的缩略图大小:
//decodes image and scales it to reduce memory consumption
public static Bitmap getScaledBitmap(String path, int newSize) {
    File image = new File(path);

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    options.inInputShareable = true;
    options.inPurgeable = true;

    BitmapFactory.decodeFile(image.getPath(), options);
    if ((options.outWidth == -1) || (options.outHeight == -1))
        return null;

    int originalSize = (options.outHeight > options.outWidth) ? options.outHeight
            : options.outWidth;

    BitmapFactory.Options opts = new BitmapFactory.Options();
    opts.inSampleSize = originalSize / newSize;

    Bitmap scaledBitmap = BitmapFactory.decodeFile(image.getPath(), opts);

    return scaledBitmap;     
}

我找到了一个与这个解决方案非常相似的解决方案,你的看起来更紧凑,所以我可能会转换到它。谢谢! - CalcProgrammer1

0
    public static Bitmap decodeSampledBitmapFromFile(String path, int reqWidth, int reqHeight)
{
    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(path, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(path, options);
}

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) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }
return inSampleSize;
}

改编自http://developer.android.com/training/displaying-bitmaps/load-bitmap.html,但使用文件加载而不是资源。我添加了缩略图选项,如下:

    //Checks cache for album art, if it is not found return default icon
static public Bitmap getAlbumArtFromCache(String artist, String album, Context c, boolean thumb)
{
    Bitmap artwork = null;
    File dirfile = new File(SourceListOperations.getAlbumArtPath(c));
    dirfile.mkdirs();
    String artfilepath = SourceListOperations.getAlbumArtPath(c) + File.separator + SourceListOperations.makeFilename(artist) + "_" + SourceListOperations.makeFilename(album) + ".png";
    File infile = new File(artfilepath);
    try
    {
        if(thumb)
        {
            artwork = decodeSampledBitmapFromFile(infile.getAbsolutePath(), 256, 256);
        }
        else
        {
            artwork = BitmapFactory.decodeFile(infile.getAbsolutePath());
        }

Yoann的答案看起来相似一些,而且更加简洁,可能会使用那个解决方案,但是那个页面上有一些关于BitmapFactory的好信息。


0

实现这个的一种方法是使用AQuery库

这是一个库,它允许您从本地存储或URL中延迟加载图像。支持缓存和降低比例等功能。

以下是一个示例,演示如何延迟加载资源而不进行比例缩小:

AQuery aq = new AQuery(mContext);
aq.id(yourImageView).image(R.drawable.myimage);

使用缩小比例的File对象来懒加载图像的示例:

    InputStream ins = getResources().openRawResource(R.drawable.myImage);
    BufferedReader br = new BufferedReader(new InputStreamReader(ins));
    StringBuffer sb;
    String line;
    while((line = br.readLine()) != null){
        sb.append(line);
        }

    File f = new File(sb.toString());

    AQuery aq = new AQuery(mContext);
    aq.id(yourImageView).image(f,350); //Where 350 is the width to downscale to

示例如何使用本地内存缓存、本地存储缓存和调整大小从 URL 下载。

AQuery aq = new AQuery(mContext);
aq.id(yourImageView).image(myImageUrl, true, true, 250, 0, null);

这将启动一个异步下载 myImageUrl 图像,将其调整为 250 宽度并缓存在内存和存储中。然后它将在您的 yourImageView 中显示图像。每当 myImageUrl 的图像已经被下载并缓存在内存或存储中时,此行代码将加载其中一个缓存在内存或存储中的图像。

通常,这些方法将在列表适配器的 getView 方法中调用。

有关 AQuery 图像加载功能的完整文档,您可以查看 文档


0

这可以很容易地通过droidQuery完成:

final ImageView image = (ImageView) findViewById(R.id.myImage);
$.ajax(new AjaxOptions(url).type("GET")
                           .dataType("image")
                           .imageHeight(256)//set the output height
                           .imageWidth(256)//set the output width
                           .context(this)
                           .success(new Function() {
                               @Override
                               public void invoke($ droidQuery, Object... params) {
                                   $.with(image).val((Bitmap) params[0]);
                               }
                           })
                           .error(new Function() {
                               @Override
                               public void invoke($ droidQuery, Object... params) {
                                   droidQuery.toast("could not set image", Toast.LENGTH_SHORT);
                               }
                           }));

您还可以使用cachecacheTimeout方法缓存响应。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接