android SVG Vector Animation

Posted by johnie on Wed, 10 Jul 2019 22:53:15 +0200

Preface

Studying android animation shows that svg vector animation works well. W3c has a complete introduction. Follow the api

What is SVG?

Enter w3c

SVG refers to Scalable Vector Graphics.
SVG is used to define vector-based graphics for networks
 SVG defines graphics in XML format
 The quality of SVG image will not be lost when it is enlarged or changed in size.
SVG is the standard of World Wide Web Alliance
 SVG is integrated with W3C standards such as DOM and XSL

The History and Advantages of SVG

In January 2003, SVG 1.1 was established as the W3C standard.
Organizations involved in defining SVG include Sun Microsystems, Adobe, Apple, IBM and Kodak.
Compared with other image formats, the advantages of using SVG are:

SVG can be read and modified by many tools (such as notepad)
Compared with JPEG and GIF images, SVG is smaller in size and more compressible.
SVG is scalable
 SVG images can be printed with high quality at any resolution
 SVG can be enlarged without degradation of image quality
 Text in SVG images is optional and searchable (suitable for making maps)
SVG can run with Java Technology
 SVG is an open standard
 SVG files are pure XML
 The main competitor of SVG is Flash.

Compared with Flash, the biggest advantage of SVG is compatibility with other standards, such as XSL and DOM. Flash is an open source private technology.

Svg format

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg width="100%" height="100%" version="1.1"
xmlns="http://www.w3.org/2000/svg">

<circle cx="100" cy="50" r="40" stroke="black"
stroke-width="2" fill="red"/>

</svg>

Svg Symbol <svg>,</svg>
<circle cx="100" cy="50" r="40" stroke="black"
stroke-width="2" fill="red"/>

Simple animation control

Here we only discuss the implementation of the client. The main point of svg is the path point of Svg Path. That's the animation point.

Effect:

1. How does path come from?

1. Using specific tools to draw vector maps, PS can also generate paths - export paths for use

2. Use the background of the picture to select the point of the specific rendering path and generate the path, recommend GIMP (ps can also be used)

For example, using GIMP, photoshop needs to download the svg plug-in.

The third problem is how to convert the SVG path to Android. The w3c defines it.

SVG Path

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
              "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">

<svg xmlns="http://www.w3.org/2000/svg"
     width="3.02778in" height="3.16667in"
     viewBox="0 0 218 228">
  <path id="Constituency"
        fill="none" stroke="black" stroke-width="1"
        d="M 218.00,0.00
           C 218.00,0.00 218.00,228.00 218.00,228.00
             218.00,228.00 0.00,228.00 0.00,228.00
             0.00,228.00 0.00,0.00 0.00,0.00
             0.00,0.00 218.00,0.00 218.00,0.00 Z
           M 85.00,49.00
           C 74.10,43.40 58.80,29.70 48.00,27.00
             47.37,32.11 49.50,33.50 53.00,36.99
             56.74,40.70 69.43,51.00 70.91,55.00
             72.37,58.95 66.69,71.89 61.00,73.75
             58.27,74.64 54.63,72.09 52.00,71.00
             ...
             ...
             ...
            C 171.48,178.95 171.13,180.88 171.76,183.00
             172.25,184.63 173.11,185.64 174.00,187.00
             171.36,190.02 171.47,190.34 173.00,194.00
             162.60,203.60 182.46,205.63 188.77,200.99
             193.07,197.84 192.64,192.04 186.98,190.14
             185.36,189.97 182.72,190.01 181.00,190.14
             181.00,190.14 177.00,190.14 177.00,190.14
             177.00,190.14 177.00,188.00 177.00,188.00
             184.57,187.98 190.42,187.53 189.00,178.00
             189.00,178.00 192.00,175.00 192.00,175.00
             185.59,174.69 177.39,170.44 172.74,177.13 Z" />
</svg>

2.SVG paths are converted to points in Android that can be Paint draw s

Format:

M = moveto, Amount to android Li moveTo()

L = lineto, Amount to lineTo()Draw straight lines

H = horizontal lineto, Draw horizontal lines

V = vertical lineto, Draw vertical lines

C = curveto, Amount to android Li cubicTo()

S = smooth curveto

Q = quadratic Belzier curve

T = smooth quadratic Belzier curveto

A = elliptical Arc

