如何将Drawable转换为Bitmap?

1093

我希望将某个特定的 Drawable 设为设备的壁纸,但所有的壁纸功能只接受 Bitmap。由于我的环境版本低于2.1,因此无法使用 WallpaperManager

此外,我的绘图对象是从网络下载而来的,而不是存储在 R.drawable 中的。


1
http://codingaffairs.blogspot.com/2016/06/how-to-convert-drawable-to-bitmap-in.html - Developine
1
https://dev59.com/NnA75IYBdhLWcg3w6Nr8#15555203 - Erfan Bagheri
22个回答

1402

这段代码会有所帮助。

Bitmap icon = BitmapFactory.decodeResource(context.getResources(),
                                           R.drawable.icon_resource);

这里是一个版本,其中图片被下载。

String name = c.getString(str_url);
URL url_value = new URL(name);
ImageView profile = (ImageView)v.findViewById(R.id.vdo_icon);
if (profile != null) {
    Bitmap mIcon1 =
        BitmapFactory.decodeStream(url_value.openConnection().getInputStream());
    profile.setImageBitmap(mIcon1);
}

15
我认为我找到了一些东西:如果“draw”是我想要转换成位图的可绘制对象,则以下语句可以完成操作:Bitmap bitmap = ((BitmapDrawable)draw).getBitmap();就可以了! - Rob
2
@Rob:如果你的Drawable只是一个BitmapDrawable(这意味着你的Drawable实际上只是一个Bitmap的包装器). - njzk2
2
注意:这会导致使用JPG格式时出现大量的java.lang.OutOfMemoryError错误。 - Someone Somewhere
1
这将资源解码为位图,它不会将Drawable转换为位图,而是从源创建位图。 - Lassi Kinnunen
9
这不适用于SVG图像。BitmapFactory.decodeResource()会返回null。 - John Glen
显示剩余5条评论

831
public static Bitmap drawableToBitmap (Drawable drawable) {
    Bitmap bitmap = null;

    if (drawable instanceof BitmapDrawable) {
        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
        if(bitmapDrawable.getBitmap() != null) {
            return bitmapDrawable.getBitmap();
        }
    }

    if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
        bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
    } else {
        bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    }

    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);
    return bitmap;
}

52
这似乎是唯一适用于任何类型的可绘制对象并且还为已经是BitmapDrawable的可绘制对象提供了快速解决方案的答案。+1 - Matt Wolfe
17
注意:如果可绘制对象是纯色的,则getIntrinsicWidth()getIntrinsicHeight()将返回-1。 - S.D.
5
所以...再检查一下ColorDrawable,我们就有赢家了。说真的,有人把这个答案设为被接受的答案吧。 - kaay
2
与被标记的答案相反,这个回答解决了问题。 - njzk2
1
这并不适用于所有可绘制类型。例如我尝试的GradientDrawable:java.lang.ClassCastException: android.graphics.drawable.GradientDrawable无法转换为android.graphics.drawable.BitmapDrawable - Anonymous
显示剩余9条评论

224

这将BitmapDrawable转换为Bitmap。

Drawable d = ImagesArrayList.get(0);  
Bitmap bitmap = ((BitmapDrawable)d).getBitmap();

10
这真的是最好的方式吗?肯定可以使用其他类型的drawable,这样会抛出运行时异常吗?例如,它可以是一个NinePatchDrawble...? - Dori
4
在进行类型转换之前,你可以将代码放入条件语句中以检查它是否确实是BitmapDrawable类型:if (d instanceof BitmapDrawable) { Bitmap bitmap = ((BitmapDrawable)d).getBitmap(); } - Tony Chan
397
难以置信这个代码得到了64个赞?显然,该代码只有在“d”已经是“BitmapDrawable”的情况下才能正常工作,在这种情况下,将其作为位图检索非常简单...在所有其他情况下,将会崩溃并抛出“ClassCastException”。 - mxk
3
@Matthias 更不用说这个问题本身,同一个作者,已经有100个投票了 :/ - quinestor
2
对于那些抱怨这是错误的人,你们有没有查看 Stack Overflow 关于页面?上面写着被采纳的答案不一定意味着答案是正确的,它只是意味着该答案为 OP 所采用。 - lemuel
显示剩余10条评论

157

Drawable可以绘制在Canvas上,而Canvas可以由Bitmap支持:

(更新以处理BitmapDrawable的快速转换并确保创建的Bitmap具有有效大小)

public static Bitmap drawableToBitmap (Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable)drawable).getBitmap();
    }

    int width = drawable.getIntrinsicWidth();
    width = width > 0 ? width : 1;
    int height = drawable.getIntrinsicHeight();
    height = height > 0 ? height : 1;

    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap); 
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}

如果drawable参数为空会发生什么? - hector6872
1
该方法不支持VectorDrawable。 - Mahmoud
http://codingaffairs.blogspot.com/2016/06/how-to-convert-drawable-to-bitmap-in.html - Developine
假设您获得了一个非空的Drawable,为什么需要检查宽度和高度不为0? 此外,如果它们的大小相同,为什么需要使用setBounds()? - Yoav Feuerstein
好的解决方案!Android 8.0 /sdk 26中,ApplicationInfo.loadIcon(PackageManager pm)返回一个AdaptiveIconDrawable。使用您的代码可以帮助我将AdaptiveIconDrawable转换为位图。 - burulangtu
BitmapDrawable 的快速路径会忽略可能的着色器、颜色过滤器和透明度。 - Miha_x64

66

方法1:你可以直接像这样转换为位图

