用SVG实现动态交互式地图

用SVG实现动态交互式地图

SVG是目前最火热的图像文件格式,它的英文全称为Scalable Vector Graphics,意思为可缩放的矢量图形,它是基于XML,由W3C联盟进行开发。SVG可以实现无损缩放,在性能上要比其它格式的图片更好,今天教大家用SVG实现一个动态交互式地图,先上效果图:
这里写图片描述
一、 实现思路
下载含有台湾地图的SVG,地图资源可以在这个网站上https://www.amcharts.com/dl/javascript-maps/ 下载,
http://inloop.github.io/svg2android/ 网站将SVG资源转换成相应的Android代码,利用DOM解析SVG的代码,将属性pathData数据封装成JavaBean,最重要的是得到点击的Path。Path指令解析如下所示:

  • M = moveto(M X,Y) :将画笔移动到指定的坐标位置,相当于 android Path 里的moveTo()
  • L = lineto(L X,Y) :画直线到指定的坐标位置,相当于 android Path 里的lineTo()
  • 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):弧线 ,相当于arcTo()
  • Z = closepath():关闭路径(会自动绘制链接起点和终点)

重写onDraw方法,利用path绘制台湾地图,重写onTouchEvent方法,记录手指触摸位置,判断坐标是否在某一个市的范围内。
二、 代码实例
自定义TaiWanMapView

public class TaiWanMapView extends View {
    private static final String TAG = TaiWanMapView.class.getName();
    private List<TaiWanItem> taiwanItems = new ArrayList<TaiWanItem>();
    //被点击的区域
    private TaiWanItem selectItem;
    //缩放1.3倍
    private float scale = 1.3f;
    private Context mContext;
    private Paint mPaint;
    private int[] colors = new int[]{0xFF239BD7,0xFF30A9E5,0xFF80CBF1,0xFFF0E68C};
    GestureDetectorCompat gestureDetectorCompat;

    public TaiWanMapView(Context context) {
        super(context);
    }

    public TaiWanMapView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        mContext = context;
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        gestureDetectorCompat = new GestureDetectorCompat(context,new GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onDown(MotionEvent e) {
                Log.d(TAG,"onDown x:"+e.getX()+";y:"+e.getY());
                handleTouch(e.getX(),e.getY());
                return true;
            }
        });
        thread.start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        //将画布缩放1.3倍
        canvas.scale(scale,scale);
        if(taiwanItems != null){
            for(TaiWanItem item:taiwanItems){
                if(item != selectItem){
                    item.draw(canvas,mPaint,false);
                }
            }
            if(selectItem != null){
                selectItem.draw(canvas,mPaint,true);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return gestureDetectorCompat.onTouchEvent(event);
    }

    private void handleTouch(float x,float y){
        TaiWanItem taiWanItem = null;
        if(taiwanItems != null){
            for(TaiWanItem item : taiwanItems){
                if(item.isTouch((int)(x/scale),(int)(y/scale))){
                    taiWanItem = item;
                    break;
                }
            }
            if(taiWanItem != null) {
                selectItem = taiWanItem;
                postInvalidate();
            }
        }
    }

    Thread thread = new Thread(){
        @Override
        public void run() {
            InputStream inputStream = mContext.getResources().openRawResource(R.raw.taiwan);
            //采用Dom解析器解析xml
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = null;
            try {
                builder = factory.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");
                    Path path = PathParser.createPathFromPathData(pathData);
                    TaiWanItem item = new TaiWanItem(path);
                    taiwanItems.add(item);
                }
                handler.sendEmptyMessage(1);
            } catch (ParserConfigurationException e) {
                e.printStackTrace();
            } catch (SAXException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };

    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if(taiwanItems == null || taiwanItems.size()==0)
                return;

            int colorNum = taiwanItems.size();
            int color = Color.WHITE;
            //赋予颜色
            for(int i=0;i<colorNum;i++){
                int flag = i % 4;
                switch(flag){
                    case 1:
                        color = colors[0];
                        break;
                    case 2:
                        color = colors[1];
                        break;
                    case 3:
                        color = colors[2];
                        break;
                    default:
                        color = colors[3];
                }
                taiwanItems.get(i).setDrawColor(color);
            }
            postInvalidate();
        }
    };
}

将地图path封装成TaiWanItem

public class TaiWanItem {

    private Path path;
    private int drawColor;

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

    /**
     * 绘制地图path
     * @param canvas
     * @param paint
     * @param isSelect
     */
    public void draw(Canvas canvas, Paint paint,boolean isSelect){
        if(isSelect){
            //画阴影图层
            paint.setStrokeWidth(2);
            paint.setShadowLayer(8,0,0,0xffffff);
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(Color.BLACK);
            canvas.drawPath(path,paint);
            //画区域path
            paint.clearShadowLayer();
            paint.setStrokeWidth(2);
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(drawColor);
            canvas.drawPath(path,paint);
        }else{
            //画线条
            paint.clearShadowLayer();
            paint.setStrokeWidth(1);
            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(0xFFD0E8F4);
            canvas.drawPath(path,paint);
            //画区域
            paint.setStrokeWidth(2);
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(drawColor);
            canvas.drawPath(path,paint);
        }
    }

    /**
     * 判断当前点击坐标是否在path范围内
     * @param x
     * @param y
     * @return
     */
    public boolean isTouch(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));
//判断X,Y是否在region区域范围内
        if(region.contains(x,y)) return true;
        return false;
    }

    public void setDrawColor(int drawColor) {
        this.drawColor = drawColor;
    }
}

整个代码不算很复杂,关键就是在onDraw方法里面,利用系统提供的方法canvas.drawPath(Path,Paint )将path区域绘制在画布上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值