SVG实现中国地图

news2024/11/24 22:27:59

1.SVG是什么?

svg 是Scalable Vector Graphics的缩写,指可伸缩矢量图形,可以用于绘制复杂不规则的控件。

svg绘制原理,就是利用了Path绘制图形。
1)svg利用xml定义图形。在xml中就包晗了绘制Path所需的数据。
2)加载xml中的PathData,转换成Path对象。
3)利用Canvas,把Path绘制在屏幕上了。
4)处理点击事件。

path支持的指令有:
M = moveto(M X,Y) :将画笔移动到指定的坐标位置
L = lineto(L X,Y) :画直线到指定的坐标位置
H = horizontal lineto(H X):画水平线到指定的X坐标位置
V = vertical lineto(V Y):画垂直线到指定的Y坐标位置
C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次贝赛曲线
S = smooth curveto(S X2,Y2,ENDX,ENDY)
Q = quadratic Belzier curve(Q X,Y,ENDX,ENDY):二次贝赛曲线
T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射
A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧线
Z = closepath():关闭路径

2.利用SVG绘制中国地图

1)利用Dom解析xml数据,把省份信息解析成java对象。
下面就是svg的数据格式。其中pathData保存的就是绘制省份地图所需要的path信息。

<path
    id="340000"
    title="安徽"
    class="land"
    pathData="M541.02,336.29L541.71,336.09L543.77,338.27L543.53,338.58..."

利用Dom解析Xml。将xml中定义的标签转换成JavaBean

关键代码将xml中定义的PathData转换成Java中Path对象。

  Path path = PathParser.createPathFromPathData(pathData);

InputStream in = getResources().openRawResource(R.raw.china);
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder documentBuilder = null;
    try {
        documentBuilder = factory.newDocumentBuilder();
        Document document = documentBuilder.parse(in);
        NodeList nodeList = document.getElementsByTagName("path");
        list = new ArrayList<>();
        float left = -1;
        float top = -1;
        float right = -1;
        float bottom = -1;

        for (int i = 0; i < nodeList.getLength(); i++) {
            Element node = (Element) nodeList.item(i);
            //拿到定义在xml中的pathData数据
            String pathData = node.getAttribute("pathData");
            //利用PathParser转换成Path对象。
            Path path = PathParser.createPathFromPathData(pathData);
            String name = node.getAttribute("title");
            ProviceItem item = new ProviceItem();
            item.path = path;
            item.name = name;
            item.drawColor = colorArray[i % colorArray.length];
            list.add(item);

            RectF rectF = new RectF();
            path.computeBounds(rectF, true);

            //下面是为了拿到整个地图的最左、最上、最右、最下的坐标值。
            left = left == -1 ? rectF.left : Math.min(left, rectF.left);
            top = top == -1 ? rectF.top : Math.min(top, rectF.top);
            right = right == -1 ? rectF.right : Math.max(right, rectF.right);
            bottom = bottom == -1 ? rectF.bottom : Math.max(bottom, rectF.bottom);

        }
        //通过坐标值,构建一个矩形,就是地图要绘制的矩形区域。
        mapRectF = new RectF(left, top, right, bottom);

    } catch (Exception e) {
        e.printStackTrace();
    }

将得到的list集合设置给自定义的MapView。 

public void setData(List<ProviceItem> list, RectF rectF) {
    this.list = list;
    this.mapRectF = rectF;
    if (mapRectF != null) {
        //拿到地图的原始大小
        double mapWidth = mapRectF.width();
        //拿到缩放比,使绘制的地图宽度铺满。
        scale = (float) (mWidth / mapWidth);
    }
    //调用View的onDraw方法
    postInvalidate();
}
在onMeasure时,拿到MapView的宽度。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    mWidth = MeasureSpec.getSize(widthMeasureSpec);
}

