带有矢量资产图标的 android 谷歌地图中的自定义标记

Posted

技术标签:

【中文标题】带有矢量资产图标的 android 谷歌地图中的自定义标记【英文标题】:Custom marker in google maps in android with vector asset icon 【发布时间】:2017-07-10 23:27:44 【问题描述】:

我们如何用矢量资产文件实现地图标记图标,谷歌以编程方式显示它的方式:

更新:

map.addMarker(new MarkerOptions()
    .position(latLng)
    .icon(BitmapDescriptorFactory.fromResource(R.drawable.your_vector_asset))
    .title(title);

这在处理矢量资产时不起作用。问这个问题的主要原因。上面代码的错误:

java.lang.IllegalArgumentException:无法解码图像。提供的图像必须是位图。

【问题讨论】:

@RishabhMahatha 我可以用 png 轻松做到这一点,但问题是 png 是一个沉重的文件。要么我必须将它存储在我的 apk 中,要么必须从服务器调用它。但我特别需要它来自矢量资产文件。 @LucaNicoletti 我必须使用矢量资产(或 SVG)文件来实现这一点。而且我没有得到任何库或方法来做到这一点。 有人吗?还没有找到任何东西.. 这个答案也可以找到:***.com/questions/33696488/… 有一个扩展方法 Drawable.toBitmap() 你可以用 BitmapDescriptorFactory.fromBitmap() 来实现 【参考方案1】:

你可以使用这个方法:

private BitmapDescriptor bitmapDescriptorFromVector(Context context, int vectorResId) 
        Drawable vectorDrawable = ContextCompat.getDrawable(context, vectorResId);
        vectorDrawable.setBounds(0, 0, vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight());
        Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        vectorDrawable.draw(canvas);
        return BitmapDescriptorFactory.fromBitmap(bitmap);

所以你的代码看起来像:

map.addMarker(new MarkerOptions()
                .position(latLng)
                .icon(bitmapDescriptorFromVector(getActivity(), R.drawable.your_vector_asset))
                .title(title);

编辑: 在 Kotlin 中它可能看起来像:

private fun bitmapDescriptorFromVector(context: Context, vectorResId: Int): BitmapDescriptor? 
        return ContextCompat.getDrawable(context, vectorResId)?.run 
            setBounds(0, 0, intrinsicWidth, intrinsicHeight)
            val bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
            draw(Canvas(bitmap))
            BitmapDescriptorFactory.fromBitmap(bitmap)
        
    

【讨论】:

你试过了吗。好久没看到这段代码了! 当然,我在项目中使用过 这仍然解决了我的问题!你的方法给我的只是那个汽车图标,而不是蓝色背景。我需要保留那个背景加上这个图标。 你的意思是汽车图标是独立于图钉的图标吗? 在我的项目中使用它从矢量资产中获取标记。【参考方案2】:

我一直在寻找完全相同的要求,起初看到这个问题让我很高兴,但和@Shuddh 一样,我对给出的答案并不满意。

为了让我的故事简短,我使用以下代码来满足这个要求:

private BitmapDescriptor bitmapDescriptorFromVector(Context context, @DrawableRes  int vectorDrawableResourceId) 
    Drawable background = ContextCompat.getDrawable(context, R.drawable.ic_map_pin_filled_blue_48dp);
    background.setBounds(0, 0, background.getIntrinsicWidth(), background.getIntrinsicHeight());
    Drawable vectorDrawable = ContextCompat.getDrawable(context, vectorDrawableResourceId);
    vectorDrawable.setBounds(40, 20, vectorDrawable.getIntrinsicWidth() + 40, vectorDrawable.getIntrinsicHeight() + 20);
    Bitmap bitmap = Bitmap.createBitmap(background.getIntrinsicWidth(), background.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    background.draw(canvas);
    vectorDrawable.draw(canvas);
    return BitmapDescriptorFactory.fromBitmap(bitmap);

还有一个用法示例:

.icon(bitmapDescriptorFromVector(this, R.drawable.ic_car_white_24dp));

注意:您可能希望对向量使用不同的边界,我的向量大小为 24dp,我使用了 48dp png 图像(蓝色部分,也可以是向量)作为背景。

更新:按要求添加屏幕截图。

【讨论】:

我认为这可以解决问题。我会检查这个并标记答案。但是如果你有截图。请分享。 我已经添加了一个截图,我希望澄清最初的要求是什么! ;-) “我的意思是在地图图钉内重用矢量” 我认为内部图像边界的计算应该不同(下面的kotlin实现):val left = (background.intrinsicWidth - vectorDrawable.intrinsicWidth) / 2val top = (background.intrinsicHeight - vectorDrawable.intrinsicHeight) / 3vectorDrawable.setBounds(left, top, left + vectorDrawable.intrinsicWidth, top + vectorDrawable.intrinsicHeight) 它们看起来分辨率很低 @xjcl 我猜有人会这么说! :) 这就是为什么我将这部分添加到我的答案中:“我使用了 48dp png 图像(蓝色部分,也可以是矢量)作为背景"【参考方案3】:

这里是 kotlin 爱好者的代码 ;)

