SeniorUI11_SVG实现可交互式中国地图

SeniorUI_高级UI汇总目录

一 需求

绘制中国地图,按省区分,点一个省,有选中效果

二 效果图

在这里插入图片描述

三 原理分析

  1. 省份是不规则的,不同的绘图方式不行,得用svg绘制
  2. 利用path绘制每一个省份
  3. 利用RegionAPI判断触摸是否在某个省份(Path)中

四 具体实现

世界地图SVG格式:
https://www.amcharts.com/dl/javascript-maps/

  • 第一步 下载含有中国地图的 SVG

  • 第二步 用http://inloop.github.io/svg2android/ 网站 将svg资源转换成相应的 Android代码

  • 第三步 利用Xml解析SVG的代码 封装成javaBean 最重要的得到Path

  • 第四步 重写OnDraw方法 利用Path绘制中国地图

  • 第五步 重写OnTouchEvent方法,记录手指触摸位置,判断这个位置是否坐落在某个省份上
    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():关闭路径(会自动绘制链接起点和终点)

         android:pathData="M541.02,336.29L541.71,336.09
     	L542.54,337.38L543.77,338.27
     	L543.53,338.58L545.92,338.99.8,350.12L561.12,349.61L562.97,349.6L563.89,349.94
     	L563.48,350.21L563.6,351.15L562.98,351.84L562.99,353.94L562.28,353.68L562.06,3
     	53.97L561.87,355.49L561.13,355.88L561.38,356.41L560.77,357.72L561.33,357.73
     	。。。。。
     	L562.06,359L563.49,358.5L563.75,357.85L564.17,358.09L564.64
     	,361.19L565.52,361.68L564.51,362.21L564.67,363.38L565.17,363.21L565.35,364.41
     	L566.19,364.53L566.23,365.29L567.26,365L568.99,365.25L569.63,364.91
     	L539.3,337.63L539.84,336.78L540.31,336.88z" />
    

//指令详情可以 参考 http://www.w3school.com.cn/svg/svg_intro.asp

//SVG —> 动画
地图资源可以在 https://www.amcharts.com/dl/javascript-maps/ 下载
里面包含世界各个国家的SVG地图 各个省份地图

五 核心代码

SvgChinaView

/**
 * Created by Ray on 2019-7-22.
 */
public class SvgChinaView extends View {

    private Context context;
    private List<ProvinceItem> list = new ArrayList<>();
    private static int[] colorArray = new int[]{0xFF239BD7, 0xFF30A9E5, 0xFF80CBF1, Color.RED};
    private ProvinceItem selectItem;
    private float scale = 1.3f;
    private Paint paint;
    private GestureDetectorCompat gestureDetectorCompat;

    public SvgChinaView(Context context) {
        this(context, null);
    }

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

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

    private void init(Context context) {
        this.context = context;
        this.paint = new Paint();
        paint.setAntiAlias(true);
        loadThread.start();
        gestureDetectorCompat = new GestureDetectorCompat(context, new GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onDown(MotionEvent e) {
                handlerTouch(e.getX(),e.getY());
                return true;
            }
        });
    }

    private void handlerTouch(float x, float y) {
        if(list != null){
            ProvinceItem temp = null;
            for(ProvinceItem item:list){
                if(item.isTouch((int)(x/scale), (int)(y/scale))){
                    temp = item;
                    break;
                }
            }
            if(temp != null){
                selectItem = temp;
                postInvalidate();
            }
        }
    }

    Thread loadThread = new Thread() {

        @Override
        public void run() {
            InputStream inputStream = context.getResources().openRawResource(R.raw.china);
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = null;
            try {
                builder = factory.newDocumentBuilder();
                Document document = builder.parse(inputStream);
                Element element = document.getDocumentElement();
                NodeList items = element.getElementsByTagName("path");
                for (int i = 0; i < items.getLength(); i++) {
                    Element element1 = (Element) items.item(i);
                    String pathData = element1.getAttribute("android:pathData");
                    @SuppressLint("RestrictedApi") Path path = PathParser.createPathFromPathData(pathData);
                    ProvinceItem item = new ProvinceItem(path);
                    list.add(item);
                    handler.sendEmptyMessage(1);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (list != null) {
                int totalNumber = list.size();
                for (int i = 0; i < totalNumber; i++) {
                    int color = Color.WHITE;
                    int flag = i % 4;
                    switch (flag) {
                        case 1:
                            color = colorArray[0];
                            break;
                        case 2:
                            color = colorArray[1];
                            break;
                        case 3:
                            color = colorArray[2];
                            break;
                        default:
                            color = 0x00FFF0EF;
                            break;
                    }
                    list.get(i).setDrawColor(color);
                }
                postInvalidate();
            }
        }
    };


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

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(list != null){
            canvas.save();
            canvas.scale(scale, scale);
            for(ProvinceItem item:list){
              if(item != selectItem){
                  item.draw(canvas,paint , false);
              }
            }

            if(selectItem != null){
                selectItem.draw(canvas,paint ,true );
            }
        }

    }
}

ProvinceItem

public class ProvinceItem {

    private Path path;

    private int drawColor;

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

    public void draw(Canvas canvas, Paint paint, boolean isSelect) {
        if (isSelect) {
            paint.setStrokeWidth(2);
            paint.setColor(Color.BLACK);
            paint.setStyle(Paint.Style.FILL);
            paint.setShadowLayer(8, 0, 0,0xFFFFFFFF);
            canvas.drawPath(path, paint);

           paint.clearShadowLayer();
            paint.setColor(drawColor);
            paint.setStyle(Paint.Style.FILL);
            paint.setStrokeWidth(2);
            canvas.drawPath(path, paint);
        } else {
            paint.clearShadowLayer();
            paint.setStrokeWidth(1);
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(drawColor);
            canvas.drawPath(path, paint);

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

    public boolean isTouch(int x, int y) {
        RectF rect = new RectF();
        path.computeBounds(rect, true);
        Region region = new Region();
        region.setPath(path, new Region((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom));
        boolean result = region.contains(x, y);

        Log.i("ProvinceItem", "isTouch x="+ result );
        return result;
    }

    public int getDrawColor() {
        return drawColor;
    }

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

六 Demo

SvgChinaView

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值