前一篇说到了使用自定义ViewGroup实现手绘地图,没看过的可以移步,因为本篇会用到上一篇的部分内容

自定义View-手绘地图(一)

和前一篇一样,实现图片的操作经过同样的操作、onMeasure,初始化图片、位移、缩放、回弹,不一样的是poi点的绘制

自定义ViewGroup直接使用addView的方式并且经由onLayout实现位置确定,而自定义View是不一样的,放置poi点,只能通过调用ondraw绘制,点击事件,通过监听onTOuch,获取位置,然后根据范围来判断poi哪个被点击,接下来进入实现

首先,在放置poi之前,代码与实现步骤与(一)部分一样,所以就直接进入绘制poi部分了

描述一下我的思路,绘制poi同样的是根据相对图片的位置百分比,算出此时应该在屏幕中的什么位置,再绘制上去,而点击事件,在第一次绘制poi时,也就是一倍图的时候记录整个poi会占的位置,然后点击屏幕时,将onTouch获得的x、y值经过计算,得到此时点击的位置,如果是在初始图时,会在什么范围,从而获取点击到的poi点

-----------------------分割线-------------------------------

onDraw中多了一个方法

/*** 地图移动到中间*/
private void moveToCenter() {
}
@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);if (mMapBitmap == null)return;//绘制地图canvas.drawBitmap(mMapBitmap, mMapMatrix, mPaint);//绘制poidrawPoi(canvas);
}

由篇(一)的效果图知道,poi绘制主要分成四部分,圆角矩形、三角形、文字、poi图标

在进行绘制之前,先对poi图标进行缩放,缩放方式与地图图片缩放方式相同

/*** 对bitmap进行缩放*/
public Bitmap scaleBitmap(Bitmap bitmap,int scaleX,int scaleY){float imgHeight = bitmap.getHeight();float imgWidth = bitmap.getWidth();Matrix matrix = new Matrix();matrix.postScale(scaleX / imgWidth, scaleY / imgHeight);return Bitmap.createBitmap(bitmap, 0, 0, (int) imgWidth, (int) imgHeight, matrix, true);
}
mPoiBitmapDefault=scaleBitmap(mPoiBitmapDefault,Utils.dip2px(mContext,16),Utils.dip2px(mContext,16));

mPoiBitmapDefault也就是缩放后的poi图标,此处贴出Utils中的方法

public class Utils {/*** 根据手机的分辨率从 dp 的单位 转成为 px(像素)*/public static int dip2px(Context context, float dpValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}/*** 根据手机的分辨率从 px(像素) 的单位 转成为 dp*/public static int px2dip(Context context, float pxValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (pxValue / scale + 0.5f);}/*** 判断p是否在abcd组成的四边形内** @param a* @param b* @param c* @param d* @param p* @return 如果p在四边形内返回true, 否则返回false.*/public static boolean pInQuadrangle(Point a, Point b, Point c, Point d,Point p) {double dTriangle = triangleArea(a, b, p) + triangleArea(b, c, p)+ triangleArea(c, d, p) + triangleArea(d, a, p);double dQuadrangle = triangleArea(a, b, c) + triangleArea(c, d, a);return dTriangle == dQuadrangle;}// 返回三个点组成三角形的面积private static double triangleArea(Point a, Point b, Point c) {double result = Math.abs((a.x * b.y + b.x * c.y + c.x * a.y - b.x * a.y- c.x * b.y - a.x * c.y) / 2.0D);return result;}
}

其中两个dip与px相互转换的方法作用不多说,另外一个pInQuadrangle在后面进行解释

到目前为止,准备就绪,开始绘制poi