2)自定义View绘制地图。执行完MapView的onDraw方法,地图就显示到了屏幕上。

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (list != null && list.size() > 0) {
        canvas.save();
        //按获得的缩放比,对画布进行缩放
        canvas.scale(scale, scale);
        for (ProviceItem proviceItem : list) {
            //selectItem 是被选中的item。
            //调用ProviceItem的方法,绘制自己省份的地图。
            proviceItem.drawItem(canvas, paint, selectItem == proviceItem);
        }
    }
}

        将JavaBean中封装的Path对象,通过canvas.drawPath(path,paint),绘制到屏幕上。

 public void drawItem(Canvas canvas, Paint paint, boolean isSelect) {
        //选中状态
        if (isSelect) {
            //清除绘制的阴影
            paint.clearShadowLayer();
            paint.setStrokeWidth(2);
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(drawColor);
            paint.setShadowLayer(1, 2, 2, 0xffffff);
            canvas.drawPath(path, paint);
            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(Color.BLACK);
            //将拿到的path绘制到画布上
            canvas.drawPath(path, paint);
        } else {
            //未选择状态
            paint.setStrokeWidth(1);
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(drawColor);
            canvas.drawPath(path, paint);
            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(Color.BLACK);
            //将拿到的path绘制到画布上
            canvas.drawPath(path, paint);
        }

    }

3)给每一个省份添加点击事件。首先要判断点击的是哪个省份地图。

在ViewGroup做事件分发处理时,判断触摸点落在哪个View上,View是一个矩形,比较容易判断,省份是一个不规则的图形,怎么判断呢?

先看下ViewGroup中是如何处理点击事件的落点的。

   public boolean pointInView(float localX, float localY, float slop) {
          return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
                  localY < ((mBottom - mTop) + slop);
  }

 但是要判断触摸点,落在哪个地图上,和上面的类似,但也有不同。
 通常View可以接收事件的区域是一个规则的矩形,但地图确实不规则的,
 这里用到了Region,来对不规则的View做事件的相应。具体做法,看代码实现。

复写onTouchEvent方法。

在按下时进行判断,也可以在手指滑动过程中进行判断。

手指抬起后,恢复原来状态。

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            //按下时响应
            handleOnTouch((int) event.getX(), (int) event.getY());
            break;
        case MotionEvent.ACTION_MOVE:
            //移动过程中响应
            handleOnTouch((int) event.getX(), (int) event.getY());
            break;
        case MotionEvent.ACTION_UP:
            //手指抬起来后,恢复原来状态
            selectItem = null;
            postInvalidate();
            break;
    }
    return true;
}

 拿到手指落点的x,y坐标和省份的Region进行对比。Region封装了一个path。

调用region.contains((int)x, (int)y)方法,就可以和落点进行比对。

ProviceItem selectItem = null;
private void handleOnTouch(int x, int y) {
    if (list == null || list.size() <= 0) {
        return;
    }
    ProviceItem select = null;
    for (int i = 0; i < list.size(); i++) {
        ProviceItem item = list.get(i);
        //isTouch返回true,说明一件匹配到了相应的省份地图来响应事件。
        if (item.isTouch(x / scale, y / scale)) {
            select = item;
            if (select != selectItem) {//判断按下的是同一个,则不重复触发绘制。
                //调用onDraw方法。
                postInvalidate();
            }
            selectItem = select;
            return;
        }
    }
}

public boolean isTouch(float x, float y) {
    RectF rectF = new RectF();
    //计算出path路径的边距,并把得到的值存在RectF中
    path.computeBounds(rectF, true);
    //创建一个和RectF矩形大小的区域
    Region region = new Region();
    //将path的路径,设置到region中,
    region.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
    //通过这个api就能够判断触摸点,是否落在了path绘制的区域内
    return region.contains((int)x, (int)y);
}


到这,中国地图绘制、事件的相应核心代码已经完毕。
举一反三,上面的代码也可以用于绘制其他SVG等不规则的图形。
核心点就三个,解析xml,绘制path,事件响应。

Demo链接:

android开发之SVG实现中国地图-Android文档类资源-CSDN下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/526288.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

创建数据库中,超详细常用的MySQL命令(含解析、图解与全部代码)

