版权声明:本文为博主原创文章,未经博主允许不得转载。

本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、效果

地图效果只有各个省份的,按上级要求,地图的展示必须是完整的,这边效果没有南海那些小岛屿,无法展示效果吐了,见谅。原先发布成功了,最近修改后发布,被警告说违法违规,只能删除图片了。
二、分析

第一步 下载含有中国地图的 SVG
这里附一个地图资源: https://www.amcharts.com/dl/javascript-maps/ ,这里面包含世界各个国家的SVG地图,各个省份地图

第二步 将 SVG 资源转换成相应的 Android 代码
用http://inloop.github.io/svg2android/ 网站,将 SVG 转化成 Xml文件,放置在 /res/raw/ 下。

第三步 利用 Xml 解析 SVG 的代码 封装成 JavaBean 最重要的得到 Path
利用 Xml 解析,把中国地图的 Path 封装成一个个省的 JavaBean。

第四步 重写 OnDraw 方法 利用 Path 绘制中国地图

第五步 重写 OnTouchEvent 方法,记录手指触摸位置,判断这个位置是否坐落在某个省份上

三、省份 JavaBean

我们先来封装一个省份的 JavaBean,以便对各个省份进行绘制。

public class Provice {/*** 绘制路径*/protected Path path;/*** 绘制颜色*/private  int drawColor;public Provice(Path path) {this.path = path;}void draw (Canvas canvas, Paint paint, boolean isSelect) {if (isSelect) {//选中时,绘制描边效果paint.setStrokeWidth(2);paint.setColor(Color.BLACK);paint.setStyle(Paint.Style.FILL);paint.setShadowLayer(8,0,0,0xffffff);canvas.drawPath(path,paint);//选中时,绘制地图paint.clearShadowLayer();paint.setColor(drawColor);paint.setStyle(Paint.Style.FILL);paint.setStrokeWidth(2);canvas.drawPath(path, paint);}else {//非选中时,绘制描边效果paint.clearShadowLayer();paint.setStrokeWidth(1);paint.setStyle(Paint.Style.FILL);paint.setColor(drawColor);canvas.drawPath(path, paint);//非选中时,绘制地图paint.setStyle(Paint.Style.STROKE);int strokeColor = 0xFFD0E8F4;paint.setColor(strokeColor);canvas.drawPath(path, paint);}}/*** 是否被选中*/public boolean isSelect(int x, int y) {//构造一个区域对象RectF rectF=new RectF();//计算控制点的边界path.computeBounds(rectF,true);Region region=new Region();region.setPath(path,new Region((int)rectF.left,(int)rectF.top,(int)rectF.right,(int)rectF.bottom));return region.contains(x,y);}public Path getPath() {return path;}public void setPath(Path path) {this.path = path;}public int getDrawColor() {return drawColor;}public void setDrawColor(int drawColor) {this.drawColor = drawColor;}
}

这段代码相对还是比较简单,在 draw 中不论选中与否都进行两次绘制,是因为当边沿颜色设置与中间颜色不一样的话,是没办法直接进行绘制,只能分两次进行边沿和中间内容的绘制。

isSelect 就是对一个点是否在一个区域中进行判断,这个方法比较常见,就不具体介绍了。拿 Path 路径与该路径边界组成的矩形进行相裁剪,获取到的就是该 Path 所围成的区域。

四、Xml 数据封装成 JavaBean

这个主要是分两部,一个是读取 Xml 中 Path 的数据,即对 Xml 数据进行解析。二是将获取到的 Path 路径数据封装成 Provice 类。

1.读取 Xml 数据

