【问题标题】:VectorDrawable with GoogleMap BitmapDescriptorVectorDrawable 与 GoogleMap BitmapDescriptor
【发布时间】:2016-02-06 12:24:48
【问题描述】:

我在使用VectorDrawable,API 5.0+ 为MarkerOptions 创建图标时遇到了谷歌地图BitmapDescriptor 的问题

用于创建的方法:

@NonNull
private BitmapDescriptor getBitmapDescriptor(int id) {
    return BitmapDescriptorFactory.fromResource(id);
}

id 参数包含 png drawable 时,一切都很好,但是如果我尝试使用在 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$23(DetailActivity.java:387)
        at xxx.xxxxx.app.ui.details.DetailActivity.access$lambda$10(DetailActivity.java)
        at xxx.xxxxx.app.ui.details.DetailActivity$$Lambda$13.onMapReady(Unknown Source)
        at com.google.android.gms.maps.SupportMapFragment$zza$1.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 创建位图,但结果是相同的。它只是不适用于矢量可绘制对象。也尝试了不同的向量,但似乎向量复杂性不是这里的问题。

有谁知道如何解决这个崩溃问题?

@编辑

似乎问题不在于BitmapDescriptior 本身,而在于加载返回不正确位图的VectorDrawable。但是答案中提出的解决方案仍然可以。

【问题讨论】:

标签: android google-maps vector-graphics


【解决方案1】:

可能的解决方法:

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

【讨论】:

  • 不用计算高度和宽度,可以使用VectorDrawable.getIntrinsicHeight/Width()。除非您实际上是在尝试缩放图形
  • 实际上我正在那里进行缩放,但它可能对其他人有用
  • 如果您 Drawable vectorDrawable = ContextCompat.getDrawable(mContext, id);
【解决方案2】:

根据the bug reportposted by vaughandroid - 谢谢!)暂时不支持使用 VectorDrawable。请参阅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"));

矢量着色是一种奖励

【讨论】:

    【解决方案3】:

    或者你可以简单地使用Android KTX

    例如:

    val markerBitmap = ResourcesCompat.getDrawable(resources, R.drawable.ic_marker, null)?.toBitmap()
    val icon = BitmapDescriptorFactory.fromBitmap(markerBitmap)
    val marker = MarkerOptions().icon(icon)
    

    参考:.toBitmap()

    【讨论】:

    • 我们如何用这个改变矢量的颜色/色调?
    【解决方案4】:

    这是另一个参考: 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");
        }
    }
    }
    

    【讨论】:

      【解决方案5】:

      VectorDrawableBitmapDescriptor 无色调

      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

      【讨论】:

        【解决方案6】:

        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
        }
        

        【讨论】:

        • 我们如何用这个改变矢量的颜色/色调?
        【解决方案7】:

        在 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 你是完全正确的,谢谢 - 编辑

        【讨论】:

        • 如果由于某种原因您使用可空的context,那么为什么要写:context?.getDrawable(id)!!?如果context == null,它将与 Kotlin NPE 一起崩溃。
        【解决方案8】:

        我认为这是最简单的解决方案,因为它将实现隐藏在 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)
        

        如果您在应用程序的不同位置有多个地图,则它特别有用,因此您无需将实现复制/粘贴到多个类。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-10-21
          • 1970-01-01
          • 1970-01-01
          • 2021-08-12
          • 1970-01-01
          相关资源
          最近更新 更多