目录 系统命令行 MySQL命令行 数据库命令 数据表命令 建表并导入数据 表的其他操作 系统命令行 以下是在系统命令行&#xff0c;已管理员身份运行的情况下&#xff0c;MySQL的一些命令 1.这两条是关闭MySQL服务与开启MySQL服务的命令 net stop MySQL net start MySQL80…

Mysql启动不了怎么回事

mysql 服务无法启动是什么原因&#xff1f; mysql服务无法启动的原因有很多&#xff1a;可能端口被占用&#xff1b;可能my.cnf配置了错误的参数&#xff1b;也有可能没有初始数据库&#xff0c;还有可能是其他原因。大多数原因都可以通过先注销掉原有的服务、重新装载服务、之…

ASIC-WORLD Verilog(5)基础语法下篇

写在前面 在自己准备写一些简单的verilog教程之前&#xff0c;参考了许多资料----asic-world网站的这套verilog教程即是其一。这套教程写得极好&#xff0c;奈何没有中文&#xff0c;在下只好斗胆翻译过来&#xff08;加了自己的理解&#xff09;分享给大家。 这是网站原文&…

k8s中pod使用详解

一、前言 在之前k8s组件一篇中,我们谈到了pod这个组件,了解到pod是k8s中资源管理的最小单位,可以说Pod是整个k8s对外提供服务的最基础的个体,有必要对Pod做深入的学习和探究。 二、再看k8s架构图 为了加深对k8s中pod的理解,再来回顾下k8s的完整架构 三、pod特点 结合上面这…

YOLOv5算法原理与网络结构

YOLOv5算法原理与网络结构 1.1 YOLOv5算法 YOLOv5算法共有4种网络结构&#xff0c;分别是YOLOv5s、YOLOv5m、YOLOv5l和YOLOv5x&#xff0c;这四种网络结构在宽度和深度上不同&#xff0c;原理上基本一样&#xff0c;接下来以 YOLOv5s 为例介绍 YOLOv5网络结构。 图1 YOLOv5网…

基于Java+SpringBoot+Vue前后端分离手机销售商城系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

软件AutoID Network Navigator设置基恩士扫码枪的使用教程

AutoID Network Navigator可以用来对扫码枪的ip和各参数进行调整 1.设置前的准备 扫码枪的默认ip是192.168.100.1&#xff0c;所以需要先把电脑IP更改为192.168.100.xxx 2.搜索扫码枪 更改电脑IP后打开软件点击绿色的号 选择以太网 出现局域网设置弹窗&#xff0c;若为你设…

为什么说网络安全行业是 IT 行业最后的红利?

一、为什么选择网络安全&#xff1f; 这几年随着我国《国家网络空间安全战略》《网络安全法》《网络安全等级保护 2.0》等一系列政策/法规/标准的持续落地&#xff0c;网络安全行业地位、薪资随之水涨船高。 未来 3-5 年&#xff0c;是安全行业的黄金发展期&#xff0c;提前踏…

《汇编语言》- 读书笔记 - 第1章-基础知识

《汇编语言》- 读书笔记 - 第1章-基础知识 1.1 机器语言1.2 汇编语言的产生1.3 汇编语言的组成1.4 存储器1.5 指令和数据1.6 存储单元1.7 CPU对存储器的读写1.8 地址总线主流CPU的寻址能力 1.9 数据总线1.10 控制总线检测点 1.11.11 内存地址空间(概述)1.12 主板1.13 接口卡1.1…

【python基础教程】csv文件的写入与读取

✅作者简介&#xff1a;大家好我是hacker707,大家可以叫我hacker &#x1f4c3;个人主页&#xff1a;hacker707的csdn博客 &#x1f525;系列专栏&#xff1a;python基础教程 &#x1f4ac;推荐一款模拟面试、刷题神器&#x1f449;点击跳转进入网站 csv文件读写 csv的简单介绍…

MAC怎么获取文件路径 MAC获取文件路径的四种方法

MAC怎么获取文件路径介绍 方法一&#xff1a;最简单的方法 右键文件或者文件夹&#xff0c;选择显示简介 2在弹出来的窗口中找到位置&#xff0c;即为路径&#xff0c;在mac 10.10之前的系统是正常的路径&#xff0c;10.10开始是小箭头代替/显示&#xff0c;注意&#xff0c…

