SVG realizes China Map

1. What is SVG?

svg is the abbreviation of Scalable Vector Graphics, which refers to Scalable Vector Graphics and can be used to draw complex and irregular controls.

The principle of svg drawing is to use Path to draw graphics.
1) svg uses xml to define graphics. The data needed to draw the Path is included in the xml.
2) Load the PathData in the xml and convert it into a Path object.
3) Use Canvas to draw Path on the screen.
4) Handle click events.

The instructions supported by path are:
M = move to (m x, y): move the brush to the specified coordinate position
L = lineto(L X,Y): draw a straight line to the specified coordinate position
H = horizontal line to (H x): draw a horizontal line to the specified X coordinate position
V = vertical line to (V Y): draw a vertical line to the specified Y coordinate position
C = curveto (C x1, Y1, X2, Y2, endx, end): cubic Bezier curve
S = smooth curveto(S X2,Y2,ENDX,ENDY)
Q = quadratic Bezier curve (Q x, y, endx, end): quadratic Bezier curve
T = smooth rectangular belzier curve to (t endx, end): mapping
A = elliptic arc (a Rx, ry, xrotion, flag1, Flag2, x, y): Arc
Z = closepath(): close path

2. Map China with SVG

1) Use Dom to parse xml data and parse the province information into java objects.
The following is the data format of svg. The pathData stores the path information needed to draw the provincial map.


Parse XML with Dom. Convert tags defined in XML into JavaBean s

The key code converts the PathData defined in xml into a Path object in Java.

  Path path = PathParser.createPathFromPathData(pathData);

InputStream in = getResources().openRawResource(R.raw.china);
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder documentBuilder = null;
    try {
        documentBuilder = factory.newDocumentBuilder();
        Document document = documentBuilder.parse(in);
        NodeList nodeList = document.getElementsByTagName("path");
        list = new ArrayList<>();
        float left = -1;
        float top = -1;
        float right = -1;
        float bottom = -1;

        for (int i = 0; i < nodeList.getLength(); i++) {
            Element node = (Element) nodeList.item(i);
            //Get the pathData data defined in xml
            String pathData = node.getAttribute("pathData");
            //Use PathParser to convert to Path object.
            Path path = PathParser.createPathFromPathData(pathData);
            String name = node.getAttribute("title");
            ProviceItem item = new ProviceItem();
            item.path = path;
   = name;
            item.drawColor = colorArray[i % colorArray.length];

            RectF rectF = new RectF();
            path.computeBounds(rectF, true);

            //The following is to get the leftmost, uppermost, rightmost and lowermost coordinate values of the whole map.
            left = left == -1 ? rectF.left : Math.min(left, rectF.left);
            top = top == -1 ? : Math.min(top,;
            right = right == -1 ? rectF.right : Math.max(right, rectF.right);
            bottom = bottom == -1 ? rectF.bottom : Math.max(bottom, rectF.bottom);

        //Through coordinate values, a rectangle is constructed, which is the rectangular area to be drawn on the map.
        mapRectF = new RectF(left, top, right, bottom);

    } catch (Exception e) {

Set the obtained list set to the custom MapView.  

public void setData(List<ProviceItem> list, RectF rectF) {
    this.list = list;
    this.mapRectF = rectF;
    if (mapRectF != null) {
        //Get the original size of the map
        double mapWidth = mapRectF.width();
        //Get the zoom ratio to make the drawn map full.
        scale = (float) (mWidth / mapWidth);
    //Call the onDraw method of View
stay onMeasure When I got it MapView Width of.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    mWidth = MeasureSpec.getSize(widthMeasureSpec);

2) Customize View to draw a map. After executing the onDraw method of MapView, the map is displayed on the screen.

protected void onDraw(Canvas canvas) {
    if (list != null && list.size() > 0) {;
        //Scale the canvas according to the obtained scaling ratio
        canvas.scale(scale, scale);
        for (ProviceItem proviceItem : list) {
            //selectItem is the selected item.
            //Call the method of ProviceItem to draw the map of your own province.
            proviceItem.drawItem(canvas, paint, selectItem == proviceItem);

Draw the Path object encapsulated in JavaBean onto the screen through canvas.drawPath(path,paint).

 public void drawItem(Canvas canvas, Paint paint, boolean isSelect) {
        //Selected status:
        if (isSelect) {
            //Clear painted shadows
            paint.setShadowLayer(1, 2, 2, 0xffffff);
            canvas.drawPath(path, paint);
            //Draw the obtained path onto the canvas
            canvas.drawPath(path, paint);
        } else {
            //No status selected
            canvas.drawPath(path, paint);
            //Draw the obtained path onto the canvas
            canvas.drawPath(path, paint);


3) Add click events to each province. First, it is necessary to determine which province map is clicked.

When the ViewGroup performs event distribution processing, judge which View the touch point falls on. The View is a rectangle, which is easy to judge. The province is an irregular figure. How to judge?

Let's take a look at how the ViewGroup handles the placement of click events.

   public boolean pointInView(float localX, float localY, float slop) {
          return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
                  localY < ((mBottom - mTop) + slop);

However, it is necessary to determine which map the touch point falls on, which is similar to the above, but also different.
Generally, the area where the View can receive events is a regular rectangle, but the map is indeed irregular,
Region is used here to respond to irregular views. See the code implementation for specific methods.

Override the onTouchEvent method.

The determination may be made when the finger is pressed or when the finger is slid.

After the finger is lifted, the original state is restored.

public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            //Respond when pressed
            handleOnTouch((int) event.getX(), (int) event.getY());
        case MotionEvent.ACTION_MOVE:
            //Response during movement
            handleOnTouch((int) event.getX(), (int) event.getY());
        case MotionEvent.ACTION_UP:
            //After lifting the finger, it will return to the original state
            selectItem = null;
    return true;

Get the x and y coordinates of the finger landing point and compare them with the region of the province. Region encapsulates a path.

Call region The contents ((int) x, (int) y) method can be compared with the landing point.

ProviceItem selectItem = null;
private void handleOnTouch(int x, int y) {
    if (list == null || list.size() <= 0) {
    ProviceItem select = null;
    for (int i = 0; i < list.size(); i++) {
        ProviceItem item = list.get(i);
        //isTouch returns true, indicating that a map matching the corresponding province responds to the event.
        if (item.isTouch(x / scale, y / scale)) {
            select = item;
            if (select != selectItem) {//If it is determined that the same one is pressed, the drawing will not be triggered repeatedly.
                //Call the onDraw method.
            selectItem = select;

public boolean isTouch(float x, float y) {
    RectF rectF = new RectF();
    //Calculate the margin of the path and store the obtained value in RectF
    path.computeBounds(rectF, true);
    //Create an area the size of RectF rectangle
    Region region = new Region();
    //Set the path of path to region,
    region.setPath(path, new Region((int) rectF.left, (int), (int) rectF.right, (int) rectF.bottom));
    //Through this api, you can determine whether the touch point falls within the area drawn by the path
    return region.contains((int)x, (int)y);

By now, the corresponding core codes of China map drawing and events have been completed.
By analogy, the above code can also be used to draw other irregular graphics such as SVG.
There are three core points: parsing xml, drawing path, and event response.

Demo link:

SVG implementation of Android development map of China - Android document resources - CSDN Download

Tags: Android

Posted by kml2katz on Tue, 23 Aug 2022 13:39:44 +0930