一. 线左右绘制文字?
当初项目由于google卫星地图的强大
项目用了osm。绘制这个很是方便:
Marker typesMarker = new Marker(mMapView);
typesMarker.setPanToView(false);
typesMarker.setOnMarkerClickListener((marker, mapView) -> false);
//设置显示位置
typesMarker.setPosition(mGeoPoint);
//设置文字颜色
typesMarker.setTextLabelFontSize(35);
//显示文字
typesMarker.setTextIcon("文字即可");
//文字背景颜色 typesMarker.setTextLabelBackgroundColor(Color.parseColor("#A9F8F4F4"));
//偏移
typesMarker.setAnchor(0.9f, 1.2f);
//是否显示
typesMarker.setVisible(btn_Length_isCheck);
//设置id参数
typesMarker.setId(String.valueOf(line.getId()));
//旋转角度
typesMarker.setRotation(rotation(lng1, lng2));
//设置对象
typesMarker.setRelatedObject(line);
1.TextOptions绘制文字
2020年的10月份,突然发现google国内禁用来的很突然,我们进行更换百度地图。百度地图有API:
- 设置文字浮动、很开心的进行中,发现文字没法进行偏移,
线和文字重合
。当然了也忙乎了几分钟,也就发现了align(,)但是不管如何设置都不起作用。后者想到对.position进行偏移。但是经纬度的偏移会导致缩放地图时候变换位置太大,飘忽不定很难达到效果。
return TextOptions()
.text(avoidNullString(line.types)) //文字内容
.bgColor(Color.parseColor("#FFFFFF")) //背景色
.fontSize(21) //字号
.fontColor(Color.BLACK) //文字颜色
.visible(whetherToDisplayThePitch)
.position(LatLng(latLng.latitude, latLng.longitude))
//死活设置不起作用.....
.align(-1111,111)
.rotate(rotation(pointStar, pointEnd))
2. marker的强大
放弃了TextOptions,在OSM中使用过Marker真的是无所不能。
所以自然想到从Marker进行尝试。Marker解决了我的问题:
1.MarkerOptions提供了icon(bitmapdescriptor BitmapDescriptor)。我们通过view进行绘制需要的View以及Text等。
2.anchor(0.3f, 1f)设置偏移即可。3.View是水平方向我们需要计算旋转角度。这个应该不难吧?
/**
* @param
* @line 线
* @whetherToDisplayThePitch 是否显示types
* @context
* 获取线上面的marker
*/
fun getLineTypeMarker(line: Line, whetherToDisplayThePitch: Boolean, context: Context): OverlayOptions {
val weekContext= WeakReference(context)
val pointStar = com.google.android.gms.maps.model.LatLng(stringToDouble(line.latA), stringToDouble(line.lonA))
val pointEnd = com.google.android.gms.maps.model.LatLng(stringToDouble(line.latB), stringToDouble(line.lonB))
val latLng = LatLng((pointStar.latitude + pointEnd.latitude) / 2, (pointStar.longitude + pointEnd.longitude) / 2)
val viewImag = View.inflate(weekContext.get(), R.layout.activity_bd_map_line_types, null)
viewImag.line_types_name.text=avoidNullString(line.types)
val mCurrentMarker =BitmapDescriptorFactory.fromView(viewImag)
return MarkerOptions()
.anchor(0.3f, 1f)
.icon(mCurrentMarker)
.visible(whetherToDisplayThePitch)
.position(LatLng(latLng.latitude, latLng.longitude))
.rotate(rotation(pointStar, pointEnd))
}
/**
* 计算两个坐标之间的角度
*
* @param start 起点
* @param end 终点
*/
public static float rotation(LatLng start, LatLng end) {
//方向一致化
LatLng swap;
if (end.longitude - start.longitude < 0) {
swap = end;
end = start;
start = swap;
}
double angle = Math.atan2((end.latitude - start.latitude), (end.longitude - start.longitude));
return (float) (angle * (180 / Math.PI));
}
经过了上门的分析,API提供了icon(..)只要有bitmap那就可以绘制出来。我们不防找个图片进行加载看看。
只要你的view够复杂,基本都可以进行绘制上去的,过度的加载图片会引起OOM。所以常见的那些头像和个人信息大家应该知道如何加载了吧。
二、图层显示隐藏
地图上文字和Marker的隐藏与显示还有人通过重新加载Option进行刷新么?今天切换百度过程中发现单独的操作MarkerOptions是不起作用的,绘制过程我将MarkerOptions储存在了集合中,然后通过按钮来控制:
var markerOption=MarkerOptions().clickable(false)
.anchor(0.3f, 1f)
.icon(mCurrentMarker)
.visible(whetherToDisplayThePitch)
.position(LatLng(latLng.latitude, latLng.longitude))
.rotate(rotation(pointStar, pointEnd))
//档距图层
private val polylineLengthLinkedList: LinkedList<MarkerOption> = LinkedList()
polylineLengthLinkedList.add(markerOption)
按钮控制隐藏与显示
//档距的显示与隐藏
R.id.map_btn_length -> {
//档距显示
WHETHER_TO_SHOW_SPAN = !WHETHER_TO_SHOW_SPAN
for (i in polylineLengthLinkedList.indices) {
polylineLengthLinkedList[i].isVisible=WHETHER_TO_SHOW_SPAN
}
}
运行最后并不能控制MarkerOption的显示与隐藏。从开始就错误,MarkerOption只是覆盖物Overlay提供设置的属性类,并非覆盖物本身,所以操作是没有效果的。最后我们真确代码应该是储存覆盖物Overlay在集合进行操作即可。
//绘制线路上面的类型Mark 构建TextOptions对象
//获取MarkerOption
val lineTypeMarkerOption = LineBDUtils.getLineTypeMarker(line, WHETHER_TO_SHOW_MERK, this)
//给地图上初始化覆盖物并添加到地图上
val overlay= baiduMap.addOverlay(lineTypeMarkerOption)
//将初始化的覆盖物储存到本地集合,后面进行控制隐藏与显示
polylineTypesLinkedList.add(overlay)
三、线的点击和自定义弹窗以及显示问题
自定义弹窗
和设置弹窗跟随线
以及多弹窗控制显示
问题。
1.线的点击
-
在OSM中用Polyline可以给每条线进行设置单独的点击事件 Polyline给每个线设置点击事件
polyline.setOnClickListener((polyline, mapView, eventPos) -> { }
-
百度地图API我们没发现。但在BaiduMap中提供了监听,且返回了Polyline:
接下来我们进行设置监听:
val listener = BaiduMap.OnPolylineClickListener {polyline->
/**地图 Polyline 覆盖物点击事件监听函数
* @param polyline 被点击的 polyline*/
Log.e(TAG, "setLineInfoWindow: " + it.extraInfo.toString())
true //是否捕获点击事件
}
//设置地图 Polyline 覆盖物点击事件监听
baiduMap.setOnPolylineClickListener(listener)
点击线打印:因为line里面toString我returen的是name变量所以打印结果比较短而不是所以变量的值。
BDMapActivity: setLineInfoWindow: Bundle[{id=3, line=线路2#}]
BDMapActivity: setLineInfoWindow: Bundle[{id=1, line=线路1#}]
2.点击线出现弹窗
- 自定义弹窗,且点击线出现在线的中间。
上面我们已经获取到线的点击事件且能获取到点击的线那么我们在点击事件里面进行初始化弹窗。百度地图中有InfoWindow那么我们就用它来作为突破口。
构造参数中有三中初始化方式,我们也发现可以搞图片。接下来我们通过最简单的构造参数在线的监听回调里面进行定义。LatLng
作为弹窗的位置,View自定义布局呗,int偏移。
我们进行计算线的终点,这个简单吧(lat,long)=((lat1+lat2)/2,(long1+long2)/2)。我们电线杆之间的距离不到20米所以几乎看作是直线计算,你要是跨洲计算肯定不行....。
val listener = BaiduMap.OnPolylineClickListener {
/**地图 Polyline 覆盖物点击事件监听函数
* @param polyline 被点击的 polyline*/
Log.e(TAG, "setLineInfoWindow: " + it.extraInfo.toString())
val linMark: Line = it.extraInfo.getParcelable("line")!!
//计算中点
val latLng = LatLng((linMark.latA.toDouble() + linMark.latB.toDouble()) / 2, (linMark.lonA.toDouble() + linMark.lonB.toDouble()) / 2)
val view: View = LayoutInflater.from(this).inflate(R.layout.layout_line_dialog, null)
//获取view
val shanchu: TextView = view.findViewById(R.id.map_tv_shanchu)
val quxiao: TextView = view.findViewById(R.id.map_tv_quxiao)
val inforWindow = InfoWindow(view, latLng, 10)
yidong.text = "插入"
//TODO 线的删除
shanchu.setOnClickListener { v: View? ->
baiduMap.hideInfoWindow()
val views = View.inflate(this, R.layout.activity_map_dialog, null)
val dialog = AlertDialog.Builder(this).create()
dialog.setView(views)
dialog.show()
val map_btn_quxiao = views.findViewById<Button>(R.id.map_btn_quxiao)
val map_btn_queding = views.findViewById<Button>(R.id.map_btn_queding)
map_btn_queding.setOnClickListener { view12: View? ->
dialog.dismiss()
removeLineMarker(linMark)
App.getInstances().daoSession.lineDao.delete(linMark)
}
map_btn_quxiao.setOnClickListener { view1: View? -> dialog.dismiss() }
}
//弹窗的展示。
baiduMap.showInfoWindow(inforWindow)
true //是否捕获点击事件
}
3.线的删除
- 删除线,同样线也是覆盖物Overler所以储存在集合中,点击时候监听返回的polylin和本地集合做对比。如果一样进行删除集合和覆盖物即可
Overlay.remove()
/**
*line监听事件返回的线
*/
private fun removeLineMarker(line: Line) {
//删除地图上线档距和类型
for (i in polylineLinkedList.indices.reversed()) {
val linOverlay = polylineLinkedList[i]
val lineOfLink: Line = linOverlay.extraInfo.getParcelable("line")!!
if (lineOfLink == line) {
//删除集合中的线图层
polylineLinkedList.remove(linOverlay)
//删除地图上点图层
linOverlay.remove()
}
}
}
点击运行效果:
四、覆盖物关联
我们这里可以看到百度地图里面定义的覆盖物。且如果你仔细看API会发现都提供了携带Bundle
,因为不同的图层我们可以设置bundle数据
,类或者id等都可以,这让图层之间可以很好的建立关系,因为bundle使得外界的逻辑可以很好的进行操控覆盖物。
例如我点击一个图层,与这个图层相关的其他图层会变为其他样式或者增加颜色背景等。大家应该看到过地图的点击绘制吧。如果你有时间可以自己很好的体验一把。
我们只需要在创建覆盖物时候设置相同的数据即可。当我们点击覆盖物时候我们可以找到数据bundle中一致的覆盖物。是否明白?
//绘制线路上面的类型Mark 构建TextOptions对象
val lineTypeMarker = LineBDUtils.getLineTypeMarker(line, WHETHER_TO_SHOW_MERK, this)
val overlay = baiduMap.addOverlay(lineTypeMarker)
val bundle: Bundle = getBundleFromLine(line)
//🌟 设置数据标记
overlay.extraInfo=bundle
polylineTypesLinkedList.add(overlay)
fun getBundleFromLine(line: Line): Bundle {
val bundle = Bundle()
bundle.putParcelable("line", line)
bundle.putLong("id", line.id)
return bundle
}
项目中我删除线段也删除了对应的文字。这个就是相关覆盖物我bundle设置了同样的line类,所以在点击选中线时候,我能很快的覆盖物集合中找到相关联的覆盖物就行对应的删除操作。ok么说的。
五、摆脱Marker偏移量的约束
上图中我们可以看到1、2、3
有图片有文字距离中间杆塔图片有角度有距离。我们如何进行绘制围绕着这个点绘制像3
所指向这样的图片呢。1、2
无非是MarkerOption的rotate与角度的计算。但是3
我们发现它和杆塔直接有些间距。在进行绘制过程中我并不能通过anchor
设置很大的范围,最后通过图片来进行解决,我们可以将自己的文字ps中贴到图片中或者图片留有空白透明背景。留出巨大的空间,让显示的范围尽可能大,从而通过旋转和anchor进行调整。这样H的左上方成了透明站位。右下角的H可调整空间变大。
变为下面