最近在帮老师做一个项目,类似于景点通的App手机应用,我们是要精细化一些室内的地图,室内的地图采用的是自己的一套定位机制,所有室内地图也要自己来实现,参考了网上一些例子,考虑到效率的问题,最后决定使用SurfaceView来进行地图绘制,实现的功能有:
- 双击放大
- 多点触摸放大
- 地图拖拽
- 添加地图标记
效果图一张:
代码思路
1.处理缩放和拖拽事件
在这里我利用了Matrix类提供的图片操作方法去进行图片的缩放和平移处理,关于该方面的知识可以参考
Android开发–利用Matrix进行图片操作
2.双击放大
为了实现双击放大,在这里我们MyMap类中设置了一个成员变量lastClickTime用来记录上一次点击屏幕的时间(点击屏幕的时间值可以通过MotionEvent的getEventTime方法去获得,单位是ms),如果当前点击事件的时间与上次点击事件的时间差值小于300ms则执行放大事件。
3.多点触摸放大
通过MotionEvent中的方法来获得两个触摸点之间的距离大小, 如下:
//计算两个触摸点的距离
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
利用一个变量oldDist表示前一次两个触摸点的距离,利用一个oldRate表示前一次的缩放,在onTouchEvent方法中move的情况下不断更新当前缩放mCurrentScale = oldRate * (newDist / oldDist);
4.地图拖拽
利用一个PointF变量mapCenter表示当前地图中心的位置在手机屏幕上的坐标,当拖拽事件发生时通过手指移动的距离来不同更新mapCenter的值,并在draw方法中利用Matrix类操作图片
matrix.postTranslate(mapCenter.x - mBitmap.getWidth() / 2, mapCenter.y - mBitmap.getHeight() / 2);
5.添加地图标记
编写一个MarkObject类来表示地图标记,在该类之下存储了标记的Bitmap对象,该标记相对于整张地图的位置,以及点击标记的回调事件的处理。
在MyMap类中利用一个List变量markList来记录所有已经添加的地图标记。
1)处理标记随拖拽和缩放事件而改变位置:这里主要是根据mapCenter的点来进行计算,具体的计算大家可以参考代码;
2)处理点击事件:在onTouchEvent方法中up情况时,遍历markList中的MarkObject进行判断当前触摸点是否被包含在当前的标记区域中;
6.异步线程绘图方法
首先,感谢 Tiger_老虎 朋友的热心提醒:现将draw方法修改如下,将原来每一次开启一个新的线程重新绘制地图的方式弃用,修改代码为:
首先在构造方法中开启一个新的DrawThread处理绘图事件,之后当我们每一次需要重写绘图时通过Handler传递绘图消息,接着在DrawThread去处理该消息,调用绘图方法进行异步绘图。
参考代码
MyMap类:
package com.example.maptest;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MyMap extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = MyMap.class.getSimpleName();
private static final long DOUBLE_CLICK_TIME_SPACE = 300;
private float mCurrentScaleMax;
private float mCurrentScale;
private float mCurrentScaleMin;
private float windowWidth, windowHeight;
private Bitmap mBitmap;
private Paint mPaint;
private PointF mStartPoint;
private volatile PointF mapCenter;// mapCenter表示地图中心在屏幕上的坐标
private long lastClickTime;// 记录上一次点击屏幕的时间,以判断双击事件
private Status mStatus = Status.NONE;
private float oldRate = 1;
private float oldDist = 1;
private float offsetX, offsetY;
private boolean isShu = true;
private DrawThread mDrawThread;
private enum Status {
NONE, ZOOM, DRAG
};
private List<MarkObject> markList = new ArrayList<MarkObject>();
public MyMap(Context context, AttributeSet attrs,