使用SVG打造可交互的自定义地图

最近发现了一个好玩的东西:SVG,秉承着学以致用的原则,做了一个小demo。
先上效果图:
这里写图片描述

这是一个可以交互的中国地图.

像这类复杂的自定义空间,如果要我们自己来画,我估计会让我开始怀疑人生吧!

好了 废话不多说,下面我们就用svg的方式来实现这个自定义的地图控件:
步骤:
1:下载中国地图的svg文件
2:然后将svg文件转换成android能用的xml文件:附上链接一个:http://inloop.github.io/svg2android/
3:由于步骤2解析到的SVG是xml格式的,如果说不做交互的话,可以直接显示(可改变源文件的颜色),如果要做交互的话,就需要获取到每一个省对应的path,然后设置交互点击事件了,这样的话,就需要解析得到我们需要的path。
4:解析完之后,就需要将每一个path会知道canvas中去
5:设置点击事件,判断当前点击的是哪个省份

这里1,2步就直接略过了,我们从第三步开始:

//开启一个线程,用于解析xml文件
private Thread parseSvgThread = new Thread(new Runnable() {
        @Override
        public void run() {
            InputStream inputStream = mContext.getResources().openRawResource(R.raw.china);
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = null;
            try {
                builder = factory.newDocumentBuilder();
                Document doc = builder.parse(inputStream);
                Element rootElements = doc.getDocumentElement();
                //解析path节点
                NodeList items = rootElements.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);
                    //将解析的每一条path创建一个javabean,然后放到容器中
                    Province province = new Province(path);
                    itemLists.add(province);
                }
                //解析完成之后,发送handler 通知解析完成并设置颜色
                mHandler.sendEmptyMessage(0);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    });

    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if(itemLists != null && itemLists.size() > 0){
                int size = itemLists.size();
                for(int i = 0;i<size;i++){
                    //简单设置下颜色itemLists.get(i).setDrawColor(colors[i%colors.length]);
                }
                //更新绘制
                invalidate();
            }
        }
    };

由于解析过程比较耗时,所以我们开启一个独立的线程来解析,解析完成之后通过handler来设置每一个省的颜色,然后更新绘制

第四步:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //1:画选择的
        //2画没别选择的
        if(itemLists != null && itemLists.size()>0){
            canvas.save();
            canvas.scale(scale,scale);
            if(mSelectedItem != null){
                mSelectedItem.drawableProvince(mPaint,canvas,true);
            }
            for(int i = 0;i<itemLists.size();i++){
                if(itemLists.get(i) != mSelectedItem){
                    itemLists.get(i).drawableProvince(mPaint,canvas,false);
                }
            }
            canvas.restore();
        }
    }

然后在province的bean中,我们来做绘画的操作:

//在画板上画
    public void drawableProvince(Paint paint, Canvas canvas,boolean isSelected){
        if(isSelected){
            //当前被选择的省
            //画阴影
            paint.setStyle(Paint.Style.FILL);
            paint.setStrokeWidth(2);
            paint.setColor(Color.BLACK);
            paint.setShadowLayer(8,0,0,0xffffff);
            canvas.drawPath(path,paint);

            //画填充部分
            paint.clearShadowLayer();
            paint.setColor(Color.RED);
            canvas.drawPath(path,paint);
        }else{
            //画填充部分
            paint.setColor(drawColor);
            paint.setStyle(Paint.Style.FILL);
            paint.setStrokeWidth(1);
            canvas.drawPath(path,paint);

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

到这里,地图就可以按照我们自定义的颜色来显示了。
最后一步,设置监听,首先,我们在prvoince这个bean中,写一个方法,来判断当前点击的这个点是否在province中:

//判断某一点是否包含在该省内
    public boolean isContain(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);
    }

这里使用的方法是:创建一个包含path的区域(region),然后使用region的contains方法来进行判断。

最后,我们只需要在mapview中来处理touch事件并刷新ui就可以了。

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值