VectorDrawable 与 GoogleMap BitmapDescriptor
VectorDrawable with GoogleMap BitmapDescriptor
我在使用 VectorDrawable
、API 5.0+
为 MarkerOptions
创建图标时遇到 google 地图 BitmapDescriptor
的问题
创建方法:
@NonNull
private BitmapDescriptor getBitmapDescriptor(int id) {
return BitmapDescriptorFactory.fromResource(id);
}
当 id
参数包含 png 可绘制对象时,一切都很好,但是如果我尝试使用 xml 中定义的 VectorDrawable
,应用程序总是在 googleMap.addMarker(marker)
时崩溃(BitmapDescriptor
不为空)。
11-05 10:15:05.213 14536-14536/xxx.xxxxx.app E/AndroidRuntime: FATAL EXCEPTION: main
Process: xxx.xxxxx.app, PID: 14536
java.lang.NullPointerException
at com.google.a.a.ae.a(Unknown Source)
at com.google.maps.api.android.lib6.d.dn.<init>(Unknown Source)
at com.google.maps.api.android.lib6.d.dm.a(Unknown Source)
at com.google.maps.api.android.lib6.d.ag.<init>(Unknown Source)
at com.google.maps.api.android.lib6.d.eu.a(Unknown Source)
at com.google.android.gms.maps.internal.j.onTransact(SourceFile:167)
at android.os.Binder.transact(Binder.java:380)
at com.google.android.gms.maps.internal.IGoogleMapDelegate$zza$zza.addMarker(Unknown Source)
at com.google.android.gms.maps.GoogleMap.addMarker(Unknown Source)
at xxx.xxxxx.app.ui.details.DetailActivity.lambda$initGoogleMaps(DetailActivity.java:387)
at xxx.xxxxx.app.ui.details.DetailActivity.access$lambda(DetailActivity.java)
at xxx.xxxxx.app.ui.details.DetailActivity$$Lambda.onMapReady(Unknown Source)
at com.google.android.gms.maps.SupportMapFragment$zza.zza(Unknown Source)
at com.google.android.gms.maps.internal.zzl$zza.onTransact(Unknown Source)
at android.os.Binder.transact(Binder.java:380)
at com.google.android.gms.maps.internal.av.a(SourceFile:82)
at com.google.maps.api.android.lib6.d.fa.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
我如何检索可绘制对象并不重要,尝试使用 BitmapFactory.fromResources
和后来的 BitmapDescritpionFactory.fromBitmap
创建位图,但结果是一样的。它不适用于矢量可绘制对象。也尝试了不同的向量,但似乎向量的复杂性不是这里的问题。
有谁知道如何解决这个崩溃问题?
@edit
似乎问题不在于 BitmapDescriptior
本身,而在于加载 VectorDrawable
返回了不正确的位图。但是答案中提出的解决方案仍然可以。
可能的解决方法:
private BitmapDescriptor getBitmapDescriptor(int id) {
Drawable vectorDrawable = context.getDrawable(id);
int h = ((int) Utils.convertDpToPixel(42, context));
int w = ((int) Utils.convertDpToPixel(25, context));
vectorDrawable.setBounds(0, 0, w, h);
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bm);
vectorDrawable.draw(canvas);
return BitmapDescriptorFactory.fromBitmap(bm);
}
这是另一个参考:
http://qiita.com/konifar/items/aaff934edbf44e39b04a
public class ResourceUtil {
@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(VectorDrawableCompat 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;
}
public static Bitmap getBitmap(Context context, @DrawableRes int drawableResId) {
Drawable drawable = ContextCompat.getDrawable(context, drawableResId);
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
} else if (drawable instanceof VectorDrawableCompat) {
return getBitmap((VectorDrawableCompat) drawable);
} else if (drawable instanceof VectorDrawable) {
return getBitmap((VectorDrawable) drawable);
} else {
throw new IllegalArgumentException("Unsupported drawable type");
}
}
}
根据the bug report ( - thanks!) using a VectorDrawable won't be supported for the time being. See the comment in the bug report了解更多信息。
以下是 Google 地图团队建议的解决方法:
/**
* Demonstrates converting a {@link Drawable} to a {@link BitmapDescriptor},
* for use as a marker icon.
*/
private BitmapDescriptor vectorToBitmap(@DrawableRes int id, @ColorInt int color) {
Drawable vectorDrawable = ResourcesCompat.getDrawable(getResources(), id, null);
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());
DrawableCompat.setTint(vectorDrawable, color);
vectorDrawable.draw(canvas);
return BitmapDescriptorFactory.fromBitmap(bitmap);
}
这样使用:
// Vector drawable resource as a marker icon.
mMap.addMarker(new MarkerOptions()
.position(ALICE_SPRINGS)
.icon(vectorToBitmap(R.drawable.ic_android, Color.parseColor("#A4C639")))
.title("Alice Springs"));
矢量图的着色是一种奖励
VectorDrawable
到 BitmapDescriptor
无色调
private BitmapDescriptor getBitmapDescriptor(@DrawableRes int id) {
Drawable vectorDrawable = ResourcesCompat.getDrawable(getResources(), id, null);
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 BitmapDescriptorFactory.fromBitmap(bitmap);
}
谢谢@lbarbosa
Kotlin 也一样
private fun getBitmapDescriptorFromVector(id: Int, context: Context): BitmapDescriptor {
var vectorDrawable: Drawable = context.getDrawable(id)
var h = (24 * getResources().getDisplayMetrics().density).toInt();
var w = (24 * getResources().getDisplayMetrics().density).toInt();
vectorDrawable.setBounds(0, 0, w, h)
var bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
var canvas = Canvas(bm)
vectorDrawable.draw(canvas)
return BitmapDescriptorFactory.fromBitmap(bm)
}
编辑
@CoolMind 你完全正确,谢谢 - 已编辑
Kotlin 版本
private fun getBitmapDescriptor(context: Context, id: Int): BitmapDescriptor? {
val vectorDrawable: Drawable?
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
vectorDrawable = context.getDrawable(id)
} else {
vectorDrawable = ContextCompat.getDrawable(context, id)
}
if (vectorDrawable != null) {
val w = vectorDrawable.intrinsicWidth
val h = vectorDrawable.intrinsicHeight
vectorDrawable.setBounds(0, 0, w, h)
val bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
val canvas = Canvas(bm);
vectorDrawable.draw(canvas);
return BitmapDescriptorFactory.fromBitmap(bm);
}
return null
}
或者您可以简单地使用 Android KTX
例如:
val markerBitmap = ResourcesCompat.getDrawable(resources, R.drawable.ic_marker, null)?.toBitmap()
val icon = BitmapDescriptorFactory.fromBitmap(markerBitmap)
val marker = MarkerOptions().icon(icon)
参考:.toBitmap()
我认为这是最简单的解决方案,因为它隐藏了 MarkerOptions 的扩展函数中的实现:
fun MarkerOptions.icon(context: Context, @DrawableRes vectorDrawable: Int): MarkerOptions {
this.icon(ContextCompat.getDrawable(context, vectorDrawable)?.run {
setBounds(0, 0, intrinsicWidth, intrinsicHeight)
val bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
draw(Canvas(bitmap))
BitmapDescriptorFactory.fromBitmap(bitmap)
})
return this
}
所以最终使用的结果应该是这样的:
MarkerOptions().position(myLocation).icon(requireContext(), R.drawable.ic_my_location_map)
如果您在应用程序的不同位置有多个地图,那么您不需要 copy/paste 实施多个 类。
我在使用 VectorDrawable
、API 5.0+
MarkerOptions
创建图标时遇到 google 地图 BitmapDescriptor
的问题
创建方法:
@NonNull
private BitmapDescriptor getBitmapDescriptor(int id) {
return BitmapDescriptorFactory.fromResource(id);
}
当 id
参数包含 png 可绘制对象时,一切都很好,但是如果我尝试使用 xml 中定义的 VectorDrawable
,应用程序总是在 googleMap.addMarker(marker)
时崩溃(BitmapDescriptor
不为空)。
11-05 10:15:05.213 14536-14536/xxx.xxxxx.app E/AndroidRuntime: FATAL EXCEPTION: main
Process: xxx.xxxxx.app, PID: 14536
java.lang.NullPointerException
at com.google.a.a.ae.a(Unknown Source)
at com.google.maps.api.android.lib6.d.dn.<init>(Unknown Source)
at com.google.maps.api.android.lib6.d.dm.a(Unknown Source)
at com.google.maps.api.android.lib6.d.ag.<init>(Unknown Source)
at com.google.maps.api.android.lib6.d.eu.a(Unknown Source)
at com.google.android.gms.maps.internal.j.onTransact(SourceFile:167)
at android.os.Binder.transact(Binder.java:380)
at com.google.android.gms.maps.internal.IGoogleMapDelegate$zza$zza.addMarker(Unknown Source)
at com.google.android.gms.maps.GoogleMap.addMarker(Unknown Source)
at xxx.xxxxx.app.ui.details.DetailActivity.lambda$initGoogleMaps(DetailActivity.java:387)
at xxx.xxxxx.app.ui.details.DetailActivity.access$lambda(DetailActivity.java)
at xxx.xxxxx.app.ui.details.DetailActivity$$Lambda.onMapReady(Unknown Source)
at com.google.android.gms.maps.SupportMapFragment$zza.zza(Unknown Source)
at com.google.android.gms.maps.internal.zzl$zza.onTransact(Unknown Source)
at android.os.Binder.transact(Binder.java:380)
at com.google.android.gms.maps.internal.av.a(SourceFile:82)
at com.google.maps.api.android.lib6.d.fa.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
我如何检索可绘制对象并不重要,尝试使用 BitmapFactory.fromResources
和后来的 BitmapDescritpionFactory.fromBitmap
创建位图,但结果是一样的。它不适用于矢量可绘制对象。也尝试了不同的向量,但似乎向量的复杂性不是这里的问题。
有谁知道如何解决这个崩溃问题?
@edit
似乎问题不在于 BitmapDescriptior
本身,而在于加载 VectorDrawable
返回了不正确的位图。但是答案中提出的解决方案仍然可以。
可能的解决方法:
private BitmapDescriptor getBitmapDescriptor(int id) {
Drawable vectorDrawable = context.getDrawable(id);
int h = ((int) Utils.convertDpToPixel(42, context));
int w = ((int) Utils.convertDpToPixel(25, context));
vectorDrawable.setBounds(0, 0, w, h);
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bm);
vectorDrawable.draw(canvas);
return BitmapDescriptorFactory.fromBitmap(bm);
}
这是另一个参考: http://qiita.com/konifar/items/aaff934edbf44e39b04a
public class ResourceUtil {
@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(VectorDrawableCompat 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;
}
public static Bitmap getBitmap(Context context, @DrawableRes int drawableResId) {
Drawable drawable = ContextCompat.getDrawable(context, drawableResId);
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
} else if (drawable instanceof VectorDrawableCompat) {
return getBitmap((VectorDrawableCompat) drawable);
} else if (drawable instanceof VectorDrawable) {
return getBitmap((VectorDrawable) drawable);
} else {
throw new IllegalArgumentException("Unsupported drawable type");
}
}
}
根据the bug report (
以下是 Google 地图团队建议的解决方法:
/**
* Demonstrates converting a {@link Drawable} to a {@link BitmapDescriptor},
* for use as a marker icon.
*/
private BitmapDescriptor vectorToBitmap(@DrawableRes int id, @ColorInt int color) {
Drawable vectorDrawable = ResourcesCompat.getDrawable(getResources(), id, null);
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());
DrawableCompat.setTint(vectorDrawable, color);
vectorDrawable.draw(canvas);
return BitmapDescriptorFactory.fromBitmap(bitmap);
}
这样使用:
// Vector drawable resource as a marker icon.
mMap.addMarker(new MarkerOptions()
.position(ALICE_SPRINGS)
.icon(vectorToBitmap(R.drawable.ic_android, Color.parseColor("#A4C639")))
.title("Alice Springs"));
矢量图的着色是一种奖励
VectorDrawable
到 BitmapDescriptor
无色调
private BitmapDescriptor getBitmapDescriptor(@DrawableRes int id) {
Drawable vectorDrawable = ResourcesCompat.getDrawable(getResources(), id, null);
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 BitmapDescriptorFactory.fromBitmap(bitmap);
}
谢谢@lbarbosa
Kotlin 也一样
private fun getBitmapDescriptorFromVector(id: Int, context: Context): BitmapDescriptor {
var vectorDrawable: Drawable = context.getDrawable(id)
var h = (24 * getResources().getDisplayMetrics().density).toInt();
var w = (24 * getResources().getDisplayMetrics().density).toInt();
vectorDrawable.setBounds(0, 0, w, h)
var bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
var canvas = Canvas(bm)
vectorDrawable.draw(canvas)
return BitmapDescriptorFactory.fromBitmap(bm)
}
编辑 @CoolMind 你完全正确,谢谢 - 已编辑
Kotlin 版本
private fun getBitmapDescriptor(context: Context, id: Int): BitmapDescriptor? {
val vectorDrawable: Drawable?
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
vectorDrawable = context.getDrawable(id)
} else {
vectorDrawable = ContextCompat.getDrawable(context, id)
}
if (vectorDrawable != null) {
val w = vectorDrawable.intrinsicWidth
val h = vectorDrawable.intrinsicHeight
vectorDrawable.setBounds(0, 0, w, h)
val bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
val canvas = Canvas(bm);
vectorDrawable.draw(canvas);
return BitmapDescriptorFactory.fromBitmap(bm);
}
return null
}
或者您可以简单地使用 Android KTX
例如:
val markerBitmap = ResourcesCompat.getDrawable(resources, R.drawable.ic_marker, null)?.toBitmap()
val icon = BitmapDescriptorFactory.fromBitmap(markerBitmap)
val marker = MarkerOptions().icon(icon)
参考:.toBitmap()
我认为这是最简单的解决方案,因为它隐藏了 MarkerOptions 的扩展函数中的实现:
fun MarkerOptions.icon(context: Context, @DrawableRes vectorDrawable: Int): MarkerOptions {
this.icon(ContextCompat.getDrawable(context, vectorDrawable)?.run {
setBounds(0, 0, intrinsicWidth, intrinsicHeight)
val bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
draw(Canvas(bitmap))
BitmapDescriptorFactory.fromBitmap(bitmap)
})
return this
}
所以最终使用的结果应该是这样的:
MarkerOptions().position(myLocation).icon(requireContext(), R.drawable.ic_my_location_map)
如果您在应用程序的不同位置有多个地图,那么您不需要 copy/paste 实施多个 类。