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

时间:2013-05-05 05:54:48

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

我正在制作媒体播放器应用,并希望加载专辑封面图片以在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吗?这就是缩略图所需要的更大,我可以创建一个重复的函数或参数来获取完整大小的艺术作品以显示全屏。

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

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

4 个答案:

答案 0 :(得分: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;     
}

答案 1 :(得分: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的一些很好的信息。

答案 2 :(得分:0)

执行此操作的一种方法是AQuery library

这是一个允许您从本地存储或网址延迟加载图像的库。支持缓存和降尺度等功能。

在没有缩减的情况下延迟加载资源的示例:

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

答案 3 :(得分: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方法缓存回复。