美文网首页工作生活
用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