从矢量图中获取位图
Posted
技术标签:
【中文标题】从矢量图中获取位图【英文标题】:Getting Bitmap from vector drawable 【发布时间】:2016-02-15 06:08:17 【问题描述】:在我的应用程序中,我必须为通知设置一个大图标。 LargeIcon 必须是 Bitmap,而我的 drawables 是矢量图(android 中的新功能,请参阅this link) 问题是,当我尝试解码矢量图像资源时,返回 null。
这是代码示例:
if (BitmapFactory.decodeResource(arg0.getResources(), R.drawable.vector_menu_objectifs) == null)
Log.d("ISNULL", "NULL");
else
Log.d("ISNULL", "NOT NULL");
在此示例中,当我将 R.drawable.vector_menu_objectifs 替换为“正常”图像时,例如 png,结果不为空(我得到正确的位图) 我有什么遗漏吗?
【问题讨论】:
有类似问题,不是解决方案,而是解决方法:***.com/questions/33548447/… 【参考方案1】:已检查 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
...
...
【讨论】:
AppCompatDrawableManager
被标记为 @RestrictTo(LIBRARY_GROUP)
所以它是内部的,你不应该使用它(它的 API 可以更改,恕不另行通知)。请改用ContextCompat
。
在这条线上不起作用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);
@mradzinski 很好,它正在工作。我已经编辑了答案以更好地反映最佳解决方案。
我对此解决方案有疑问。对于我使用:AppCompatResources 而不是 ContextCompat 修复它: Drawable drawable = AppCompatResources.getDrawable(context, drawableId);
这对我很有用。在 API 28 上运行没有问题【参考方案2】:
如果您愿意将Android KTX 用于Kotlin,您可以使用扩展方法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"
请参阅here 了解将依赖项添加到项目的最新说明。
请注意,这适用于Drawable
的任何 子类,如果Drawable
是BitmapDrawable
,它将使用底层Bitmap
的快捷方式。
【讨论】:
这是 Kotlin 最简单的解决方案。 对我来说,VectorDrawable
工作得很好。
这是最好的选择.. 默认 androidx 提供功能【参考方案3】:
您可以使用以下方法:
@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");
【讨论】:
希望@liltof 回来并将其标记为答案。需要注意的一件事是,这两种方法都需要 targetAPi 包装器——但 android studio 会告诉你。 我花了大约两天时间尝试这样做,现在正在考虑它的 svg 文件问题。谢谢! 在这条线上不起作用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);
【参考方案4】:
根据之前的答案,可以这样简化,以匹配 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
在 Lollipop 之前,它将使用 VectorDrawableCompat,而在 Lollipop 上,它将使用 VectorDrawable。
编辑
我根据@user3109468 的评论编辑了条件
编辑 2 (10/2020)
至少从 API 21 开始,您现在可以使用它来代替上面的代码(我没有尝试过以前的 API 版本):
AppCompatResources.getDrawable(context, R.drawable.your_drawable)
【讨论】:
VectorDrawable 的可绘制实例 ||可绘制 instanceof VectorDrawableCompat 应该交换边。当 VectorDrawable 不存在时导致 Class Not Found,此时应首先检查 VectorDrawableCompat,因为它确实存在。 VertorDrawable的类型检查可以描述为(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable instanceof VectorDrawable)
【参考方案5】:
向@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)
【讨论】:
【参考方案6】:在 API 16 上测试 - 带有 Vector Drawable 的 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;
【讨论】:
【参考方案7】:对于vector drawable这里给出cup代码帮助我们,但是记住如果drawable没有找到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;
【讨论】:
【参考方案8】:使用以下代码将图像转换为正确的纵横比(例如,通知图标):
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;
【讨论】:
【参考方案9】:如果您的vector
图像intrinsicWidth
和intrinsicHeight
很小,并且您尝试将位图显示为大视图,那么您会看到结果是模糊的。
在这种情况下,您可以为位图提供新的宽度/高度以获得更好的图像(或者您可以增加 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
希望对你有帮助
【讨论】:
【参考方案10】: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));
【讨论】:
【参考方案11】:如果您希望能够将输出缩放到所需的输出大小,请尝试以下 sn-p:
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)
【讨论】:
【参考方案12】:这将为您提供所需大小的位图。此外,它还允许您根据每个图像保持或不保持透明度,以便在不需要它的图像中获得更好的性能。
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;
【讨论】:
【参考方案13】:创建矢量到位图的独立乐趣
//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"))))
【讨论】:
以上是关于从矢量图中获取位图的主要内容,如果未能解决你的问题,请参考以下文章
在 iPad 上将 UIView 作为矢量呈现为 PDF - 有时呈现为位图,有时呈现为矢量