Android MapView覆盖,绘制时缓存位图
我有一个覆盖图,可以在我的Android MapView覆盖,绘制时缓存位图,android,caching,overlay,android-mapview,android-canvas,Android,Caching,Overlay,Android Mapview,Android Canvas,我有一个覆盖图,可以在我的地图视图上绘制路径,但我注意到它每秒被不必要地重画十次。 由于在draw方法中,我绘制了路径的每一段,因此这很容易成为一个效率问题 出于这个原因,我决定缓存覆盖的内容,并在必要时(即路径更改、地图中心移动或缩放级别更改时)实际重新绘制它 现在,draw方法的参数之一是要绘制的Canvas。我知道如何在上面绘制缓存的位图,问题是我不知道如何在位图上缓存画布的内容。 我不能实例化一个新画布,也不能调用setBitmap,因为hardwareCavas中的画布会抛出一个Uns
地图视图上绘制路径,但我注意到它每秒被不必要地重画十次。
由于在draw
方法中,我绘制了路径的每一段,因此这很容易成为一个效率问题
出于这个原因,我决定缓存覆盖的内容,并在必要时(即路径更改、地图中心移动或缩放级别更改时)实际重新绘制它
现在,draw
方法的参数之一是要绘制的Canvas
。我知道如何在上面绘制缓存的位图,问题是我不知道如何在位图上缓存画布的内容。
我不能实例化一个新画布,也不能调用setBitmap
,因为hardwareCavas
中的画布会抛出一个UnsupportedOperationException
,如果调用该方法
总而言之,我有一个画布和一个位图,如何将画布的内容复制到位图
编辑
为了清晰起见,这是我的draw方法,我不手动调用它,但它仍然会被重复调用,即使地图根本没有移动
public void draw(Canvas canvas, MapView map, boolean shadow) {
if (shadow) {
// this overlay doesn't need to draw shadows
return;
}
if (paths.isEmpty()) {
// nothing to draw
return;
}
center = map.getMapCenter();
zoomLevel = map.getZoomLevel();
map.getDrawingRect(bounds);
projection = map.getProjection();
maxLevel = map.getMaxZoomLevel();
for (MapPath mp : paths) {
// adjust path width to current zoom
adjustedWidth = mp.getWidth() * zoomLevel / maxLevel;
if (adjustedWidth < MIN_WIDTH_TO_DRAW) {
// path is too thin, avoid drawing it
continue;
}
paint.setStrokeWidth(adjustedWidth);
paint.setColor(mp.getColor());
state = PathState.FIRST_POINT;
path.reset();
for (PathPoint pp : mp.getPoints()) {
if (!pp.shoudAppearAtZoomLevel(zoomLevel)) {
// do not draw this point at this zoom level
continue;
}
// project a geopoint to a pixel
projection.toPixels(pp.getGeoPoint(), point);
inside = isInsideBounds(point, map);
switch (state) {
case FIRST_POINT:
// move to starting point
firstX = point.x;
firstY = point.y;
path.moveTo(firstX, firstY);
break;
case WAS_INSIDE:
// segment is completely or partially on map
path.lineTo(point.x, point.y);
break;
case WAS_OUTSIDE:
if (inside) {
// segment is partially on map
path.lineTo(point.x, point.y);
} else {
// segment is completely off map
path.moveTo(point.x, point.y);
}
break;
}
// update state
state = inside ? PathState.WAS_INSIDE : PathState.WAS_OUTSIDE;
}
// workaround to avoid canvas becoming too big when path is mostly off screen
path.moveTo(firstX, firstY);
// draw this path to canvas
canvas.drawPath(path, paint);
}
super.draw(canvas, map, shadow);
}
public void draw(画布、地图视图、布尔阴影){
如果(阴影){
//此覆盖不需要绘制阴影
返回;
}
if(path.isEmpty()){
//没什么可画的
返回;
}
center=map.getMapCenter();
zoomLevel=map.getZoomLevel();
map.getDrawingRect(边界);
projection=map.getProjection();
maxLevel=map.getMaxZoomLevel();
用于(映射路径mp:路径){
//将路径宽度调整为当前缩放
adjustedWidth=mp.getWidth()*zoomLevel/maxLevel;
如果(调整宽度<最小宽度到绘图){
//路径太细,请避免绘制它
继续;
}
油漆。设置行程宽度(调整宽度);
paint.setColor(mp.getColor());
state=PathState.FIRST_点;
path.reset();
for(路径点pp:mp.getPoints()){
如果(!pp.shoudAppearAtZoomLevel(zoomLevel)){
//不要在此缩放级别绘制此点
继续;
}
//将地质点投影到像素
toPixels(pp.getGeoPoint(),point);
内部=isInsideBounds(点、地图);
开关(状态){
案例第一点:
//移到起点
firstX=点x;
firstY=点y;
path.moveTo(firstX,firstY);
打破
案情如下:
//段完全或部分在地图上
lineTo路径(点x、点y);
打破
案件发生在:
如果(内部){
//部分路段在地图上
lineTo路径(点x、点y);
}否则{
//这个片段完全偏离了地图
路径移动到(点x,点y);
}
打破
}
//更新状态
state=内部?PathState.WAS\u内部:PathState.WAS\u外部;
}
//解决方法,以避免画布在路径大部分在屏幕外时变得过大
path.moveTo(firstX,firstY);
//将此路径绘制到画布
画布.绘制路径(路径,绘制);
}
超级。绘制(画布、地图、阴影);
}
无法将位图移到绘制Mapview
画布的位置
方法应如下:
- 首先,创建自己的(空且透明)位图,其大小与
MapView
画布相同
- 然后为位图创建WIN画布(此画布是用于绘制位图的绘图工具),并使用它绘制路径
- 最后,将位图(已绘制路径)绘制到
MapView
画布
但是,您提到的性能/效率问题可能是由于现有解决方案的设计不正确造成的。在中等范围的设备中,我可以在大约3毫秒的时间内,不使用位图(并且有一些不使用位图的好理由)绘制具有10.000个点的路径
在我对这篇文章的回答中,有一些关于如何处理它的提示:。也可以在同一帖子中查看@shkschneider的答案
--编辑--
仅仅通过查看代码,我无法理解为什么您会收到此警告。。。但我们正在使它变得更加复杂
按以下方式组织代码:
绘制
draw()
方法仅检查缩放是否有变化(如果有,请重新生成路径)以及贴图是否已移动(如果有偏移路径),并最终绘制路径
@Override
public void draw(Canvas canvas, MapView mapview, boolean shadow) {
super.draw(canvas, mapview, shadow);
if(shadow) return;
if(mp.getPoints() == null || mp.getPoints().size() < 2) return;
Projection projection = mapview.getProjection();
int lonSpanNew = projection.fromPixels(0,mapview.getHeight()/2).getLongitudeE6() -
projection.fromPixels(mapview.getWidth(),mapview.getHeight()/2).getLongitudeE6();
if(lonSpanNew != pathInitialLonSpan)
pathBuild();
else{ //check if path need to be offset
projection.toPixels(mp.getPoints().get(0), p1);
if(p1.x != pathInitialPoint.x || p1.y != pathInitialPoint.y){
path.offset(p1.x - pathInitialPoint.x, p1.y - pathInitialPoint.y);
pathInitialPoint.x = p1.x;
pathInitialPoint.y = p1.y;
}
}
canvas.drawPath(path, paint);
}
某些对象(即p1、pPrev等)在类级别定义,以避免每次Methodos运行时创建新对象
注意:
我已经更改了变量名,以适合您正在使用的变量名。我希望我没有犯任何错误,但你应该能找到答案
尊敬。我的地图没有任何慢下来的问题,我非常确定我在解决方案的设计方面做得很好(我遵循了许多最佳实践,还包括了一些优化)只是我认为缓存结果路径比无限每秒重新计算十次更有效。。。但是,您是否建议避免使用位图?我可以问你为什么吗?我建议你重新检查最佳实践。。。首先,仅当地图移动/缩放或根据您的请求(不是每秒10次)时才绘制覆盖图,其次,您可以将路线缓存在路径上(虽然绘制时比位图慢一点,但它使用的内存要少很多,并且需要在缩放更改时重新计算,而不是在地图移动时)。使用位图的缺点是:1-它们需要
private void pathBuild(){
path.rewind();
if(mp.getPoints() == null || mp.getPoints().size() < 2) return;
Projection projection = mapView.getProjection();
pathInitialLonSpan = projection.fromPixels(0,mapView.getHeight()/2).getLongitudeE6() -
projection.fromPixels(mapView.getWidth(),mapView.getHeight()/2).getLongitudeE6();
projection.toPixels(mp.getPoints().get(0), pathInitialPoint);
path.moveTo(pathInitialPoint.x,pathInitialPoint.y);
for(int i=1; i<mp.getPoints().size(); i++){
projection.toPixels(mp.getPoints().get(i), p1);
int distance2 = (pPrev.x - p1.x) * (pPrev.x - p1.x) + (pPrev.y - p1.y) * (pPrev.y - p1.y);
if(distance2 > 9){
path.lineTo(p1.x,p1.y);
pPrev.set(p1.x, p1.y);
}
}