学习android动画中发现svg矢量动画效果还不错。W3c中有完整的介绍。跟着api进行学习
进入w3c
SVG 指可伸缩矢量图形 (Scalable Vector Graphics)
SVG 用来定义用于网络的基于矢量的图形
SVG 使用 XML 格式定义图形
SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失
SVG 是万维网联盟的标准
SVG 与诸如 DOM 和 XSL 之类的 W3C 标准是一个整体
在 2003 年一月,SVG 1.1 被确立为 W3C 标准。
参与定义 SVG 的组织有:太阳微系统、Adobe、苹果公司、IBM 以及柯达。
与其他图像格式相比,使用 SVG 的优势在于:
SVG 可被非常多的工具读取和修改(比如记事本)
SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性更强。
SVG 是可伸缩的
SVG 图像可在任何的分辨率下被高质量地打印
SVG 可在图像质量不下降的情况下被放大
SVG 图像中的文本是可选的,同时也是可搜索的(很适合制作地图)
SVG 可以与 Java 技术一起运行
SVG 是开放的标准
SVG 文件是纯粹的 XML
SVG 的主要竞争者是 Flash。
与 Flash 相比,SVG 最大的优势是与其他标准(比如 XSL 和 DOM)相兼容。而 Flash 则是未开源的私有技术。
<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 标示符号 <svg>,svg>
<circle cx="100" cy="50" r="40" stroke="black"
stroke-width="2" fill="red"/>
这里我们只讨论客户端的实现方案,svg最主要的是Svg Path 路径点。也就是动画点。
1.使用具体的工具去画矢量图, PS也可以,生成路径–导出路径留待使用
2.使用图片最背景选取具体的绘制路径的点,并生成路径,推荐GIMP (ps也同样可以)
这里使用GIMP 进行举例子,photoshop 需要下载svg插件。
SVG路径
<svg xmlns="http://www.w3.org/2000/svg"
width="3.02778in" height="3.16667in"
viewBox="0 0 218 228">
<path id="选区"
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>
格式:
M = moveto, 相当于android 里的 moveTo()
L = lineto, 相当于lineTo()进行画直线
H = horizontal lineto, 画水平直线
V = vertical lineto, 画竖直直线
C = curveto, 相当于android 里的 cubicTo()
S = smooth curveto
Q = quadratic Belzier curve
T = smooth quadratic Belzier curveto
A = elliptical Arc
Z = closepath
在这里需要将svg文件转换为能别Paint画笔处理具体值,这里使用网上给出的一个封装好的工具类进行解析path处理
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;
// 具体的数值或".""-"
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;
// 要解析的path 字符串
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指令,相当于android 里的 moveTo()
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指令,相当于android 里的 cubicTo()
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': {
// 相当于lineTo()进行画直线
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': {
// 画水平直线
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': {
// 画竖直直线
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': {
// 封闭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;
}
}
转到studio中将路径值保存在String中留待使用。
"app_name">demo
"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
万事具备只差画出来了
在自定义view onDraw()方法调用系统方法
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);
至于动画效果这里不做深入,感兴趣自行阅读引用相关的实现代码。
引用
Svg 使用 http://blog.csdn.net/tianjian4592/article/details/44538605
Svg demo https://github.com/unclepizza/SVGTraceDemo.git