之前公司 项目有用到 gps定位 ,以及 工厂地图 布置 点 ,但是 无法使用百度地图之类的 第三方地图SDK ,只给一个工厂平面图,就要实现gps定位,一直 地图上布点。
gps 坐标转换到 图片上 的 算法 由 老大 搞定了,我这边 负责 UI 的 显示。
这是 demo 地址: 点击打开链接
这是 效果图:
首先 整个 控件 布局 是一个相对布局,右上角 放置了 放大,原始,缩小 按钮,实现对应功能
显示 地图图片 的 控件 也是一个 自定义控件 ,继承ImageView 里面 重写 了onDraw 方法 用来 画地图 的 线
/**
* 自定义 地图 地图
* Created by Administrator on 2016/6/30.
*/
public class MyBaseMapAndLines extends ImageView {
// 线 坐标
public ArrayList mapLineCoords;
public MyBaseMapAndLines(Context context) {
super(context);
mapLineCoords = new ArrayList<>();
}
public MyBaseMapAndLines(Context context, AttributeSet attrs) {
super(context, attrs);
mapLineCoords = new ArrayList<>();
}
public ArrayList getMapLineCoords() {
return mapLineCoords;
}
public int getLineSize() {
return mapLineCoords.size();
}
public void clearLines() {
mapLineCoords.clear();
}
public void addLines(ArrayList mapLineCoords) {
this.mapLineCoords.addAll(mapLineCoords);
}
public MapLineCoord getLine(int index) {
return mapLineCoords.get(index);
}
public void addLine(MapLineCoord mapLineCoord) {
mapLineCoords.add(mapLineCoord);
this.invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mapLineCoords == null || mapLineCoords.size() <= 0)
return;
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth((float) 5.0);
canvas.drawCircle(mapLineCoords.get(0).getViewX(), mapLineCoords.get(0).getViewY(), 10, paint);
canvas.drawCircle(mapLineCoords.get(mapLineCoords.size() - 1).getViewX(),
mapLineCoords.get(mapLineCoords.size() - 1).getViewY(), 10, paint);
// 划线
for (int i = 1; i < mapLineCoords.size(); i++) {
canvas.drawLine(mapLineCoords.get(i - 1).getViewX(),
mapLineCoords.get(i - 1).getViewY(),
mapLineCoords.get(i).getViewX(), mapLineCoords.get(i).getViewY(), paint);
}
}
/**
* 地图 线 拐点 坐标
*/
public class MapLineCoord {
private float firstX;
private float firstY;
private float viewX;
private float viewY;
public MapLineCoord() {
}
public MapLineCoord(float firstX, float firstY, float viewX, float viewY) {
this.firstX = firstX;
this.firstY = firstY;
this.viewX = viewX;
this.viewY = viewY;
}
public float getFirstX() {
return firstX;
}
public void setFirstX(float firstX) {
this.firstX = firstX;
}
public float getFirstY() {
return firstY;
}
public void setFirstY(float firstY) {
this.firstY = firstY;
}
public float getViewX() {
return viewX;
}
public void setViewX(float viewX) {
this.viewX = viewX;
}
public float getViewY() {
return viewY;
}
public void setViewY(float viewY) {
this.viewY = viewY;
}
@Override
public String toString() {
return "MapLineCoord{" +
"firstX=" + firstX +
", firstY=" + firstY +
", viewX=" + viewX +
", viewY=" + viewY +
'}';
}
}
}
地图上的 点 也是 一个自定义控件 继承LinearLayout 里面 放了一个 ImageView 显示地图点 的 图片,一个TextView 显示 地图点的 名称
因为 服务器 那边的 地图点的 坐标 是获取 图片左上角的坐标 作为 定义的 ,所以 我这里 对于地图点的 坐标 计算 也是 计算至ImageView的 左上角
要这样计算,就必须 先对这个 地图点的 ViewGroup 所有 进行 测量 宽高
/**
* 测量 地图点 的 边界
*/
private void measureBorder() {
int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
int width = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
this.pointIcon.measure(width, height);
this.pointTitle.measure(width, height);
this.measure(width, height);
// 是否 显示 地图点 的 标题
if (!isTitleShow) {
pointTitle.setVisibility(INVISIBLE);
} else {
pointTitle.setVisibility(VISIBLE);
}
this.borderLeft = (this.getMeasuredWidth() - this.pointIcon.getMeasuredWidth()) / 2;
this.borderTop = (this.getMeasuredHeight() - this.pointIcon.getMeasuredHeight() - this.pointTitle.getMeasuredHeight());
Log.i("testss", this.borderLeft + "fffffff=========" + this.borderTop);
}
然后 在 设置 显示位置 的 时候 ,调用 这两个方法
public void setFirstXShow(float x) {
x -= borderLeft;
setX(x);
}
public void setFirstYShow(float y) {
y -= borderTop;
setY(y);
}
/**
* 地图 移动 放大 监听
*/
private class MyTouchListener implements OnTouchListener {
/**
* 记录是拖拉照片模式还是放大缩小照片模式
*/
private int mode = 0;// 初始状态
/**
* 拖拉照片模式
*/
private static final int MODE_DRAG = 1;
/**
* 放大缩小照片模式
*/
private static final int MODE_ZOOM = 2;
/**
* 用于记录开始时候的坐标位置
*/
private PointF startPoint = new PointF();
/**
* 用于记录拖拉图片移动的坐标位置
*/
private Matrix matrix = new Matrix();
/**
* 用于记录图片要进行拖拉时候的坐标位置
*/
private Matrix currentMatrix = new Matrix();
/**
* 两个手指的开始距离
*/
private float startDis;
/**
* 两个手指的中间点
*/
private PointF midPoint;
@Override
public boolean onTouch(View v, MotionEvent event) {
/** 通过与运算保留最后八位 MotionEvent.ACTION_MASK = 255 */
switch (event.getAction() & MotionEvent.ACTION_MASK) {
// 手指压下屏幕
case MotionEvent.ACTION_DOWN:
mode = MODE_DRAG;
// 记录ImageView当前的移动位置
currentMatrix.set(myBaseMapAndLines.getImageMatrix());
startPoint.set(event.getX(), event.getY());
// 当 手指 按下时 初始化 长按标志
longPressTag = true;
downTime = System.currentTimeMillis();
// Log.i("test", "=onTouch=x==" + event.getX() + "===y==" + event.getY());
setFirstDownX(event.getX());
setFirstDownY(event.getY());
break;
// 手指在屏幕上移动,改事件会被不断触发
case MotionEvent.ACTION_MOVE:
// 拖拉图片
if (mode == MODE_DRAG) {
float dx = event.getX() - startPoint.x; // 得到x轴的移动距离
float dy = event.getY() - startPoint.y; // 得到x轴的移动距离
// 在没有移动之前的位置上进行移动
matrix.set(currentMatrix);
matrix.postTranslate(dx, dy);
// 如果 手指移动 距离不超过 5 个像素点 的 视为 没有移动
float offset = (float) Math.sqrt(dx * dx + dy * dy);
longPressTag = offset < 5 ? true : false;
}
// 放大缩小图片
else if (mode == MODE_ZOOM) {
float endDis = distance(event);// 结束距离
if (endDis > 10f) { // 两个手指并拢在一起的时候像素大于10
float scale = endDis / startDis;// 得到缩放倍数
matrix.set(currentMatrix);
matrix.postScale(scale, scale, midPoint.x, midPoint.y);
}
}
break;
// 手指离开屏幕
case MotionEvent.ACTION_UP:
//如果 按下 抬起 时间 大于 2s 则是 长按 事件
longPressTag = System.currentTimeMillis() - downTime > 2000 ? true : false;
// 当触点离开屏幕,但是屏幕上还有触点(手指)
case MotionEvent.ACTION_POINTER_UP:
mode = 0;
break;
// 当屏幕上已经有触点(手指),再有一个触点压下屏幕
case MotionEvent.ACTION_POINTER_DOWN:
longPressTag = false;
mode = MODE_ZOOM;
/** 计算两个手指间的距离 */
startDis = distance(event);
/** 计算两个手指间的中间点 */
if (startDis > 10f) { // 两个手指并拢在一起的时候像素大于10
midPoint = mid(event);
//记录当前ImageView的缩放倍数
currentMatrix.set(myBaseMapAndLines.getImageMatrix());
}
break;
}
/**
* 如果 此次 触摸事件 是 移动,放大事件
* 则 改变 地图 和 坐标点的位置
*/
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE:
// 移动 地图
myBaseMapAndLines.setImageMatrix(matrix);
float[] matrixValues = new float[9];
matrix.getValues(matrixValues);
// 地图原点移动
originalPointView.setX((float) (0 * matrixValues[0] + matrixValues[2]));
originalPointView.setY((float) (0 * matrixValues[4] + matrixValues[5]));
Log.i("testss", originalPointView.getX() + "===originalPointView====" + originalPointView.getY());
firstScale = matrixValues[0];
// 移动 点
for (int i = 0; i < mapPoints.size(); i++) {
double scaleX = mapPoints.get(i).getFirstX() * matrixValues[0];
double scaleY = mapPoints.get(i).getFirstY() * matrixValues[4];
mapPoints.get(i).setFirstXShow((float) (scaleX + matrixValues[2]));
mapPoints.get(i).setFirstYShow((float) (scaleY + matrixValues[5]));
}
// 移动 线
for (int i = 0; i < myBaseMapAndLines.getLineSize(); i++) {
float v1 = myBaseMapAndLines.getLine(i).getFirstX() * matrixValues[0] + matrixValues[2];
float v2 = myBaseMapAndLines.getLine(i).getFirstY() * matrixValues[4] + matrixValues[5];
myBaseMapAndLines.getLine(i).setViewX(v1);
myBaseMapAndLines.getLine(i).setViewY(v2);
}
myBaseMapAndLines.invalidate();
/**
*如果 外层为ScrollView 此句代码是解决
* 地图的移动 和 ScrollView 的滚动冲突的
* 当触摸事件在地图范围内时,ScrollView 滚动事件无法响应
* 当触摸事件在 地图范围外时,ScrollView可以滚动
*/
getParent().requestDisallowInterceptTouchEvent(true);
break;
}
// 如果 设置 了 长按 监听 则 传递 事件
// 否则 自己 消费 该 事件
if (getMapOnLongClickListener() != null) {
return false;
}
return true;
}
/**
* 计算两个手指间的距离
*/
private float distance(MotionEvent event) {
float dx = event.getX(1) - event.getX(0);
float dy = event.getY(1) - event.getY(0);
/** 使用勾股定理返回两点之间的距离 */
return (float) Math.sqrt(dx * dx + dy * dy);
}
/**
* 计算两个手指间的中间点
*/
private PointF mid(MotionEvent event) {
float midX = (event.getX(1) + event.getX(0)) / 2;
float midY = (event.getY(1) + event.getY(0)) / 2;
return new PointF(midX, midY);
}
}