用SVG绘制中国地图,并实现各省份可点击

一、问题分析

1、需要下载含有中国地图的 SVG文件

下载地址:https://www.amcharts.com/dl/javascript-maps/ ,这里包含世界各个国家的SVG文件以及省份地图的SVG

2、需要SVG资源转换xml文件

转换可以用http://inloop.github.io/svg2android/ 网站

中国地图svg格式如下:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="-1dp"
    android:height="-1dp"
    android:viewportWidth="-1"
    android:viewportHeight="-1">
    <path
        android:fillColor="#CCCCCC"
        android:strokeColor="#ffffff"
        android:strokeWidth="0.5"
        android:pathData="M541.02,336.29L541.71,336.09L54...."/>
     <path ... />
     ....
</vector>

每个path都代表一个省份
pathData是绘制的命令和对于的坐标值,一个命令后面是两个具体坐标值

例如: M541.02,336.29L541.71,336.09

M是移动命令,后面两个数(541.02,336.29)值是移动的具体坐标
L是绘直线命令,从当前值绘制到L后面的坐标(541.71,336.09)

3、如何将SVG文件绘制成图形?

第一步需要解析xml文件获取path值,可通过dom解析获取xml文件中的值
获取到String类型的Path值后,还需要将String创建Path对象,可以通过PathParser来创建。
Path path = PathParser.createPathFromPathData(strPath);

 //Dom 解析 SVG文件

InputStream inputStream = mContext.getResources().openRawResource(R.raw.china);


DocumentBuilderFactory facotory = DocumentBuilderFactory.newInstance();

try {
    DocumentBuilder builder = facotory.newDocumentBuilder();

    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"); 
        @SuppressLint("RestrictedApi")
        Path path = PathParser.createPathFromPathData(pathData);
        ProvinceItem item = new ProvinceItem(path);            
        list.add(new ProvinceItem(path));
    }
}catch (ParserConfigurationException e) {
    e.printStackTrace();
} catch (SAXException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

获取的Path后,就可以直接通过Canvas绘制了。

canvas.drawPath(path,paint);
4、如何判断点击的坐标在哪个省份?

点击的时候,我们可得到一个(x、y),而我们的地图是有很多的省份的Path绘制而成。那么我们可以讲Path转换成Region(区域),再通过Region的contains判断点击的坐标是否在这个区域内 ,这样我们就知道点击在哪个省份了。

public boolean isTouch(float x, float y) {
   Region region = new Region();
   //将Path转化为RectF矩形
   RectF rectF = new RectF();
   path.computeBounds(rectF,true);
   region.setPath(path,new Region((int)rectF.left,(int)rectF.top,(int)rectF.right,(int)rectF.bottom));
   return region.contains((int)x,(int)y);
}

二、完整代码

MapView类:

package com.metre.svg_demo;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.Nullable;
import android.support.v4.graphics.PathParser;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;


import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

public class MapView extends View {
    public MapView(Context context) {
        this(context,null);
    }

    public MapView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public MapView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private  Context mContext;

    private Paint mPaint;

    private int[] colorArray = new int[]{0xFF239BD7, 0xFF30A9E5, 0xFF80CBF1, 0xFFFFFFFF};


    private void init(Context context){
        mContext = context;
        loadSVGThread.start();

        mPaint = new Paint();
        mPaint.setAntiAlias(true);

        //colorArray = new int[]{R.color.color_838B8B, R.color.color_607B8B, R.color.color_8FBC8F, R.color.color_8DB6CD};
    }

    private List<ProvinceItem> itemList = new ArrayList<>();

    private Thread loadSVGThread = new Thread(){
        @Override
        public void run() {
            super.run();

            //Dom 解析 SVG文件

            InputStream inputStream = mContext.getResources().openRawResource(R.raw.china);


            DocumentBuilderFactory facotory = DocumentBuilderFactory.newInstance();

            try {
                DocumentBuilder builder = facotory.newDocumentBuilder();

                Document doc = builder.parse(inputStream);

                Element rootElement = doc.getDocumentElement();
                NodeList items = rootElement.getElementsByTagName("path");

                List<ProvinceItem> list = new ArrayList<>();

                float left = -1;
                float top = -1;
                float right = -1;
                float bottom = -1;


                for (int i = 0; i < items.getLength(); i++) {
                    Element element = (Element) items.item(i);
                    String pathData = element.getAttribute("android:pathData");

                    @SuppressLint("RestrictedApi")
                    Path path = PathParser.createPathFromPathData(pathData);

                    //将Path转化成矩形
                    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);

                    ProvinceItem item = new ProvinceItem(path);
                    item.setColor(getResources().getColor(colorArray[i%4]));//随机颜色
                    list.add(new ProvinceItem(path));
                }

                //float left, float top, float right, float bottom
                totalRect = new RectF(left,top,right,bottom);

                itemList = list;

                Handler handler = new Handler(Looper.getMainLooper());
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        requestLayout();
                        invalidate();
                    }
                });

            } catch (ParserConfigurationException e) {
                e.printStackTrace();
            } catch (SAXException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };

    private ProvinceItem select;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if(itemList == null)return;
        canvas.save();
        canvas.scale(scale,scale);
        for (ProvinceItem provinceItem : itemList) {
            if(select == provinceItem){
                provinceItem.drawItem(canvas, mPaint, true);
            }else {
                provinceItem.drawItem(canvas, mPaint, false);
            }
        }

    }

    private RectF totalRect;
    private float scale = 1.0f;

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //当前控件高宽度
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        if(totalRect != null){
            float mapWidth = totalRect.width();
            scale = width/mapWidth;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        handleTouch(event.getX()/scale,event.getY()/scale);
        return super.onTouchEvent(event);
    }

    private void handleTouch(float x, float y) {
        if(itemList == null)return;
        ProvinceItem selectItem = null;
        for (ProvinceItem proviceItem : itemList) {
            if(proviceItem.isTouch(x,y)){
                selectItem = proviceItem;
            }
        }

        if(selectItem != null){
            select = selectItem;
            postInvalidate();
        }
    }
}

ProvinceItem省份类:

package com.metre.svg_demo;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.Log;

public class ProvinceItem {

    private Path path;
    private int color;

    public ProvinceItem(Path path) {
        this.path = path;
    }

    public ProvinceItem(Path path, int color) {
        this.path = path;
        this.color = color;
    }

    public Path getPath() {
        return path;
    }

    public void setPath(Path path) {
        this.path = path;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        Log.e("ProvinceItem","setColor -->"+color);
        this.color = color;
    }

    public void drawItem(Canvas canvas, Paint paint, boolean isSelect) {

        if(isSelect){
            paint.clearShadowLayer();
            paint.setStrokeWidth(1);
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(color);
            canvas.drawPath(path,paint);

            paint.clearShadowLayer();
            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(0xFFD0E8F4);
            canvas.drawPath(path,paint);

        }else{

            paint.setStrokeWidth(2);
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(Color.BLACK);
            paint.setShadowLayer(8,0,0,0xffffff);
            canvas.drawPath(path,paint);

            paint.clearShadowLayer();
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(color);
            paint.setStrokeWidth(2);
            canvas.drawPath(path,paint);

        }
    }

    public boolean isTouch(float x, float y) {

        Region region = new Region();
       //将Path转化为RectF矩形
        RectF rectF = new RectF();
        path.computeBounds(rectF,true);
        region.setPath(path,new Region((int)rectF.left,(int)rectF.top,(int)rectF.right,(int)rectF.bottom));

        return region.contains((int)x,(int)y);
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354

推荐阅读更多精彩内容