具有矢量资产图标的android中的Google地图中的自定义标记


75

我们如何通过矢量资产文件来实现地图标记图标,即Google如何以编程方式将其显示为:

地图

更新:

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

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

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


@RishabhMahatha我可以很容易地用png来做,但是问题是png是很重的文件。我必须将其存储在我的apk中,或者必须从服务器中调用它。但是我特别需要从矢量资产文件中获取它。
Shuddh

@LucaNicoletti我必须使用矢量素材(或SVG)文件来实现。而且我没有任何库或方法来执行此操作。
Shuddh

任何人?发现任何东西..
Shuddh

1
也可以在以下答案中找到:stackoverflow.com/questions/33696488/…–
Erfan

Answers:


39

我一直在寻找完全相同的要求,看到这个问题起初让我很高兴,但与@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图像(蓝色部分,也可以是向量)作为背景。

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

最终结果的屏幕截图


我认为这可以解决问题。我会检查并标记答案。但是,如果您有屏幕截图。请分享。
Shuddh

我添加了一个屏幕截图,我希望澄清一下最初的要求!;-)“我的意思是Map销内的重用向量”
SidMorad

2
我认为内部图像范围的计算应该有所不同(以下Kotlin实现): val left = (background.intrinsicWidth - vectorDrawable.intrinsicWidth) / 2 val top = (background.intrinsicHeight - vectorDrawable.intrinsicHeight) / 3 vectorDrawable.setBounds(left, top, left + vectorDrawable.intrinsicWidth, top + vectorDrawable.intrinsicHeight)
gswierczynski

他们看起来真的很低分辨率
xjcl

@xjcl我确实猜到有人会这么说!:)这就是为什么我将这一部分添加到我的答案中:“我使用了48dp png图像(蓝色部分,也可以是矢量)作为背景”
SidMorad

114

您可以使用以下方法:

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

编辑
在科特林它可能看起来像:

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

1
你有试过吗 我已经有一段时间没有看到此代码了!
Shuddh

当然,我用它在我的项目
狮子座Droidcoder

这仍然可以解决我的问题!您的方法给我的只是那个汽车图标,而不是蓝色背景。我需要保留该背景并加上该图标。
Shuddh

您是说汽车图标是与图钉分开的图标吗?
Leo Droidcoder

2
在我的项目中用它来从矢量资产中获取标记。
anoo_radha

5

可能会晚一点,但这在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()只返回初始化的非nulllocationMarkerIcon成员变量。

屏幕截图:

在此处输入图片说明


1
这不是问题所要的。如果您可以看到正确答案所附的屏幕截图。
Shuddh

1
@Shuddh基本上我的矢量资产在我的屏幕快照中有所不同(基于我的项目),但据我所知,您最初的问题是询问如何将矢量资产用作标记图标,这就是静态方法的作用(以及并为其动态添加颜色)。
prerak

它不是动态添加颜色。基本上是内部图标的更改。如果您可以在所选答案中看到屏幕截图
Shuddh

晚饭!就我而言,我摆脱了色彩的困扰,它起作用了!另外,我建议按照以下方式修改标记的锚点(如果您的那是经典的图钉):.anchor(0.5f, 1f);
russellhoff

5

这是科特林爱好者的代码;)

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

2
您对这个摘要有任何疑问吗?特别是setBounds一部分?
拉维

2

将矢量资源转换为位图对象并使用 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;
    }

1
是的,我一直在寻找这个答案,它以位图格式返回。:)
Ramkesh Yadav

1

如果有人在科特林寻找住宿,这里就是您的理想选择:

  private fun  bitmapDescriptorFromVector(context: Context, vectorResId:Int):BitmapDescriptor {
            var vectorDrawable = ContextCompat.getDrawable(context, 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);
}

上面的方法会将您的矢量图标转换为bitmapdescritor

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

这是一个为您的地图设置标记的工具,感谢Leo Droidcoder从他的回答中得到的,我已经将其转换为kotlin


0

在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))

屏幕截图:


-1

尝试这个

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:height="32dp" android:viewportHeight="512.0"
    android:viewportWidth="512.0" android:width="32dp" 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返回语句。您正在使用什么getBitmap方法。
Shuddh

另外,我认为这也不会给我我想要的标记。因为图像中显示的标记是包含矢量资产的默认标记。但是此代码将根据矢量资产生成图标,但不会在标记(蓝色部分)中生成图标。
Shuddh

@Shuddh您要查找的图标必须是自定义的。矢量资产应定义蓝色和白色汽车零件。然后,您将矢量添加为图标。
Pradumn Kumar Mahanta

@PradumnKumarMahanta所以我必须为图标背景创建另一个向量。对?但是仍然如何设置向量作为标记?
Shuddh

@Shuddh不,您可以为完整的对象创建向量。标记+图片。然后使用Eswar或Hiristo Stoyanov和我建议的方法添加该向量作为标记,这两种方法都对我有用。
Pradumn Kumar Mahanta

-1

对于Kotlin用户,请检查以下代码。

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;

    }




}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.