Z = closepath

Here we need to convert svg file to Paint brush processing specific value. Here we use a packaged tool class on the Internet to parse path processing.

package com.gaok.ui.svgtrace.utils;

import android.graphics.Path;
import android.graphics.PointF;

public class SvgPathParser {

    private static final int TOKEN_ABSOLUTE_COMMAND = 1;
    private static final int TOKEN_RELATIVE_COMMAND = 2;
    // Specific values or ".""-"
    private static final int TOKEN_VALUE = 3;
    private static final int TOKEN_EOF = 4;

    private int mCurrentToken;
    private PointF mCurrentPoint = new PointF();
    private int mLength;
    private int mIndex;
    // The path string to parse
    private String mPathString;
    private float scale = 4f;
    private float xMin;
    private float xMax;
    private float yMin;
    private float yMax;

    protected float transformX(float x) {
        return x * scale;
    }

    protected float transformY(float y) {
        return y * scale;
    }

    public Path parsePath(String s) {
        try {
            mCurrentPoint.set(Float.NaN, Float.NaN);
            mPathString = s;
            mIndex = 0;
            mLength = mPathString.length();

            PointF tempPoint1 = new PointF();
            PointF tempPoint2 = new PointF();
            PointF tempPoint3 = new PointF();

            Path p = new Path();
            p.setFillType(Path.FillType.WINDING);

            boolean firstMove = true;
            while (mIndex < mLength) {
                char command = consumeCommand();
                boolean relative = (mCurrentToken == TOKEN_RELATIVE_COMMAND);
                switch (command) {
                    case 'M':
                    case 'm': {
                        // m instruction, equivalent to moveTo() in android
                        boolean firstPoint = true;
                        while (advanceToNextToken() == TOKEN_VALUE) {
                            consumeAndTransformPoint(tempPoint1, relative && mCurrentPoint.x != Float.NaN);
                            if (firstPoint) {
                                p.moveTo(tempPoint1.x, tempPoint1.y);
                                firstPoint = false;
                                if (firstMove) {
                                    mCurrentPoint.set(tempPoint1);
                                    firstMove = false;
                                }
                            } else {
                                p.lineTo(tempPoint1.x, tempPoint1.y);
                            }
                        }
                        mCurrentPoint.set(tempPoint1);
                        break;
                    }

                    case 'C':
                    case 'c': {
                        // c instruction, equivalent to cubicTo() in android
                        if (mCurrentPoint.x == Float.NaN) {
                            throw new Exception("Relative commands require current point");
                        }

                        while (advanceToNextToken() == TOKEN_VALUE) {
                            consumeAndTransformPoint(tempPoint1, relative);
                            consumeAndTransformPoint(tempPoint2, relative);
                            consumeAndTransformPoint(tempPoint3, relative);
                            p.cubicTo(tempPoint1.x, tempPoint1.y, tempPoint2.x, tempPoint2.y, tempPoint3.x, tempPoint3.y);
                        }
                        mCurrentPoint.set(tempPoint3);
                        break;
                    }

                    case 'L':
                    case 'l': {
                        // It is equivalent to lineTo() to draw a straight line.
                        if (mCurrentPoint.x == Float.NaN) {
                            throw new Exception("Relative commands require current point");
                        }

                        while (advanceToNextToken() == TOKEN_VALUE) {
                            consumeAndTransformPoint(tempPoint1, relative);
                            p.lineTo(tempPoint1.x, tempPoint1.y);
                        }
                        mCurrentPoint.set(tempPoint1);
                        break;
                    }

                    case 'H':
                    case 'h': {
                        // Draw horizontal lines
                        if (mCurrentPoint.x == Float.NaN) {
                            throw new Exception("Relative commands require current point");
                        }

                        while (advanceToNextToken() == TOKEN_VALUE) {
                            float x = transformX(consumeValue());
                            if (relative) {
                                x += mCurrentPoint.x;
                            }
                            p.lineTo(x, mCurrentPoint.y);
                        }
                        mCurrentPoint.set(tempPoint1);
                        break;
                    }

                    case 'V':
                    case 'v': {
                        // Draw vertical lines
                        if (mCurrentPoint.x == Float.NaN) {
                            throw new Exception("Relative commands require current point");
                        }

                        while (advanceToNextToken() == TOKEN_VALUE) {
                            float y = transformY(consumeValue());
                            if (relative) {
                                y += mCurrentPoint.y;
                            }
                            p.lineTo(mCurrentPoint.x, y);
                        }
                        mCurrentPoint.set(tempPoint1);
                        break;
                    }

                    case 'Z':
                    case 'z': {
                        // Closed path
                        p.close();
                        break;
                    }
                }
            }

            return p;
        } catch (Exception e) {

        }
        return null;
    }