Bitmap myLogo = BitmapFactory.decodeResource(context.getResources(), R.drawable.my_drawable);

方法二: 您甚至可以将资源转换为drawable,然后从中获取位图,就像这样:

Bitmap myLogo = ((BitmapDrawable)getResources().getDrawable(R.drawable.logo)).getBitmap();

对于 API > 22getDrawable 方法已经被移动到了 ResourcesCompat 类中,因此你可以像这样做:

对于 API > 22getDrawable 方法已经被移动到了 ResourcesCompat 类中,因此你可以通过以下方式实现:

Bitmap myLogo = ((BitmapDrawable) ResourcesCompat.getDrawable(context.getResources(), R.drawable.logo, null)).getBitmap();

ResourcesCompat 只在 drawable 是 BitmapDrawable 时有效,如果你使用的是 VectorDrawable,那么就会出现 CCE。 - Brill Pappin
这两种方法都无法使用 VectorDrawable 资源。会出现以下错误 - android.graphics.drawable.VectorDrawable 无法转换为 android.graphics.drawable.BitmapDrawable - AdamHurwitz
这个解决方案在Kotlin中运作良好。 - AdamHurwitz

60

2
这是Kotlin中VectorDrawable的最简单解决方案!此外,在此SO帖子中还分享了更详细的内容。 - AdamHurwitz
我从这个方法中得到了一个空的位图。 - John Glen

51

1) 将Drawable转换为Bitmap:

Bitmap mIcon = BitmapFactory.decodeResource(context.getResources(),R.drawable.icon);
// mImageView.setImageBitmap(mIcon);

2) 将位图转换为可绘制对象:

Drawable mDrawable = new BitmapDrawable(getResources(), bitmap);
// mImageView.setDrawable(mDrawable);

2
问题明确说明了可绘制对象不驻留在R.drawable中,而您的解决方案是针对R.drawable的。 - Mohammed Junaid
R.drawable.icon 是 Int 类型而不是 Drawable 类型。 - undefined

32

非常简单

Bitmap tempBMP = BitmapFactory.decodeResource(getResources(),R.drawable.image);

11
这只是在三年前的这个问题上另一个答案的抄袭。 - Nick Cardoso

20
最新的Androidx核心库(androidx.core:core-ktx:1.2.0)现在有一个扩展函数:Drawable.toBitmap(...),可以将Drawable转换为Bitmap。

1
我还无法弄清楚如何导入那个函数。我假设它在 Kotlin 之外也能工作? - Saman Miran
1
从Java中,您需要导入包含扩展方法的Kt文件。此外,签名略微复杂,因为接收器和默认参数在Java中不可用。它将是这样的:DrawableKt.toBitmap(...); - Balazs Banyai
确保包含参数,因为Java不能使用默认参数来检测方法重载。DrawableKt.toBitmap( mCornerTopLeftDrawable, mCornerTopLeftDrawable.getIntrinsicWidth(), mCornerTopLeftDrawable.getIntrinsicHeight(), null ) - Tamim Attafi

19

看了(和使用)其他答案后,似乎它们都没有正确处理ColorDrawablePaintDrawable。(特别是在棒棒糖版本上)看起来Shader被调整以使单色块颜色不正确地处理。

我现在正在使用以下代码:

public static Bitmap drawableToBitmap(Drawable drawable) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    }

    // We ask for the bounds if they have been set as they would be most
    // correct, then we check we are  > 0
    final int width = !drawable.getBounds().isEmpty() ?
            drawable.getBounds().width() : drawable.getIntrinsicWidth();

    final int height = !drawable.getBounds().isEmpty() ?
            drawable.getBounds().height() : drawable.getIntrinsicHeight();

    // Now we check we are > 0
    final Bitmap bitmap = Bitmap.createBitmap(width <= 0 ? 1 : width, height <= 0 ? 1 : height,
            Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}

与其他方法不同的是,如果在要求将Drawable转换为位图之前对其调用setBounds,它将以正确的大小绘制位图!


@Chris.Jenkins 如果它没有边界,现在会得到新的边界吗?我还想问另一个问题:设置位图大小的最佳方法是什么(即使对于返回的BitmapDrawable)? - android developer
我建议您仔细阅读代码。如果Drawable没有设置边界,则使用IntrinsicWidth/Height。如果它们都小于等于0,则将画布设置为1像素。如果Drawable没有边界,您是正确的,它将传递一些(在大多数情况下为1x1),但是对于像ColorDrawable这样没有内在大小的东西,这是必需的。 如果我们不这样做,它会抛出一个异常,因为您无法将0x0绘制到画布上。 - Chris.Jenkins
@Chris.Jenkins 在这里mutate()是做什么的?存储先前的边界,最后再设置不是更容易吗?您能否更新代码使其更正确?另外,关于位图大小,您所写的是非BitmapDrawable情况。对于BitmapDrawable,我猜您只需使用“createScaledBitmap”(或类似的东西,取决于您想如何缩放)? - android developer
1
mutate()会创建一个副本,保留原始的可绘制对象,这将消除传回原始边界的问题。我很少根据这些点更改代码。如果您的用例需要,请添加另一个答案。我建议您为位图缩放创建另一个问题。 - Chris.Jenkins
@Chris.Jenkins:需要注意的是,mutate() 方法不会创建副本。它只是将其标记为可变的,以便不影响与同一 ConstantState 共享的其他可绘制对象。此外,边界不包含在 ConstantState 中,因此在这种情况下 mutate() 方法无法使用。 - David Liu
显示剩余6条评论

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