Git操作不规范,战友提刀来相见。

年终奖都没了&#xff0c;还要扣我绩效&#xff0c;门都没有&#xff0c;哈哈。 这波骚Git操作我也是第一次用&#xff0c;担心闪了腰&#xff0c;所以不仅做了备份&#xff0c;也做了笔记&#xff0c;分享给大家。 文末留言抽奖&#xff0c;聊聊你的年终奖。 问题描述 小A和…

2023年最新Python安装详细教程

目录 一、python官网 二、在官网的Downloads栏目&#xff0c;选择对应的操作系统 三、进入windows对应的页面&#xff0c;选择python版本 (1)选择python的稳定发布版本Stable Releases (2)下载python的安装程序Windows Installer 四、运行安装python的安装程序Windows Install…

【郭东白架构课 模块二:创造价值】25|节点四:架构规划之需求确认

你好&#xff0c;我是郭东白。 上节课我们讲了架构规划这个环节的第一个部分&#xff0c;也就是统一语义。那么这节课我们就来讲第二个部分——需求确认。 需求确认与统一语义的过程是密不可分的。需求确认是在统一语义赋能之下进行的&#xff0c;所以两者并不是先后顺序的关系…

【模拟IC】MOM 电容 和 MIM 电容的简介与比较

文章目录 一、MIM和MOM电容简介1、MIM (Metal-Insulator-Metal)电容2、MOM(Metal-Oxide-Metal)电容 二、 先进工艺下&#xff0c;MIM 和 MOM 电容的比较1、单位面积容值2、工艺实现和电压系数3、电容密度受频率的影响(稳定性&#xff09;4、自谐振频率随面积的变化5、品质因数6…

ChatGPT火爆科研圈,登上《Nature》《Science》正刊

ChatGPT火出圈了&#xff0c;几乎涉及到各行各业的每个领域&#xff0c;科研圈更甚。 《Science》期刊主编H. HOLDEN THORP发表关于ChatGPT的社论&#xff1a; “ChatGPT is fun, but not an author”。 “ChatGPT 很好玩&#xff0c;但不是作者” 文章指出&#xff0c;许多担忧…

JavaWeb-CSS的盒模型与弹性布局

目录 CSS的盒模型边框内边距外边距块级元素水平居中 弹性布局常用的一些属性justify-contentalign-items CSS的盒模型 每一个HTML就相当于一个矩形的"盒子". 这个盒子由以下几个部分组成 边框 border内容 content(下图中间蓝色部分)内边距 padding外边距 margin 边…

【饭谈】ChatGpt如果让软件ui都消失的话,那ui自动化测试该何去何从?

“未来的软件长什么样&#xff1f;” 一位妹子产品经理问我&#xff1a;“你说说未来的软件ui是什么样的&#xff1f;听到这个问题我先是诧异了一下&#xff0c;随即陷入了沉思。” 我看着眼前的产品经理&#xff0c;她是一位比较年轻干练的女强人类型&#xff0c;1.65的身材…

Mac软件打开提示:已损坏,无法打开。您应该将它移到废纸娄 怎么解决?

新入手的苹果电脑打开软件出现&#xff1a;“已损坏&#xff0c;无法打开。您应该将它移到废纸娄” 或 “已损坏&#xff0c;打不开。推出磁盘映像”。这个怎么解决&#xff1f; 第一部分&#xff1a; 1、点菜单栏搜索图标&#xff0c;输入&#xff1a;终端 &#xff0c;找到后…

2022 第十三届蓝桥杯大赛软件赛省赛(第二场),C/C++ 大学B组题解

2022 第十三届蓝桥杯大赛软件赛省赛&#xff08;第二场&#xff09;&#xff0c;C/C 大学B组题解 文章目录 第1题 —— 练习 &#xff08;5分&#xff09;第2题 —— 三角回文数 &#xff08;5分&#xff09;第3题 —— 卡片 &#xff08;10分&#xff09;第4题 —— 考勤刷卡 …