    private int advanceToNextToken() {
        while (mIndex < mLength) {
            char c = mPathString.charAt(mIndex);
            if ('a' <= c && c <= 'z') {
                return (mCurrentToken = TOKEN_RELATIVE_COMMAND);
            } else if ('A' <= c && c <= 'Z') {
                return (mCurrentToken = TOKEN_ABSOLUTE_COMMAND);
            } else if (('0' <= c && c <= '9') || c == '.' || c == '-') {
                return (mCurrentToken = TOKEN_VALUE);
            }

            ++mIndex;
        }

        return (mCurrentToken = TOKEN_EOF);
    }

    private char consumeCommand() throws Exception {
        advanceToNextToken();
        if (mCurrentToken != TOKEN_RELATIVE_COMMAND && mCurrentToken != TOKEN_ABSOLUTE_COMMAND) {
            throw new Exception("Expected command");
        }

        return mPathString.charAt(mIndex++);
    }

    private void consumeAndTransformPoint(PointF out, boolean relative) throws Exception {
        float xValue = consumeValue();
        out.x = transformX(xValue);
        if (out.x < xMin) {
            xMin = out.x;
        } else if (out.x > xMax) {
            xMax = out.x;
        }
        float yValue = consumeValue();
        out.y = transformY(yValue);
        if (out.y < yMin) {
            yMin = out.y;
        } else if (out.y > yMax) {
            yMax = out.y;
        }
        if (relative) {
            out.x += mCurrentPoint.x;
            out.y += mCurrentPoint.y;
        }
    }

    private float consumeValue() throws Exception {
        advanceToNextToken();
        if (mCurrentToken != TOKEN_VALUE) {
            throw new Exception("Expected value");
        }

        boolean start = true;
        boolean seenDot = false;
        int index = mIndex;
        while (index < mLength) {
            char c = mPathString.charAt(index);
            if (!('0' <= c && c <= '9') && (c != '.' || seenDot) && (c != '-' || !start)) {
                break;
            }
            if (c == '.') {
                seenDot = true;
            }
            start = false;
            ++index;
        }

        if (index == mIndex) {
            throw new Exception("Expected value");
        }

        String str = mPathString.substring(mIndex, index);
        try {
            float value = Float.parseFloat(str);
            mIndex = index;
            return value;
        } catch (NumberFormatException e) {
            throw new Exception("Invalid float value '" + str + "'.");
        }
    }

    public float getPathWidth() {
        return xMax - xMin;
    }

    public float getPathHeight() {
        return yMax - yMin;
    }
}

Go to studio and save the path values in String for later use.

<resources>
    <string name="app_name">demo</string>
    <string name="path">
     M 85.00,34.57
           C 90.35,27.78 95.91,20.00 99.32,12.00
             100.42,9.42 101.66,3.01 102.93,1.60
             ...
             ...
             ...
           M 238.00,387.00
           C 238.00,387.00 239.00,388.00 239.00,388.00
             239.00,388.00 239.00,387.00 239.00,387.00
             239.00,387.00 238.00,387.00 238.00,387.00 Z

    </string>
</resources>

Everything's OK but it's just a blueprint.

3. How to draw to the screen?

Call system methods in the custom view onDraw () method

 public void drawPath(@NonNull Path path, @NonNull Paint paint) {
        if (path.isSimplePath && path.rects != null) {
            native_drawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
        } else {
            native_drawPath(mNativeCanvasWrapper, path.readOnlyNI(), paint.getNativeInstance());
        }
}
canvas.drawPath(mSvgPath, mPaint);

As for the animation effect, I'm not going to go deep here. I'm interested in reading and referring to the relevant implementation code by myself.

Quote
Svg Use http://blog.csdn.net/tianjian4592/article/details/44538605
Svg demo https://github.com/unclepizza/SVGTraceDemo.git

Topics: Android xml Java encoding