【问题标题】:Load many large bitmap images as thumbnails - Android将许多大型位图图像加载为缩略图 - Android
【发布时间】:2013-04-29 05:01:45
【问题描述】:

我正在开发一个媒体播放器应用程序,并希望加载专辑封面图像以显示在 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;
    }

我可以添加什么来限制生成的位图对象的大小,比如 256x256?缩略图需要更大,我可以创建一个重复的函数或一个参数来获取全尺寸的艺术品以全屏显示。

另外,我将这些位图显示在很小的 ImageView 上,大约 150x150 到 200x200。较小的图像比大的图像更容易缩小。有没有办法应用缩小过滤器来平滑图像(也许是抗锯齿)?如果不需要的话,我不想缓存一堆额外的缩略图文件,因为这会使管理艺术品图像变得更加困难(目前您可以将新的文件转储到目录中,下次将自动使用它们它们被加载)。

完整代码位于 src/com/calcprogrammer1/calctunes/AlbumArtManager.java 中的 http://github.org/CalcProgrammer1/CalcTunes,尽管其他函数没有太大不同(如果图像丢失,则返回检查 last.fm)。

【问题讨论】:

标签: android image bitmap out-of-memory bitmapfactory


【解决方案1】:

我使用这个私有函数来设置我想要的缩略图大小:

//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;     
}

【讨论】:

  • 我找到了一个与这个非常相似的解决方案,你的看起来更紧凑,所以我可能会切换到它。谢谢!
【解决方案2】:
    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 的好信息。

【讨论】:

    【解决方案3】:

    一种方法是AQuery library

    这是一个允许您从本地存储或 url 延迟加载图像的库。支持缓存和缩减等功能。

    在不缩减规模的情况下延迟加载资源的示例:

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

    使用缩小比例延迟加载文件对象中的图像的示例:

        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 图像加载功能的完整文档,您可以查看the documentation

    【讨论】:

      【解决方案4】:

      这很容易用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 方法缓存响应。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-05-11
        • 2013-04-29
        • 2013-04-03
        • 2012-03-28
        • 1970-01-01
        • 2018-12-07
        • 1970-01-01
        • 2016-03-01
        相关资源
        最近更新 更多