从矢量可绘制获取位图


132

在我的应用程序中,我必须为通知设置一个大图标。LargeIcon必须是位图,而我的可绘制对象是矢量图像(Android的新功能,请参阅此链接)问题是,当我尝试解码作为矢量图像的资源时,返回空值。

这是代码示例:

if (BitmapFactory.decodeResource(arg0.getResources(), R.drawable.vector_menu_objectifs) == null)
        Log.d("ISNULL", "NULL");
    else
        Log.d("ISNULL", "NOT NULL");

在此示例中,当我用“正常”图像(例如png)替换R.drawable.vector_menu_objectifs时,结果不为null(我得到了正确的位图)是否缺少某些内容?


1
有similiar问题,而不是解决办法,但解决方法:stackoverflow.com/questions/33548447/...

Answers:


231

已检查API:17、21、23

public static Bitmap getBitmapFromVectorDrawable(Context context, int drawableId) {
    Drawable drawable = ContextCompat.getDrawable(context, drawableId);
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        drawable = (DrawableCompat.wrap(drawable)).mutate();
    }

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

更新:

项目gradle:

dependencies {
        classpath 'com.android.tools.build:gradle:2.2.0-alpha5'
    }

模块gradle:

android {
    compileSdkVersion 23
    buildToolsVersion '23.0.3'
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 23
        vectorDrawables.useSupportLibrary = true
    }
    ...
}
...

谢谢。从23
Paha

2
这对我来说很棒。运行在15 API没有问题
维拉

4
AppCompatDrawableManager标记为,@RestrictTo(LIBRARY_GROUP)因此它是内部的,您不应使用它(它的API可能会更改,恕不另行通知)。使用ContextCompat代替。
mradzinski'2

不工作Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object reference在这条线Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
user25

5
我对此解决方案有疑问。对我而言,使用:AppCompatResources而不是ContextCompat对其进行了修复:Drawable drawable = AppCompatResources.getDrawable(context,drawableId);
Mike T

64

您可以使用以下方法:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static Bitmap getBitmap(VectorDrawable vectorDrawable) {
    Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
            vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    vectorDrawable.draw(canvas);
    return bitmap;
}

我有时会结合:

private static Bitmap getBitmap(Context context, int drawableId) {
    Drawable drawable = ContextCompat.getDrawable(context, drawableId);
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    } else if (drawable instanceof VectorDrawable) {
        return getBitmap((VectorDrawable) drawable);
    } else {
        throw new IllegalArgumentException("unsupported drawable type");
    }
}

2
希望@liltof回来并将其标记为答案。需要注意的一件事是,这两种方法都需要targetAPi包装器,但是android studio会告诉您。
robertotomás16

1
我花了大约两天的时间尝试执行此操作,现在正在考虑其svg文件问题。谢谢!
sparkly_frog

1
不工作Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object reference在这条线Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
user25

45

如果您愿意为Kotlin 使用Android KTX,则可以使用扩展方法Drawable#toBitmap()来获得与其他答案相同的效果:

val bitmap = AppCompatResources.getDrawable(requireContext(), drawableId).toBitmap() 

要么

val bitmap = AppCompatResources.getDrawable(context, drawableId).toBitmap() 

要添加此扩展方法和其他有用的扩展方法,您需要在模块级别添加以下内容 build.gradle

repositories {
    google()
}

dependencies {
    implementation "androidx.core:core-ktx:1.2.0"
}

有关将依赖项添加到项目中的最新说明,请参见此处

请注意,这将适用于的任何子类,Drawable如果DrawableBitmapDrawable,则将是使用底层的快捷方式Bitmap


对于Kotlin,这是最简单的解决方案。
亚当·赫维兹

1
对我来说,它与完美配合VectorDrawable
ubuntudroid

这是最好的选择..默认androidx提供的功能
Reshma

27

根据先前的答案,可以简化为既匹配VectorDrawable也可以匹配BitmapDrawable,并且至少与API 15兼容。

public static Bitmap getBitmapFromDrawable(Context context, @DrawableRes int drawableId) {
    Drawable drawable = AppCompatResources.getDrawable(context, drawableId);

    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    } else if (drawable instanceof VectorDrawableCompat || drawable instanceof VectorDrawable) {
        Bitmap 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;
    } else {
        throw new IllegalArgumentException("unsupported drawable type");
    }
}

然后,您必须添加gradle文件:

android {
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
}

在棒棒糖之前,它将使用VectorDrawableCompat;在棒棒糖上,它将使用VectorDrawable。

编辑

我已经根据@ user3109468的评论编辑了条件


1
drawable instanceof VectorDrawable || VectorDrawableCompat的drawable实例应该交换边。如果VectorDrawable不存在,则应首先检查VectorDrawableCompat,因为它确实存在,因此导致未找到类的结果。
Warrick

