美文网首页工作生活
用SVG绘制中国地图,并实现各省份可点击

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

作者: 小米Metre | 来源:发表于2019-06-29 18:21 被阅读0次

一、问题分析

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);
    }
}

相关文章

网友评论

    本文标题:用SVG绘制中国地图,并实现各省份可点击

    本文链接:https://www.haomeiwen.com/subject/afnpcctx.html