从矢量绘制中获取位图

在我的应用程序中,我必须为通知设置一个大图标。 LargeIcon 必须是一个 Bitmap,我的绘图工具是矢量图像(Android 的新特性,参见 这个链接) 问题在于,当我试图解码一个向量图像资源时,会得到一个 null 返回值。

下面是代码示例:

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

在这个示例中,当我用一个“普通”图像(例如 png)替换 R.draable.Vector _ menu _ objectifs 时,结果不是 null (我得到了正确的位图) 有什么我不知道的吗?

79494 次浏览

你可使用以下方法:

@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");
}
}

检查 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;
}

更新:

项目分级:

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

单元级:

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

基于以前的答案,它可以简化为同时匹配 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");
}
}

然后你必须加入你的等级档案:

android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}

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

剪辑

我已经在@user3109468的注释后面编辑了条件

编辑2(10/2020)

至少从 API 21开始,你可以使用这个代码代替上面的代码(我还没有尝试过以前的 API 版本) :

AppCompatResources.getDrawable(context, R.drawable.your_drawable)
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));

向“ Alexey”致敬

下面是使用 Context扩展的 Kotlin版本

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)

如果你愿意为 Kotlin 使用 安卓 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和如果 Drawable是一个 BitmapDrawable它将快捷方式使用底层的 Bitmap

如果您的 vector图像 intrinsicWidthintrinsicHeight是小的,你试图显示位图到一个大视图,然后你会看到结果是模糊的。

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

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
}

希望能有帮助

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

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)

基于 API 16-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;
}

使用以下代码转换图像 正确的高宽比(例如,通知图标) :

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

这样就可以得到所需大小的位图。此外,它允许您根据每个图像保持或不保持透明度,以便更好地处理那些不需要的图像。

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

对于这里给出的可绘制向量的一杯代码帮助我们,但是请记住,如果可绘制没有找到 NULL,它可能是 NULL

@Nullable
public static Bitmap drawableToBitmap(Context context, int drawableId) {
Drawable drawable = ContextCompat.getDrawable(context, drawableId);
if (drawable != null) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bmp;
}
return null;
}

创建矢量到位图的独立乐趣

 //vectorToBitmapMarker
private fun fromVectorToBitmap(id: Int, color: Int): BitmapDescriptor
{
val vectorDrawable: Drawable? = ResourcesCompat.getDrawable(resources, id, null)


if (vectorDrawable == null)
{
d("VTOB","Resource not found!")
return BitmapDescriptorFactory.defaultMarker()
}


val bitmap = Bitmap.createBitmap(
vectorDrawable.intrinsicWidth,
vectorDrawable.intrinsicHeight,
Bitmap.Config.ARGB_8888)


val canvas = Canvas(bitmap)
vectorDrawable.setBounds(0,0,canvas.width, canvas.height)
DrawableCompat.setTint(vectorDrawable, color)
vectorDrawable.draw(canvas)


return BitmapDescriptorFactory.fromBitmap(bitmap)


}

现在在 onMapReady ()-> . icon ()中进行更改

mMap.addMarker(
MarkerOptions().position(goa)
.title("Marker in Goa")
.draggable(true)
.icon(fromVectorToBitmap(R.drawable.location, Color.parseColor("#FF0560"))))