  InputStream inputStream = context.getResources().openRawResource(R.raw.china_svg);try {//取得 DocumentBuilderFactory 实例DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();//从 factory 获取 DocumentBuilder 实例DocumentBuilder builder = factory.newDocumentBuilder();//解析输入流,得到 Document 实例Document doc = builder.parse(inputStream);Element rootElement = doc.getDocumentElement();NodeList items = rootElement.getElementsByTagName("path");for (int i = 0; i < items.getLength(); i ++) {Element element= (Element) items.item(i);String pathData=element.getAttribute("android:pathData");}} catch (Exception e) {e.printStackTrace();}

这即是对一个 Xml 数据进行读取的方法,跟 File 读写一样,都是固定套路。

  1. 封装 JavaBean

由 Xml 解析器解析出来的 Path 数据是一个字符串,我们需要把这串字符串解析成对应的 Path ,然后调用构造函数创建对应的 Provice。

选取其中一个省份数据进行分析:

    <pathandroid:fillColor="#CCCCCC"android:strokeColor="#ffffff"android:strokeWidth="0.5"android:pathData="M546.46,257.82L546.2,258.07L545.19,257.69L545.32,258.18L543.93,257.5L543.54,257.65L542.41,258.89L541.38,259.38L542.02,260.56L540.61,260.4L539.44,259.53L539.28,258.7L538.66,258.32L537.92,258.56L536.13,258.13L535.82,258.52L535.34,258.01L535.11,258.57L534.23,258.84L533.87,259.42L533.2,259.42L532.34,257.98L530.81,258.02L529.74,257.1L529.84,255.69L529,255.13L530.84,254.47L530.2,254L530.15,252.87L528.99,252.2L529.29,251L531.38,249.47L533.13,249.19L533.53,248.49L534.28,249.13L534.78,247.81L535.79,247.12L535.17,245.59L534.67,245.52L533.4,244.1L533.46,243.33L532.94,243.15L533.23,242.5L535.15,241.24L536.1,241.87L537.59,241.28L539.29,238.42L540,238.94L541.47,238.45L542.04,238.74L542.1,238.37L540.49,236.54L540.53,236.05L540.92,235.88L541.32,236.49L542.23,236.64L541.97,235.28L543.78,235.25L543.95,234.15L544.4,233.9L545.04,234.33L544.79,235.26L545.4,236.52L546.36,237.49L547.05,237.66L548.92,239.98L550.77,239.84L552.86,240.63L554.07,240.2L555.26,240.44L555.25,240.92L554.58,240.66L554.36,240.98L554.53,241.89L552.78,241.9L551.97,242.41L552.11,242.94L551.46,243.23L551.9,244.24L551.74,245.25L553.34,247.62L553.79,247.69L553.79,247.69L553.79,248.62L551.45,250.07L551.45,250.07L549.71,250.41L548.96,250.95L548.26,250.65L546.19,250.97L545.91,251.98L546.21,253.02L547.91,253.88L548.31,254.99L547.73,255.44L547.66,256.41L547.66,256.41L547.72,256.71L547.07,256.96z" />

地图的 Path 是由很多条短的直线连接而成的,我们解析 Xml 获取到数据是表示这些连接点的字符串,需要把这个字符串转化成对应的 Path。网络上有提供对应的的工具类,当然也可以自己进行代码编写解析,代码较长,但不是很难,这边也不展示。

3.另起线程

考虑到读取地图数据是一个耗时的操作,故另起一个线程进行数据的读取以及解析。
最终代码:

   private Thread loadThread=new Thread(){@Overridepublic void run() {InputStream inputStream = context.getResources().openRawResource(R.raw.china_svg);try {//取得 DocumentBuilderFactory 实例DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();//从 factory 获取 DocumentBuilder 实例DocumentBuilder builder = factory.newDocumentBuilder();//解析输入流,得到 Document 实例Document doc = builder.parse(inputStream);Element rootElement = doc.getDocumentElement();NodeList items = rootElement.getElementsByTagName("path");for (int i = 0; i < items.getLength(); i ++) {Element element= (Element) items.item(i);String pathData=element.getAttribute("android:pathData");Path path = PathParser.createPathFromPathData(pathData);proviceList.add(new Provice(path));}} catch (Exception e) {e.printStackTrace();}handler.sendEmptyMessage(0);}};private int[] colorArray = new int[]{0xFF239BD7, 0xFF30A9E5, 0xFF80CBF1, 0xFFB0D7F8};Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {if (proviceList == null) {return;}for (int i = 0; i < proviceList.size(); i++) {int color;int flag =  i %4;switch (flag) {case 1:color = colorArray[0];break;case 2:color = colorArray[1];break;case 3:color = colorArray[2];break;default:color = colorArray[3];break;}proviceList.get(i).setDrawColor(color);}postInvalidate();}};

PathParser 即是 Path 路径字符串转化为 Path 对象的工具类。在需要加载地图的时候,启动这个线程即可。

把数据解析成 JavaBean 之后,需要通知进行刷新操作。在这里,我们对各个省份的绘制颜色进行随机设置,实际中可以根据某一数据进行颜色绘制的选择。

五、onDraw()

onDraw()一直是核心重点。

   @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (proviceList != null) {//绘制没有被选择的省份for (Provice item : proviceList) {if (item != selectItem) {item.draw(canvas, paint, false);}}//绘制选中的省份if (selectItem != null) {selectItem.draw(canvas, paint, true);}}}

onDraw()分别进行了选中与未选中省份的绘制,由于前面已经封装好 Provide 类的绘制方法 draw(),这边直接调用即可。
注:必须先绘制未选中的省份,否则在选中与未选中的边沿上,未选中的边沿颜色会把选中的边沿颜色给覆盖掉。

六、OnTouchEvent()

上面已经把 SVG 的地图数据转化为 Path 绘制出来,接下来需要对屏幕的触摸事件进行处理,选中对应区域的省份,重绘地图。

    @Overridepublic boolean onTouchEvent(MotionEvent event) {if (proviceList != null) {Provice provice = null;for ( Provice item : proviceList) {if (item.isSelect((int) (event.getX()), (int) (event.getY()))) {provice = item;break;}}if (provice != null) {selectItem = provice;postInvalidate();}}return true;}

OnTouchEvent() 方法也比较简单,最开始的时候省份已经封装了判断点是否在该省份 Path 路径内的方法,所以这边只要遍历省份,调用该方法即可,最后进行重绘。

这时候基本实现了大体功能:把 SVG 地图数据绘制出来,实现了点击选中功能。

效果无法在这里展示了,见谅。

可以在 onDraw 方法前后记录时间去获取绘制地图的所需要的时间,是在0~1 毫秒之间,我们初始化的时候把 Path 保存在 List 集合里面,后面只需要再次绘制尽可,绘制的速度是特别快的。

这里,xml 的宽高都是设置为 match_parent,可以发现,屏幕宽度还是过小,导致地图无法完全显示,所以还需要重写 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 以及对地图进行缩放。

七、onMeasure()

首先,要确认一下过去的 SVG 中国地图的具体边界,在 Provice 类的 isSelect(int x, int y) 就有获取各个省份的边界,可以在这边进行一个测试,记录边界值,并取最值,获取中国地图的边界。这边用的边界值为:左:0 上:0 右:773 下:568。即中国地图宽度为 773,高度为 568。

private static int Mleft=0, Mright=0, Mtop=0, Mbotton=0;public boolean isSelect(int x, int y) {//构造一个区域对象RectF rectF = new RectF();
//        计算控制点的边界path.computeBounds(rectF,true);Region region = new Region();region.setPath(path, new Region((int)rectF.left, (int)rectF.top, (int)rectF.right, (int)rectF.bottom));if (rectF.right > Mright) {Mright = (int)rectF.right;}if (rectF.bottom > Mbotton) {Mbotton = (int)rectF.bottom;}System.out.println( Mright + " " + Mbotton);return region.contains(x,y);}

加的这些是为了计算中国地图大小,实际不需要。

在 onMeasure()方法里面计算缩放比例。

    @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int width = MeasureSpec.getSize(widthMeasureSpec);int height = MeasureSpec.getSize(heightMeasureSpec);scale = Math.min(width/mapWidth, height/mapHeight);}

onDraw()方法的时候要进行缩放后在进行绘制

    @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (proviceList != null) {canvas.scale(scale, scale);for (Provice item : proviceList) {if (item != selectItem) {item.draw(canvas, paint, false);}}if (selectItem != null) {selectItem.draw(canvas, paint, true);}}}

onTouchEvent(MotionEvent event) 判定的时候也要添加对应的缩放

    @Overridepublic boolean onTouchEvent(MotionEvent event) {if (proviceList != null) {Provice provice = null;for ( Provice item : proviceList) {if (item.isSelect((int) (event.getX() / scale), (int) (event.getY() / scale))) {provice = item;break;}}if (provice != null) {selectItem = provice;postInvalidate();}}return true;}

八、附

代码连接:http://download.csdn.net/download/qq_18983205/9956046

(十七)SVG 实例-可交互式中国地图相关推荐

  1. Android 使用svg构造交互式中国地图

    概念 什么是svg 即Scalable Vector Graphics 可伸缩矢量图形 SVG的W3C的解释: http://www.w3school.com.cn/svg/svg_intro.asp ...

  2. SeniorUI11_SVG实现可交互式中国地图

    SeniorUI_高级UI汇总目录 一 需求 绘制中国地图,按省区分,点一个省,有选中效果 二 效果图 三 原理分析 省份是不规则的,不同的绘图方式不行,得用svg绘制 利用path绘制每一个省份 利 ...

  3. SVG 详解——自定义可点击的中国地图

    SVG 详解--自定义可点击的中国地图 SVG 定义 SVG 是一种图像文件格式,类似于 JPG.PNG.只不过 JPG 和 PNG 这种文件需要图像引擎加载,而 SVG 则是由画布来加载的. 它的英 ...

  4. php mysql 地图 矩形_PHP+Mysql+jQuery中国地图区域数据统计实例讲解

    今天我要给大家介绍在实际应用中,如何把数据载入到地图中.本文结合实例,使用PHP+Mysql+jQuery实现中国地图各省份数据统计效果. 本例以统计某产品在各省份的活跃用户数为背景,数据来源于mys ...

  5. 地图 svg中国地图、echarts百度迁徙图

    总结下最近使用过的地图控件: 第一种 svg中国地图 demo:http://www.oschina.net/code/snippet_54124_36979 这个很简单,照着demo里面直接调用就可 ...

  6. html5 svg中国地图map悬停显示省市地区代码

    html5 svg中国地图map悬停显示省市地区代码 html5 svg中国地图map悬https://www.51qianduan.com/article/4401.html停显示省市地区代码

  7. java 中国地图_高级UI-svg实现可交互的中国地图

    [TOC] 思路 第三步 利用Xml解析SVG的代码 封装成javaBean 最重要的得到Path 第四步 重写OnDraw方法 利用Path绘制中国地图 第五步 重写OnTouchEvent方法,记 ...

  8. Echarts——中国地图绘制

    1.首先准备中国城市分布Geo数据 china.jsonhttps://download.csdn.net/download/heal_l/75671467https://download.csdn. ...

  9. echarts实现中国地图和自定义形状的词云图

    目录 1.中国地图 2.词云图 3.资源源码 用echarts实现了中国地图上的发票流入流出图和小鸟形状的词云图. 先看下效果 中国地图(有动态的流入流出箭头和悬浮框提示信息): 词云图(小鸟形状,也 ...

最新文章

  1. linux 同步方法剖析,Linux 同步方法剖析
  2. Shellz中awk的简单用法
  3. 计算点、线、面等元素之间的交点、交线、封闭区域面积和闭合集(续7)
  4. 迭代器以及迭代器的作用
  5. 2018/7/13-纪中某C组题【jzoj3382,jzoj3383,jzoj3384,jzoj3385】
  6. div跳转html页面底部,即使没有内容,如何强制DIV块扩展到页面底部?
  7. 【JEECG TBSchedule】详解应对平台高并发的分布式调度框架TBSchedule
  8. python字典按照value进行排序
  9. Begin using git (Part1) - Git的安装与配置
  10. 矩阵分析(二):子空间之间的代数关系-无交连、正交与正交补
  11. 源码 | 幽灵交易者策略
  12. matlab2020面板介绍
  13. 2022年第三届全国大学生网络安全精英赛
  14. R语言连续变量正态性检验
  15. 服务器英文系统怎么切中文,云服务器怎么把英文改成中文
  16. F.dropout()与nn.dropout()
  17. 时域反射仪(TDR)介绍
  18. Agile Scrum WaterFall通俗易懂的全过程整理
  19. 400. 第 N 位数字【我亦无他唯手熟尔】
  20. 《动手学深度学习》(七) -- 边界框和锚框

热门文章

  1. 在亚马逊上你知道怎么定价-跨境知道
  2. Flask框架Jinjia模板中常用语法
  3. 利用IIS日志追查网站入侵者
  4. 市场规模超600亿?中国企业级应用软件正加速前行
  5. C语言练习题:企业发放的奖金根据利润提成。利润 I 低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成
  6. 零跑C01超低的售价,让梦想照进现实
  7. C#底层库--万能进制转换器(自定义有序字符,支持任意进制)
  8. 亮眼财报里失意的联想
  9. AI教你画油画:任意画风都可驾驭,笔画序列秒秒钟呈现,百度南大团队打造 | Reddit高赞...
  10. 全面讲解Flink中CheckPoint机制和Exactly Once / At Least Once应用