/*** 画poi点,并确定位置*/
private void drawPoi(Canvas canvas){if (mMapPoiEntityList!=null&&mMapPoiEntityList.size()>0){for (int i = 0; i < mMapPoiEntityList.size(); i++) {//加粗文字mPaint.setFakeBoldText(true);//获取文字宽高mPaint.setTextSize(Utils.dip2px(mContext,10));mPaint.setStrokeWidth(8);mPaint.getTextBounds(mMapPoiEntityList.get(i).getName(),0,mMapPoiEntityList.get(i).getName().length(),mTextRect);//获取地图图片对应矩阵RectF matrixRectF = getMatrixRectF();//poi点的宽高int poiWidth=mTextRect.width()+ Utils.dip2px(mContext,16+2+2+6);int poiHeight=0;if (mTextRect.height()>Utils.dip2px(mContext,16)){poiHeight=mTextRect.height()+Utils.dip2px(mContext,2+2+66);}else{poiHeight=Utils.dip2px(mContext,2+2+16+6);}int left=0;int top=0;//此处存储一下poi点的四个角,用于后面的点击事件//首先获取到中心点,即三角形下顶点的top、left值,此处left与top的值是相对于屏幕的left = (int) (matrixRectF.width() * (mMapPoiEntityList.get(i).getPrecentageLeft() / 100.0f) + matrixRectF.left);top = (int) (matrixRectF.height() * (mMapPoiEntityList.get(i).getPrecentageTop() / 100.0f) + matrixRectF.top);if (mIsFirstDraw){PoiBoundEntity poiBoundEntity=new PoiBoundEntity();poiBoundEntity.setId(mMapPoiEntityList.get(i).getI());//此处的范围是相对于地图的poiBoundEntity.setLeft((int) ((matrixRectF.width() * (mMapPoiEntityList.get(i).getPrecentageLeft() / 100.0f) )-poiWidth/2));poiBoundEntity.setTop((int) ((matrixRectF.height() * (mMapPoiEntityList.get(i).getPrecentageTop() / 100.0f))-poiHeight));poiBoundEntity.setRight((int) ((matrixRectF.width() * (mMapPoiEntityList.get(i).getPrecentageLeft() / 100.0f) )+poiWidth/2));poiBoundEntity.setBootom((int) (matrixRectF.height() * (mMapPoiEntityList.get(i).getPrecentageTop() / 100.0f)));mPoiBoundEntities.add(poiBoundEntity);}//画圆角矩形poiConersRectf.set(left-poiWidth/2,top-poiHeight,left+poiWidth/2,top-Utils.dip2px(mContext,6));mPaint.setColor(getResources().getColor(R.color.white));mPaint.setStyle(Paint.Style.FILL);canvas.drawRoundRect(poiConersRectf,Utils.dip2px(mContext,10),Utils.dip2px(mContext,10),mPaint);//画三角形trianglePath.reset();trianglePath.moveTo(left,top);trianglePath.lineTo(left-Utils.dip2px(mContext,3),top-Utils.dip2px(mContext,6));trianglePath.lineTo(left+Utils.dip2px(mContext,3),top-Utils.dip2px(mContext,6));trianglePath.close();canvas.drawPath(trianglePath,mPaint);//画poi图片canvas.drawBitmap(mPoiBitmapDefault,left-poiWidth/2+Utils.dip2px(mContext,2),top-poiHeight+(poiHeight-Utils.dip2px(mContext,6+16))/2,mPaint);//画文字mPaint.setColor(getResources().getColor(R.color.black));canvas.drawText(mMapPoiEntityList.get(i).getName(),left-poiWidth/2+Utils.dip2px(mContext,2+16+2),top-Utils.dip2px(mContext,8)-(poiHeight-Utils.dip2px(mContext,6)-mTextRect.height())/2,mPaint);}mIsFirstDraw=false;}
}

代码就是这些,注释的还是很清楚的,流程为先获取即将绘制的文字的宽高,然后根据UI给的设计图,得到poi的宽高,然后计算分别将圆角矩形,三角形,文字,图标绘制在合适的位置,并且因为poi的x、y值由相对于地图的百分比确定,因此在地图进行移动缩放操作时,poi点就自行跟着改变了。并且在第一次绘制的时候,存储下了每一个poi点的左上右下几个点的坐标值。接下来就进行点击事件的处理

case MotionEvent.ACTION_DOWN://获取到上一次手指位置mLastSinglePointX = event.getX();mLastSinglePointY = event.getY();//寻找点击位置getWhichPoiClick(event.getX(),event.getY());break;

在down中增加了一个方法

/*** 获取poi点击事件*/
private void getWhichPoiClick(float xTouch,float yTouch){List<PoiBoundEntity> mListPoiBoundEntity=new ArrayList<>();RectF matrixRectF = getMatrixRectF();float scaleX = (Math.abs(matrixRectF.left) + xTouch) / mNowMatrixvalues[Matrix.MSCALE_X];float scaleY = (Math.abs(matrixRectF.top) + yTouch) / mNowMatrixvalues[Matrix.MSCALE_X];Point point=new Point((int)scaleX,(int)scaleY);for (int i = 0; i < mPoiBoundEntities.size(); i++) {Point one=new Point(mPoiBoundEntities.get(i).getLeft(),mPoiBoundEntities.get(i).getTop());Point two=new Point(mPoiBoundEntities.get(i).getRight(),mPoiBoundEntities.get(i).getTop());Point three=new Point(mPoiBoundEntities.get(i).getLeft(),mPoiBoundEntities.get(i).getBootom());Point four=new Point(mPoiBoundEntities.get(i).getRight(),mPoiBoundEntities.get(i).getBootom());boolean b = Utils.pInQuadrangle(one, two, four,three,point);if (b){mListPoiBoundEntity.add(mPoiBoundEntities.get(i));}}/***解决poi位置又接触时,点击冲突状态(因按照poi绘制顺序,后面的点总是在之前绘制的点上方,因此点击事件* 存在两个位置重合时,取上层,也就是后绘制上去的*/if (mListPoiBoundEntity.size()>0){Toast.makeText(mContext,"被点击了"+mListPoiBoundEntity.get(mListPoiBoundEntity.size()-1).getId()+"",Toast.LENGTH_SHORT).show();}

这样也就获取到了点击poi,解释一下:

(此处更正,因后续操作发现,如果一倍图poi点叠加在一起则会导致放大了以后  点击事件混乱qaq-------不知道有没有小伙伴发现,既然没有留言应该就是没人发现吧。。。解决办法如下:

首先在onDraw中绘制poi时,是控制了添加边界次数,本来是为了避免重复赋值,目前看来得不断的变化值。于是首先每次画poi时将边界集合清空

/*** 画poi点,并确定位置*/
private void drawPoi(Canvas canvas) {mPoiBoundEntities.clear();

然后将之前的开关mIsFirstDraw去掉(没用了),然后在方法

getWhichPoiClick()

中更正如下

float scaleX = (Math.abs(matrixRectF.left) + xTouch) ;
float scaleY = (Math.abs(matrixRectF.top) + yTouch);

就是去除了倍数换算,而作为实际倍数

首先计算出当前点击的x、y值还原到一倍图时的x、y,(有错误我也不删除了。。。)然后取出每一个存储的值,进行遍历

判断点是否在四边形内的方法为之前贴出的Utils中的pInQuadrangle,原理为将点击点与每一个四边形的点两两相连,获取到的三角形面积相加若是大于四边形面积,则在范围外,否则在范围内,纸上画一画就知道了

后面还有一段注释,用于poi重合的处理,因为绘制的顺序,第二个绘制的总是在第一个之上,因此,如果点击的时候触发第二个的点击事件,当然 这是比较简单的做法,但是不够完善,更好的做法应该是取两个相交点的范围相交值的中心点,然后计算点击点到相交点的值为正还是负来判断点击事件具体位于哪边。感兴趣的朋友可以自己实践

至此本篇结束。感谢观看,不足之处请留言(-*..*-)

另外附上源码:点击打开链接

ps:因为有朋友下载了资源说卡,于是附上我后来优化后的代码,虽然多了些功能,但是大体的思路未变,感谢包容

Android手绘地图(优化版)

自定义View进阶-手绘地图(二)相关推荐

  1. WebGIS——OpenLayers 3 地图叠加自定义卫星/航拍/手绘地图(任意瓦片图)

    使用OpenLayers 3 第一步 首先创建Html文件的结构,在body中放入一个Div作为地图显示的容器,调整其宽度高度使其全屏显示 html页结构如下,其中id为map的div为显示地图的容器 ...

  2. H5手绘地图(自定义栅格图层)踩坑

    手绘地图简介 手绘地图顾名思义就是手工绘制的地图,比普通的地图更有观赏性和生动性,通过把特定的地点绘制出来,兼具实用和纪念性,同时更加具有可看性.一般在旅游景点有很多这种纸质版手绘地图.比如这种: 前 ...

  3. 景区自定义手绘地图叠加

    地图上嵌套手绘地图--实现效果: 这里用到了地图的图层 简单一个完整Demo代码 <!DOCTYPE html> <html lang="en"><h ...

  4. 手绘地图制作的关键点之“图层覆盖”

    前面介绍了<景区手绘地图(电子地图.智慧导览系统)如何制作>以及<景区手绘地图的绘制流程>,接下来介绍一些手绘地图制作的关键点. 手绘地图最关键的一点,就是把手绘地图准确的覆盖 ...

  5. Android基于mAppWidget实现手绘地图(一)--简介

    http://lemberg.github.io/mappwidget/user_guide.html 最近在看一些导游类应用,发现一些景区的导览图使用的完全是自定义地图,也就是手绘地图.这种小范围使 ...

  6. 微信小程序--放入个性化手绘地图具体步骤(腾讯地图)

    微信小程序–放入个性化手绘地图具体步骤(腾讯地图) 前言:小程序中想要实现个性化手绘地图需要通过H5嵌入的模式进行实现. 1.首先需要一个腾讯地图的账号(微信登录即可),然后选择个性化地图进入(htt ...

  7. Android高德地图贴合图片完成手绘地图展示

    上周刚接到一个需求,产品觉得高德的默认地图样式不好看,想要一个手绘地图贴合上去,看着美观很多,然而我内心确是抵触的,无法 ,产品讲了,只能先回答试试看看.接下拉就是一搏谷歌搜索. 1.使用web版本的 ...

  8. 手绘地图深度解析:类型、风格、功能、价值、制作流程、智慧导览

    本文概要:文本尝试系统性.多角度.全方位的介绍一下现在流行的手绘地图系统. 作者:轻轻的烟雾(z281099678) 一.手绘地图定义 什么是手绘地图?或者说,手绘地图到底是什么样的? 手绘地图首先是 ...

  9. 如何制作专业的手绘地图(电子地图、智慧导览系统)

    一.智慧导览系统介绍 手绘电子地图,就是把手绘地图覆盖到地图上,游客或者普通用户,可以在手机上通过地图的链接(或者现在流行的小程序)打开使用.是一种使用非常方便,集**"视.听.路径规划.实 ...

最新文章

  1. 关于浮点数的误差理解
  2. SDUT_2118 数据结构实验之链表三:链表的逆置
  3. MySQL基础入门学习【2】数据类型
  4. vs2008配置winddk
  5. 为什么Siri总是像个智障?智能助手背后的技术到底有多难?
  6. flask笔记3-模板
  7. 软件技术债务是什么_为什么我爱技术债务
  8. mysql 极限优化配置_MySQL优化(二) 优化诀窍
  9. YOLO系列专题——YOLOv3理论篇
  10. spring aop和事务同时开启带来的一些问题
  11. cad批量打印_CAD插件——批量打印软件安装包+安装教程
  12. 分布式系统架构简单介绍
  13. 题5 正确的Java垃圾回收说法
  14. android跑马灯监听,android跑马灯成效
  15. 如何在TOMCAT上安装Liferay
  16. matlab图形与动画设计 pdf,MATLAB图形与动画设计
  17. Java网络编程学习
  18. linux一级目录全解
  19. Navicat工具数据库表结构导出 word编写数据库设计文档
  20. gta4光影补丁_GTA4低配置优化版ENB光影补丁

热门文章

  1. 手机突然电量消耗很快_手机电量消耗快是什么原因(手机电池电量突然猛掉)...
  2. CISP证书对个人求职有帮助吗?
  3. CISP证书有什么用
  4. taobao.trades.sold.get-查询卖家已卖出的交易数据(根据创建时间),淘宝店铺卖出订单查询API接口,淘宝R2接口,淘宝oAuth2.0交易接口代码分享
  5. 不正经技术研究,键盘侠,你武器可能有个坑!!
  6. python自动化看什么书_python自动化测试书籍
  7. USB 传输方式(控制)
  8. Windows10家庭版 VMWare15 安装虚拟机启动时出现 蓝屏(而且重启)问题
  9. Linux下链接库出现的undefined reference问题总结
  10. Windows桌面下面任务栏无法点击(卡住)的解决办法