1.背景
OsmAnd 使用的矢量地图
osmdroid 使用的 栅格地图(图片展示的方式)
瓦片地图的前世今生
本文是探究 OsmAnd -- 矢量地图 渲染底图的方式
2.基础知识
Android中的SurfaceView使用简介
3. 源码分析
3.1 准备图片
绘制入口 mapView.refreshMap(true);
// this method could be called in non UI thread
public void refreshMap(final boolean updateVectorRendering) {
//此处的 view 是Activity里的 OsmAndMapSurfaceView
if (view != null && view.isShown()) {
boolean nightMode = application.getDaynightHelper().isNightMode();
Boolean currentNightMode = this.nightMode;
boolean forceUpdateVectorDrawing = currentNightMode != null && currentNightMode != nightMode;
if (forceUpdateVectorDrawing) {
resetDefaultColor();
}
this.nightMode = nightMode;
DrawSettings drawSettings = new DrawSettings(nightMode, updateVectorRendering || forceUpdateVectorDrawing);
sendRefreshMapMsg(drawSettings, 20);
refreshBufferImage(drawSettings);
}
}
private void refreshBufferImage(final DrawSettings drawSettings) {
if (mapRenderer != null) {
return;
}
if (!baseHandler.hasMessages(BASE_REFRESH_MESSAGE) || drawSettings.isUpdateVectorRendering()) {
Message msg = Message.obtain(baseHandler, new Runnable() {
@Override
public void run() {
baseHandler.removeMessages(BASE_REFRESH_MESSAGE);
try {
DrawSettings param = drawSettings;
Boolean currentNightMode = nightMode;
if (currentNightMode != null && currentNightMode != param.isNightMode()) {
param = new DrawSettings(currentNightMode, true);
resetDefaultColor();
}
if (handler.hasMessages(MAP_FORCE_REFRESH_MESSAGE)) {
if (!param.isUpdateVectorRendering()) {
param = new DrawSettings(drawSettings.isNightMode(), true);
}
handler.removeMessages(MAP_FORCE_REFRESH_MESSAGE);
}
refreshBaseMapInternal(currentViewport.copy(), param);
//此处绘制 surfaceView,将准备好的 Bitmap 绘制到 surfaceView 上
sendRefreshMapMsg(param, 0);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
});
msg.what = drawSettings.isUpdateVectorRendering() ? MAP_FORCE_REFRESH_MESSAGE : BASE_REFRESH_MESSAGE;
//baseHandler 是一个异步线程 handler
baseHandler.sendMessage(msg);
}
}
执行方法为 refreshBufferImage
--> refreshBaseMapInternal
private void refreshBaseMapInternal(RotatedTileBox tileBox, DrawSettings drawSettings) {
long start = SystemClock.elapsedRealtime();
final QuadPoint c = tileBox.getCenterPixelPoint();
Canvas canvas = new Canvas(bufferBitmapTmp);
fillCanvas(canvas, drawSettings);
//layer 是具体执行绘制的类
for (int i = 0; i < layers.size(); i++) {
try {
OsmandMapLayer layer = layers.get(i);
canvas.save();
// rotate if needed
if (!layer.drawInScreenPixels()) {
canvas.rotate(tileBox.getRotate(), c.x, c.y);
}
//所有绘图的逻辑入口
layer.onPrepareBufferImage(canvas, tileBox, drawSettings);
canvas.restore();
} catch (IndexOutOfBoundsException e) {
// skip it
canvas.restore();
}
}
Bitmap t = bufferBitmap;
synchronized (this) {
bufferImgLoc = tileBox;
bufferBitmap = bufferBitmapTmp;
bufferBitmapTmp = t;
}
long end = SystemClock.elapsedRealtime();
additional.calculateFPS(start, end);
}
上述代码执行完后即准备好地图底图,具体的绘制逻辑在 layer.onPrepareBufferImage
3.2 将图片显示在界面上
在 refreshBaseMapInternal
后会执行 sendRefreshMapMsg(param, 0);
,
private void sendRefreshMapMsg(final DrawSettings drawSettings, int delay) {
if (!handler.hasMessages(MAP_REFRESH_MESSAGE) || drawSettings.isUpdateVectorRendering()) {
Message msg = Message.obtain(handler, new Runnable() {
@Override
public void run() {
DrawSettings param = drawSettings;
handler.removeMessages(MAP_REFRESH_MESSAGE);
refreshMapInternal(param);
}
});
msg.what = MAP_REFRESH_MESSAGE;
//该handler为主线程handler
if (delay > 0) {
handler.sendMessageDelayed(msg, delay);
} else {
handler.sendMessage(msg);
}
}
}
该 msg 会 执行 refreshMapInternal
-->drawOverMap
,drawOverMap 即是将 3.1 准备的 bitmap 绘制到 view 上。
public void drawOverMap(Canvas canvas, RotatedTileBox tileBox, DrawSettings drawSettings) {
if (mapRenderer == null) {
fillCanvas(canvas, drawSettings);
}
final QuadPoint c = tileBox.getCenterPixelPoint();
synchronized (this) {
if (bufferBitmap != null && !bufferBitmap.isRecycled() && mapRenderer == null) {
canvas.save();
canvas.rotate(tileBox.getRotate(), c.x, c.y);
//将 bufferBitmap 绘制到 surfaceView 上
drawBasemap(canvas);
canvas.restore();
}
}
if (onDrawMapListener != null) {
onDrawMapListener.onDrawOverMap();
}
for (int i = 0; i < layers.size(); i++) {
try {
OsmandMapLayer layer = layers.get(i);
canvas.save();
// rotate if needed
if (!layer.drawInScreenPixels()) {
canvas.rotate(tileBox.getRotate(), c.x, c.y);
}
if (mapRenderer != null) {
layer.onPrepareBufferImage(canvas, tileBox, drawSettings);
}
layer.onDraw(canvas, tileBox, drawSettings);
canvas.restore();
} catch (IndexOutOfBoundsException e) {
// skip it
}
}
if (showMapPosition || animatedDraggingThread.isAnimatingZoom()) {
drawMapPosition(canvas, c.x, c.y);
} else if (multiTouchSupport.isInZoomMode()) {
drawMapPosition(canvas, multiTouchSupport.getCenterPoint().x, multiTouchSupport.getCenterPoint().y);
} else if (doubleTapScaleDetector.isInZoomMode()) {
drawMapPosition(canvas, doubleTapScaleDetector.getCenterX(), doubleTapScaleDetector.getCenterY());
}
}
3.3 矢量地图绘制原理总结
OsmAnd 的底图是由一层层 layer
绘制而成,先准备好 bitmap ,通过 drawBasemap(canvas);
来完成最终的展示