Android中国地图区域检测控件的实现

Android中国地图区域检测控件的实现

前言

之前有项目需求需要实现地图区域检测功能,然后就不想重复造轮子,结果搜索良久,发现网上对于Android版的中国地图控件基本没有现成的可用。

于是只好自己实现了,下面会阐述实现的原理。

这里写图片描述

实现方案

1 利用图片的像素值检测区域

原理是使用多张png图片,每张图片只有一个区域,其他都为透明,通过类似PS图层的概念一层层叠加成为完整的地图,在点击的点通过获取像素值的透明度来分发事件,如果透明则事件继续传递,最后分发到非透明的那一层消费事件,从而检测到点击的区域。

这种做法在区域少的情况下还可以,一旦区域多了,就非常耗内存。简单计算一下,就拿一张480*800的png图片来说,中国的省市加上特区大概30个,每个需要两张png(一个是正常显示,一个是高亮显示),每个像素4Byte,那么需要的内存是480*800*30*2*4B=87.89MB,这么大手机内存肯定吃不消。当然如果只有几个区域几张图片那这种方法还是可以接受的。

2 嵌入网页

可以使用网页js来完成区域检测,但对网络环境有要求,可能没有那么流畅。
此种方案未详细实现过,具体效果未知,但似乎也是不错的选择。

3 矢量图实现

利用现有的SVG图,在AndroidStudio上转成android可用的vector,这样能节省大量内存,支持无限缩放不失真,降低性能损耗。


本文采用第三种矢量图的实现方式。

实现原理及过程

  • 首先先直观了解下android的vector长什么样,其实跟SVG基本一致
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportHeight="600.0"
    android:viewportWidth="700.0">
    <!--黑龙江-->
    <path
        android:name="@string/china_heilongjiang"
        android:fillAlpha="0.1"
        android:fillColor="#51b133"
        android:pathData="M646.2,...,152.4Z"
        android:strokeColor="#ffffff"
        android:strokeWidth="0" />
    <!--吉林-->
    <path
        android:name="@string/china_jilin"
        android:fillAlpha="0.1"
        android:fillColor="#51b133"
        android:pathData="M622,...,201.3Z"
        android:strokeColor="#ffffff"
        android:strokeWidth="0" />

...
</vector>

以上最重要的就是viewportHeight,viewportWidth,name,pathData四个属性,其他只是辅助显示效果而已。


  • 解析xml获得Path对象

    我们知道Canvas能够通过drawPath绘制路径,然后发现pathData里的字符串其实也就是描述了路径需要的数据,那么我把pathData的内容转成Path对象就可以用Canvas绘制了。

    那么问题又来了,怎么转成Path对象?我其实还不是特别清楚pathData内容里的协议,想写个解析器来解析肯定很麻烦,花费时间久还不一定好用。转念一想,google也要解析这些内容,那么它肯定实现了这个解析器,于是就从VectorDrawableCompat这个类着手,层层跟进,发现还真有个PathParser的类,可惜是私有的(在6.0之前记得是私有的),没法直接用,那么就copy一下啦,PathParser类的静态方法createPathFromPathData可以将字符串生成path对象,经过试验,的确能绘制出来,心里一万个高兴。

    但问题又来了,难道我要将pathData的内容一个一个copy过来然后调用createPathFromPathData生成path对象再保存起来,这样还是麻烦,有没有直接解析整个xml然后获得path对象数组的呢?google肯定实现了,但好像没开放出来,无法直接使用。

    于是想到使用反射去使用,这为后面的兼容埋了一个坑,在7.0上发现跟6.0的使用有些区别,导致反射失败,因此在这里就不再介绍使用反射的方式了。

    看来没法偷懒啦,为了兼容,需要将PathParser(在7.0上已经公有)拷贝出来,因为6.0还是私有的,其实这个类google自己也说了是复制来的。

    接下来使用pull解析xml,将xml的内容映射到实体类上,再通过PathParser转成Path对象,这样实现起来其实比之前用反射的方式更简单了。


  • 利用Canvas进行绘制

    获得了path对象数组,可以愉快地进行绘制了。

    绘制时需要注意的是,通过缩放画布来实现缩放的话会导致失真,原因是canvas当成bitmap来缩放,放大后自然模糊了,这样的话就无法体验到矢量图的优势了,但办法还是有的,通过Path的这个接口:
    public void Path.addPath(Path src, Matrix matrix);
    利用Matrix来缩放。


  • 区域检测

    Android系统自带的好东西Region可以用来实现不规则区域检测,但有个问题是区域经过缩放位移操作后跟原来设置的Region已经有出入了,此时还得想办法对坐标进行换算,换算的原理看下图,主要是计算实际地图在原始地图的相对位置即可。


换算示意图

最后

猜你喜欢

转载自blog.csdn.net/huweijian5/article/details/78921793