VertorDrawable的类型检查可以描述为(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable instanceof VectorDrawable)
chmin

6

@Alexey的荣誉

这是Kotlin使用扩展名的版本Context

fun Context.getBitmapFromVectorDrawable(drawableId: Int): Bitmap? {
    var drawable = ContextCompat.getDrawable(this, drawableId) ?: return null

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        drawable = DrawableCompat.wrap(drawable).mutate()
    }

    val bitmap = Bitmap.createBitmap(
            drawable.intrinsicWidth,
            drawable.intrinsicHeight,
            Bitmap.Config.ARGB_8888) ?: return null
    val canvas = Canvas(bitmap)
    drawable.setBounds(0, 0, canvas.width, canvas.height)
    drawable.draw(canvas)

    return bitmap
}

用法示例Activity

val bitmap = this.getBitmapFromVectorDrawable(R.drawable.ic_done_white_24dp)

1

在API 16-具有Vector Drawables的JellyBean上进行了测试

public static Bitmap getBitmapFromVectorDrawable(Context context, int drawableId) {
    Drawable drawable = AppCompatResources.getDrawable(context, drawableId);
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        drawable = (DrawableCompat.wrap(drawable)).mutate();
    }

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

1

使用以下代码转换具有正确纵横比的图像(例如,用于通知图标):

public static Bitmap getBitmapFromVector(Context context, int drawableId) {
    Drawable drawable = ContextCompat.getDrawable(context, drawableId);
    int width = drawable.getIntrinsicWidth();
    int height = drawable.getIntrinsicHeight();
    Bitmap bitmap;
    if (width < height) {    //make a square
        bitmap = Bitmap.createBitmap(height, height, Bitmap.Config.ARGB_8888);
    } else {
        bitmap = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
    }
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0,
            drawable.getIntrinsicWidth(),    //use dimensions of Drawable
            drawable.getIntrinsicHeight()
    );
    drawable.draw(canvas);
    return bitmap;
}

0

如果vector图像intrinsicWidthintrinsicHeight很小,并且您尝试将位图显示为大视图,那么您将看到结果是模糊的。

在这种情况下,您可以为位图提供新的宽度/高度,以获得更好的图像(或者可以增加xml中的向量大小,但可以提供,desireWidth并且desireHeight可能更灵活)。

private fun getBitmap(drawableId: Int, desireWidth: Int? = null, desireHeight: Int? = null): Bitmap? {
    val drawable = AppCompatResources.getDrawable(context, drawableId) ?: return null
    val bitmap = Bitmap.createBitmap(
        desireWidth ?: drawable.intrinsicWidth,
        desireHeight ?: drawable.intrinsicHeight,
        Bitmap.Config.ARGB_8888
    )
    val canvas = Canvas(bitmap)
    drawable.setBounds(0, 0, canvas.width, canvas.height)
    drawable.draw(canvas)
    return bitmap
}

希望对你有帮助


0
Drawable layerDrawable = (Drawable) imageBase.getDrawable();
Bitmap bitmap = Bitmap.createBitmap(layerDrawable.getIntrinsicWidth(),
        layerDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
layerDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
layerDrawable.draw(canvas);  
imageTeste.setImageBitmap(addGradient(bitmap));

0

如果您希望能够将输出缩放到所需的输出大小,请尝试以下代码段:

fun getBitmapFromVectorDrawable(context: Context, drawableId: Int, outputSize: OutputSize? = null): Bitmap? {
    var drawable = ContextCompat.getDrawable(context, drawableId) ?: return null
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        drawable = DrawableCompat.wrap(drawable).mutate()
    }

    var targetBitmap: Bitmap
    if (outputSize != null) {
        targetBitmap = Bitmap.createBitmap(outputSize.width,
                outputSize.height, Bitmap.Config.ARGB_8888)
    } else {
        targetBitmap = Bitmap.createBitmap(drawable.intrinsicWidth,
            drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
    }

    val canvas = Canvas(targetBitmap)
    val scaleX =  targetBitmap.width.toFloat()/drawable.intrinsicWidth.toFloat()
    val scaleY =  targetBitmap.height.toFloat()/drawable.intrinsicHeight.toFloat()
    canvas.scale(scaleX, scaleY)
    drawable.draw(canvas)

    return targetBitmap
}

class OutputSize(val width: Int, val height: Int)

0

这将为您提供所需大小的位图。另外,它使您可以根据每个图像来保持或不保持透明度,以使不需要的图像具有更好的性能。

public static Bitmap drawableToBitmap(Resources res, int drawableId,
        int width, int height, boolean keepAlpha) {
    Drawable drawable = res.getDrawable(drawableId);
    Bitmap bmp = createBitmap(width, height, keepAlpha ?
            Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
    Canvas cvs = new Canvas(bmp);
    drawable.setBounds(0, 0, width, height);
    drawable.draw(cvs);
    return bmp;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.