osmdroid API解读(十五)
osmdroid-android MapView解析
public class MapView extends ViewGroup implements IMapView, MapViewConstants,
MultiTouchObjectCanvas<Object> {
// ===========================================================
// Constants
// ===========================================================
private static final double ZOOM_SENSITIVITY = 1.0;//缩放敏感度
private static final double ZOOM_LOG_BASE_INV = 1.0 / Math.log(2.0 / ZOOM_SENSITIVITY);
private static Method sMotionEventTransformMethod;
// ===========================================================
// Fields
// ===========================================================
private double mZoomLevel = 0;//当前的缩放等级
private OverlayManager mOverlayManager;//图层管理者,用于管理多个Overlay
protected Projection mProjection;//当前的投影,用于设备屏幕坐标系与地理坐标系之间的转换
private TilesOverlay mMapOverlay;//瓦片Overlay,用作底图
private final GestureDetector mGestureDetector;//手势检测
private final Scroller mScroller;//处理地图滚动
protected boolean mIsFlinging;//地图的Flinging状态
protected final AtomicReference<Double> mTargetZoomLevel = new AtomicReference<>();
protected final AtomicBoolean mIsAnimating = new AtomicBoolean(false);
protected Integer mMinimumZoomLevel;//最小缩放等级
protected Integer mMaximumZoomLevel;//最大缩放等级
private final MapController mController;//地图控制器,如缩放、平移等
private MultiTouchController<Object> mMultiTouchController;//多点触摸控制
protected float mMultiTouchScale = 1.0f;
protected PointF mMultiTouchScalePoint = new PointF();
protected MapListener mListener;//地图状态监听,缩放和平移
private float mapOrientation = 0;//地图旋转角度
private final Rect mInvalidateRect = new Rect();
protected BoundingBox mScrollableAreaBoundingBox;
protected RectL mScrollableAreaLimit;
private MapTileProviderBase mTileProvider;//瓦片提供者,用于底图的数据获取
private Handler mTileRequestCompleteHandler;//瓦片数据请求后,结果处理
private boolean mTilesScaledToDpi = false;
final Point mRotateScalePoint = new Point();
private final Point mLayoutPoint = new Point();
private final PointL mMercatorPoint = new PointL();
// 地图布局监听
private final LinkedList<OnFirstLayoutListener> mOnFirstLayoutListeners = new LinkedList<MapView.OnFirstLayoutListener>();
/* becomes true once onLayout has been called for the first time i.e. map is ready to go. */
private boolean mLayoutOccurred = false;
private GeoPoint mCenter;
private long mMapScrollX;
private long mMapScrollY;
private ZoomButtonsOverlay mZoomButtonsOverlay;
//布局监听接口
public interface OnFirstLayoutListener {
/**
* this generally means that the map is ready to go
* @param v
* @param left
* @param top
* @param right
* @param bottom
*/
void onFirstLayout(View v, int left, int top, int right, int bottom);
}
// ===========================================================
// Constructors
// ===========================================================
public MapView(final Context context,
MapTileProviderBase tileProvider,
final Handler tileRequestCompleteHandler, final AttributeSet attrs) {
this(context, tileProvider, tileRequestCompleteHandler, attrs, Configuration.getInstance().isMapViewHardwareAccelerated());
}
public MapView(final Context context,
MapTileProviderBase tileProvider,
final Handler tileRequestCompleteHandler, final AttributeSet attrs, boolean hardwareAccelerated) {
super(context, attrs);
...
}
//xml布局
public MapView(final Context context, final AttributeSet attrs) {
this(context, null, null, attrs);
}
public MapView(final Context context) {
this(context, null, null, null);
}
public MapView(final Context context,
final MapTileProviderBase aTileProvider) {
this(context, aTileProvider, null);
}
public MapView(final Context context,
final MapTileProviderBase aTileProvider,
final Handler tileRequestCompleteHandler) {
this(context, aTileProvider, tileRequestCompleteHandler,
null);
}
// ===========================================================
// Getter & Setter
// ===========================================================
...
//获取当前屏幕的边界
public Rect getScreenRect(final Rect reuse) {
final Rect out = getIntrinsicScreenRect(reuse);
if (this.getMapOrientation() != 0 && this.getMapOrientation() != 180) {
GeometryMath.getBoundingBoxForRotatatedRectangle(out, out.centerX(), out.centerY(),
this.getMapOrientation(), out);
}
return out;
}
public Rect getIntrinsicScreenRect(final Rect reuse) {
final Rect out = reuse == null ? new Rect() : reuse;
out.set(0, 0, getWidth(), getHeight());
return out;
}
...
//设置该点为屏幕坐标系的中间位置
@Deprecated
void setMapCenter(final IGeoPoint aCenter) {
getController().animateTo(aCenter);
}
@Deprecated
void setMapCenter(final int aLatitudeE6, final int aLongitudeE6) {
setMapCenter(new GeoPoint(aLatitudeE6, aLongitudeE6));
}
@Deprecated
void setMapCenter(final double aLatitude, final double aLongitude) {
setMapCenter(new GeoPoint(aLatitude, aLongitude));
}
public boolean isTilesScaledToDpi() {
return mTilesScaledToDpi;
}
public void setTilesScaledToDpi(boolean tilesScaledToDpi) {
mTilesScaledToDpi = tilesScaledToDpi;
updateTileSizeForDensity(getTileProvider().getTileSource());
}
private void updateTileSizeForDensity(final ITileSource aTileSource) {
int tile_size = aTileSource.getTileSizePixels();
float density = getResources().getDisplayMetrics().density * 256 / tile_size ;
int size = (int) ( tile_size * (isTilesScaledToDpi() ? density : 1));
if (Configuration.getInstance().isDebugMapView())
Log.d(IMapView.LOGTAG, "Scaling tiles to " + size);
TileSystem.setTileSize(size);
}
public void setTileSource(final ITileSource aTileSource) {
mTileProvider.setTileSource(aTileSource);//将瓦片源设置给建好的瓦片提供者
updateTileSizeForDensity(aTileSource);
this.checkZoomButtons();
this.setZoomLevel(mZoomLevel); // revalidate zoom level
postInvalidate();
}
//缩放
double setZoomLevel(final double aZoomLevel) {
...
return this.mZoomLevel;
}
@Deprecated
public void zoomToBoundingBox(final BoundingBoxE6 boundingBox) {
BoundingBox box = new BoundingBox(boundingBox.getLatNorthE6()/1e6, boundingBox.getLonEastE6()/1e6, boundingBox.getLatSouthE6()/1e6, boundingBox.getLonWestE6()/1e6);
zoomToBoundingBox(box, false);
}
public void zoomToBoundingBox(final BoundingBox boundingBox, final boolean animated) {
zoomToBoundingBox(boundingBox, animated, 0);
}
public void zoomToBoundingBox(final BoundingBox boundingBox, final boolean animated, final int borderSizeInPixels) {
...
}
...
public boolean canZoomIn() {
final int maxZoomLevel = getMaxZoomLevel();
if ((isAnimating() ? mTargetZoomLevel.get() : mZoomLevel) >= maxZoomLevel) {
return false;
}
return true;
}
public boolean canZoomOut() {
final int minZoomLevel = getMinZoomLevel();
if ((isAnimating() ? mTargetZoomLevel.get() : mZoomLevel) <= minZoomLevel) {
return false;
}
return true;
}
/**
* Zoom in by one zoom level.
*/
boolean zoomIn() {
return getController().zoomIn();
}
@Deprecated
boolean zoomInFixing(final IGeoPoint point) {
Point coords = getProjection().toPixels(point, null);
return getController().zoomInFixing(coords.x, coords.y);
}
@Deprecated
boolean zoomInFixing(final int xPixel, final int yPixel) {
return getController().zoomInFixing(xPixel, yPixel);
}
/**
* Zoom out by one zoom level.
*/
boolean zoomOut() {
return getController().zoomOut();
}
@Deprecated
boolean zoomOutFixing(final IGeoPoint point) {
Point coords = getProjection().toPixels(point, null);
return zoomOutFixing(coords.x, coords.y);
}
@Deprecated
boolean zoomOutFixing(final int xPixel, final int yPixel) {
return getController().zoomOutFixing(xPixel, yPixel);
}
@Override
public IGeoPoint getMapCenter() {
return getProjection().fromPixels(getWidth() / 2, getHeight() / 2, null);
}
/**
* rotates the map to the desired heading
* @param degrees
*/
public void setMapOrientation(float degrees) {
setMapOrientation(degrees, true);
}
/**
* There are some cases when we don't need explicit redraw
* @since 6.0.0
*/
public void setMapOrientation(final float degrees, final boolean forceRedraw) {
mapOrientation = degrees % 360.0f;
if (forceRedraw) {
// Request a layout, so that children are correctly positioned according to map orientation
requestLayout();
invalidate();
}
}
public float getMapOrientation() {
return mapOrientation;
}
/**
* @since 5.6.6
*/
public float getMapScale() {
return mMultiTouchScale;
}
//是否使用网络连接
public boolean useDataConnection() {
return mMapOverlay.useDataConnection();
}
public void setUseDataConnection(final boolean aMode) {
mMapOverlay.setUseDataConnection(aMode);
}
@Deprecated
public void setScrollableAreaLimit(BoundingBoxE6 boundingBox) {
setScrollableAreaLimitDouble(boundingBox == null ? null : new BoundingBox(
boundingBox.getLatNorthE6()/1E6, boundingBox.getLonEastE6()/1E6,
boundingBox.getLatSouthE6()/1E6, boundingBox.getLonWestE6()/1E6));
}
public void setScrollableAreaLimitDouble(BoundingBox boundingBox) {
mScrollableAreaBoundingBox = boundingBox;
...
}
...
//滚动
@Override
public void scrollBy(int x, int y) {
scrollTo((int)(getMapScrollX() + x), (int)(getMapScrollY() + y));
}
//设置北京颜色
@Override
public void setBackgroundColor(final int pColor) {
mMapOverlay.setLoadingBackgroundColor(pColor);
invalidate();
}
@Override
protected void dispatchDraw(final Canvas c) {
final long startMs = System.currentTimeMillis();
// Reset the projection
resetProjection();
// Save the current canvas matrix
c.save();
// Apply the scale and rotate operations
c.concat(getProjection().getScaleRotateCanvasMatrix());
/* Draw background */
// c.drawColor(mBackgroundColor);
try {
/* Draw all Overlays. */
this.getOverlayManager().onDraw(c, this);
mZoomButtonsOverlay.draw(c, this, false);
// Restore the canvas matrix
c.restore();
super.dispatchDraw(c);
}catch (Exception ex){
//for edit mode
Log.e(IMapView.LOGTAG, "error dispatchDraw, probably in edit mode", ex);
}
if (Configuration.getInstance().isDebugMapView()) {
final long endMs = System.currentTimeMillis();
Log.d(IMapView.LOGTAG,"Rendering overall: " + (endMs - startMs) + "ms");
}
}
@Override
protected void onDetachedFromWindow() {
this.onDetach();
super.onDetachedFromWindow();
}
...
//设置地图监听器
public void setMapListener(final MapListener ml) {
mListener = ml;
}
// ===========================================================
// Methods 常用的功能
// ===========================================================
private void checkZoomButtons() {
mZoomButtonsOverlay.setZoomInEnabled(canZoomIn());
mZoomButtonsOverlay.setZoomOutEnabled(canZoomOut());
}
public void setBuiltInZoomControls(final boolean on) {
setBuiltInZoomControls(on,
ZoomButtonsOverlay.POSITION_HORIZONTAL_DEFAULT
| ZoomButtonsOverlay.POSITION_VERTICAL_DEFAULT);
}
public void setBuiltInZoomControls(final boolean on, final int pPosition) {
mZoomButtonsOverlay.setEnabled(on);
mZoomButtonsOverlay.setPosition(pPosition);
checkZoomButtons();
}
public void setBuiltInZoomControls(final boolean on, final int pLeft, final int pTop) {
mZoomButtonsOverlay.setEnabled(on);
mZoomButtonsOverlay.setLeftTop(pLeft, pTop);
checkZoomButtons();
}
public void setMultiTouchControls(final boolean on) {
mMultiTouchController = on ? new MultiTouchController<Object>(this, false) : null;
}
...
private boolean enableFling = true;
private boolean pauseFling = false; // issue 269, boolean used for disabling fling during zoom changes
public void setFlingEnabled(final boolean b){
enableFling = b;
}
public boolean isFlingEnabled(){
return enableFling;
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
//手势检测
private class MapViewGestureDetectorListener implements OnGestureListener {
@Override
public boolean onDown(final MotionEvent e) {
...
return true;
}
@Override
public boolean onFling(final MotionEvent e1, final MotionEvent e2,
final float velocityX, final float velocityY) {
...
return true;
}
@Override
public void onLongPress(final MotionEvent e) {
...
}
@Override
public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
final float distanceY) {
...
}
@Override
public void onShowPress(final MotionEvent e) {
...
}
@Override
public boolean onSingleTapUp(final MotionEvent e) {
...
}
}
//双点击手势检测
private class MapViewDoubleClickListener implements GestureDetector.OnDoubleTapListener {
@Override
public boolean onDoubleTap(final MotionEvent e) {
if (MapView.this.getOverlayManager().onDoubleTap(e, MapView.this)) {
return true;
}
// final IGeoPoint center = getProjection().fromPixels((int) e.getX(), (int) e.getY(),
// null);
getProjection().rotateAndScalePoint((int) e.getX(), (int) e.getY(), mRotateScalePoint);
return zoomInFixing(mRotateScalePoint.x, mRotateScalePoint.y);
}
@Override
public boolean onDoubleTapEvent(final MotionEvent e) {
if (MapView.this.getOverlayManager().onDoubleTapEvent(e, MapView.this)) {
return true;
}
return false;
}
@Override
public boolean onSingleTapConfirmed(final MotionEvent e) {
if (MapView.this.getOverlayManager().onSingleTapConfirmed(e, MapView.this)) {
return true;
}
return false;
}
}
// ===========================================================
// Public Classes
// ===========================================================
/**
* OpenStreetMapView每个子类布局
*/
public static class LayoutParams extends ViewGroup.LayoutParams {
...
}
//使你能够设置瓦片提供者,如zip, assets, sqlite, etc
public void setTileProvider(final MapTileProviderBase base){
this.mTileProvider.detach();
mTileProvider.clearTileCache();
this.mTileProvider=base;
mTileProvider.setTileRequestCompleteHandler(mTileRequestCompleteHandler);
updateTileSizeForDensity(mTileProvider.getTileSource());
this.mMapOverlay = new TilesOverlay(mTileProvider, this.getContext());
mOverlayManager.setTilesOverlay(mMapOverlay);
invalidate();
}
/**
* @since 6.0.0
*/
@Deprecated
public void setInitCenter(final IGeoPoint geoPoint) {
setCenter(geoPoint);
}
public long getMapScrollX() {
return mMapScrollX;
}
public long getMapScrollY() {
return mMapScrollY;
}
private void setMapScroll(final long pMapScrollX, final long pMapScrollY) {
mMapScrollX = pMapScrollX;
mMapScrollY = pMapScrollY;
requestLayout();
}
/**
* @since 5.6.6
*/
public GeoPoint getCenter() {
return mCenter;
}
/**
* @since 5.6.6
*/
public void setCenter(final IGeoPoint pGeoPoint) {
mCenter = (GeoPoint)pGeoPoint;
setMapScroll(0, 0);
resetProjection();
}
}