private fun bitMapFromVector(vectorResID:Int):BitmapDescriptor 
    val vectorDrawable=ContextCompat.getDrawable(context!!,vectorResID)
    vectorDrawable!!.setBounds(0,0,vectorDrawable!!.intrinsicWidth,vectorDrawable.intrinsicHeight)
    val bitmap=Bitmap.createBitmap(vectorDrawable.intrinsicWidth,vectorDrawable.intrinsicHeight,Bitmap.Config.ARGB_8888)
    val canvas=Canvas(bitmap)
    vectorDrawable.draw(canvas)
    return BitmapDescriptorFactory.fromBitmap(bitmap)

【讨论】:

你对这个sn-p有什么问题吗?特别是setBounds 部分?【参考方案4】:

可能有点晚了,但这与 Google Maps v2 配合得很好:

public static BitmapDescriptor getBitmapFromVector(@NonNull Context context,
                                                   @DrawableRes int vectorResourceId,
                                                   @ColorInt int tintColor) 

    Drawable vectorDrawable = ResourcesCompat.getDrawable(
            context.getResources(), vectorResourceId, null);
    if (vectorDrawable == null) 
        Log.e(TAG, "Requested vector resource was not found");
        return BitmapDescriptorFactory.defaultMarker();
    
    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, tintColor);
    vectorDrawable.draw(canvas);
    return BitmapDescriptorFactory.fromBitmap(bitmap);

初始化为:

locationMarkerIcon = LayoutUtils.getBitmapFromVector(ctx, R.drawable.ic_location_marker,
                ContextCompat.getColor(ctx, R.color.marker_color));

用法:

googleMap.addMarker(MarkerOptions().icon(getMarkerIcon()).position(latLng));

注意:getMarkerIcon() 只是返回初始化的非空locationMarkerIcon 成员变量。

截图:

【讨论】:

这不是问题所要求的。如果你能看到正确答案附上的截图。 @Shuddh 基本上我的矢量资产在我的屏幕截图中有点不同(基于我的项目),但据我所知,您的原始问题询问如何使用矢量资产作为标记图标和这就是静态方法的作用(同时还动态添加颜色)。 它不是动态添加颜色的。这基本上是内部图标的变化。如果您可以在所选答案中看到屏幕截图 太棒了!就我而言,我摆脱了色调,它起作用了!此外,我建议修改标记的锚点如下(如果你是经典图钉):.anchor(0.5f, 1f);【参考方案5】:

在 Kotlin 中:我使用以下代码在 Marker 上显示 SVG 图像。在这里,我没有使用背景颜色/SVG。

fun getBitmapDescriptorFromVector(context: Context, @DrawableRes vectorDrawableResourceId: Int): BitmapDescriptor? 

    val vectorDrawable = ContextCompat.getDrawable(context, vectorDrawableResourceId)
    val bitmap = Bitmap.createBitmap(vectorDrawable!!.intrinsicWidth, vectorDrawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)
    vectorDrawable.setBounds(0, 0, canvas.width, canvas.height)
    vectorDrawable.draw(canvas)

    return BitmapDescriptorFactory.fromBitmap(bitmap)

这样使用:

googleMap?.addMarker(MarkerOptions().position(LatLng(it.latitude!!, it.longitude!!))
            .title(it.airLineDetails))?.setIcon(
            getBitmapDescriptorFromVector(requireContext(), R.drawable.ic_flight_blue))

屏幕截图:

【讨论】:

【参考方案6】:

如果有人在这里寻找 kotlin 是适合你的方法:

  private fun  bitmapDescriptorFromVector(vectorResId:Int):BitmapDescriptor 
            var vectorDrawable = ContextCompat.getDrawable(requireContext(), vectorResId);
            vectorDrawable!!.setBounds(0, 0, vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight());
            var bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
            var canvas =  Canvas(bitmap);
            vectorDrawable.draw(canvas);
            return BitmapDescriptorFactory.fromBitmap(bitmap);

上述方法会将您的矢量图标转换为位图描述符

map.addMarker(new MarkerOptions()
                .position(latLng)
                .icon(bitmapDescriptorFromVector(getActivity(), R.drawable.your_vector_asset))
                .title(title)

这个用于为您的地图设置标记感谢 Leo Droidcoder 从他的回答中,只有我已将其转换为 kotlin

【讨论】:

【参考方案7】:

将矢量资源转换为位图对象并使用BitmapDescriptorFactory.fromBitmap(bitmap)

   Bitmap bitmap = getBitmapFromVectorDrawable(getContext(),R.drawable.ic_pin);
   BitmapDescriptor descriptor =BitmapDescriptorFactory.fromBitmap(bitmap);
   MarkerOptions markerOptions = new MarkerOptions();
   markerOptions.icon(descriptor);

位图转换器:

 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;
    

【讨论】:

是的,我正在寻找这个答案,它以位图格式返回。 :)【参考方案8】:

试试这个

MarkerOptions op = new MarkerOptions();
op.position(src_latlng);
Marker origin_marker = googleMap.addMarker(op);

Bitmap bitmap = getBitmap(this,R.drawable.ic_map_marker);
origin_marker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));

getBitmap

public Bitmap getBitmap(Context context, int drawableId) 
   Drawable drawable = ContextCompat.getDrawable(context, drawableId);
   if (drawable instanceof BitmapDrawable) 
       return BitmapFactory.decodeResource(context.getResources(), drawableId);
    else if (drawable instanceof VectorDrawable) 
       return getBitmap((VectorDrawable) drawable);
    else 
       throw new IllegalArgumentException("unsupported drawable type");
   

ic_map_marker.xml

<vector android: android:viewportHeight="512.0"
    android:viewportWidth="512.0" android: xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="#f32f00" android:pathData="M288,284.8V480l-64,32V284.8c10.3,2.1 21,3.3 32,3.3S277.7,286.9 288,284.8zM384,128c0,70.7 -57.3,128 -128,128c-70.7,0 -128,-57.3 -128,-128S185.3,0 256,0C326.7,0 384,57.3 384,128zM256,64c0,-17.7 -14.3,-32 -32,-32s-32,14.3 -32,32s14.3,32 32,32S256,81.7 256,64z"/>
</vector>

【讨论】:

能否解释一下vectorDrawable的return语句。您使用的 getBitmap 方法是什么。 另外我不认为这也会给我我想要的标记。因为图像中显示的标记是包含矢量资产的默认标记。但是此代码将根据矢量资产生成图标,但不会在标记中(蓝色部分)。 @Shuddh 您要查找的图标必须是自定义的。矢量资产应定义蓝色和白色汽车部分。然后您将该矢量添加为图标。 @PradumnKumarMahanta 所以我必须为图标背景创建另一个矢量。正确的?但是仍然如何将向量设置为标记? @Shuddh 不,您可以为完整的事物创建一个向量。标记+图像。并使用 Eswar 或 Hiristo Stoyanov 和我建议的方法将该向量添加为标记,两者都对我有用。【参考方案9】:

对于 Kotlin 用户。请检查以下代码。正如我在 Fragment 类中所做的那样。

class MapPinFragment : Fragment() 

    private lateinit var googleMap1: GoogleMap

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
    

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? 
        return inflater.inflate(R.layout.fragment_map_pin, container, false)
    

    override fun onActivityCreated(savedInstanceState: Bundle?) 
        super.onActivityCreated(savedInstanceState)
        mapView.onCreate(savedInstanceState)
        mapView.onResume()

    

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
        super.onViewCreated(view, savedInstanceState)


        mapView.getMapAsync  googleMap ->
            googleMap1 = googleMap as GoogleMap
            addCustomMarker()
        

    

    private fun addCustomMarker() 
        Log.d("addCustomMarker", "addCustomMarker()")
        if (googleMap1 == null) 
            return
        
        // adding a marker on map with image from  drawable
        googleMap1.addMarker(
            MarkerOptions()
                .position(LatLng(23.0225 , 72.5714))
                .icon(BitmapDescriptorFactory.fromBitmap(getMarkerBitmapFromView()))
        )
    

    override fun onDestroy() 
        super.onDestroy()
        if (mapView != null)
            mapView.onDestroy()
    
    override fun onLowMemory() 
        super.onLowMemory()
        mapView.onLowMemory()
    

    private fun getMarkerBitmapFromView(): Bitmap? 
        val customMarkerView: View? = layoutInflater.inflate(R.layout.view_custom_marker, null)
//        val markerImageView: ImageView =
//            customMarkerView.findViewById<View>(R.id.profile_image) as ImageView
        customMarkerView?.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED );
        customMarkerView?.layout(0, 0, customMarkerView.measuredWidth, customMarkerView.measuredHeight);
        customMarkerView?.buildDrawingCache();
        val returnedBitmap = Bitmap.createBitmap(
            customMarkerView!!.measuredWidth, customMarkerView.measuredHeight,
            Bitmap.Config.ARGB_8888
        )
        val canvas = Canvas(returnedBitmap)
        canvas.drawColor(Color.WHITE, PorterDuff.Mode.SRC_IN)
        val drawable = customMarkerView.background

        drawable?.draw(canvas);
        customMarkerView.draw(canvas);
        return returnedBitmap;

    





【讨论】:

以上是关于带有矢量资产图标的 android 谷歌地图中的自定义标记的主要内容,如果未能解决你的问题,请参考以下文章

我的颤振资产图像没有被用作谷歌地图的标记图标

Android 地图:如何使用 SVG 文件自定义标记图标?

为啥我的自定义 Google 地图图标在 PC 浏览器中显示,但在 Android 浏览器中不显示?

谷歌地图上的自定义标记

谷歌地图如何在自定义地点显示我的位置标记?

如何在 Android 应用程序中使用自己的自定义图像而不是谷歌地图