背景介绍
我们都知道android中支持svg图片,但是到底支持到什么程度,哪些支持哪些不支持,这个问题最近让我反思了起来
根据经验来讲,android中是支持path,group等标签的,但是这两天在通过android studio转换svg成xml的时候遇到了一系列的问题,从png转换的svg图片在转换Vector Asset的时候无法转化,报错,不支持image等标签.
之前确实没有遇到过这个问题,于是在论坛上查了很多博客,但是没有一篇文章能说出来到底不支持哪些标签,支持哪些标签的(也有可能是我没找到),于是放弃吃现成的,准备手撕源码解决,不就是一个xml转换成VectorDrawable吧,无外乎就是xml解析转换,只要找到它解析什么标签,那不解析的不是自动就不支持了吗.说撕就撕.
注: 本文仅仅为了解决支持标签问题,所以对于VectorDrawable整体的绘制流程不阐述,代码也很少,如果有兴趣可以自行梳理.
另:VectorDrawable的类注释其实已经把大概的内容都交代的差不多了,java的javadoc做的还是很nice的,如果想要直接看官方解析,可以直接查看类注释.
VectorDrawable简介
说白了就是一个drawable就是展示图片用的,继承自Drawable,Drawable也很简单,就是一个抽象类,里面会实现一些常用的如Bounds,配置,方向,alpha值,tint着色等参数的配置.
使用时通过使用VectorDrawableCompat中保存的代理mDelegateDrawable实现
源码分析
定位过程忽略,直接上分析
Let‘s go~!
我们在Drawable中可以找到这么一个方法createFromXml
public static Drawable createFromXml(@NonNull Resources r, @NonNull XmlPullParser parser)
throws XmlPullParserException, IOException {
return createFromXml(r, parser, null);
}
可以看到注释的意思就是通过xml文件创建一个drawable对象, 所以这个方法我们也需要看一下
static Drawable createFromXmlInnerForDensity(@NonNull Resources r, @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, int density, @Nullable Theme theme) throws XmlPullParserException, IOException {
return r.getDrawableInflater().inflateFromXmlForDensity(parser.getName(), parser, attrs,
density, theme);
}
Drawable inflateFromXmlForDensity(@NonNull String name, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, int density, @Nullable Theme theme)
throws XmlPullParserException, IOException {
...
Drawable drawable = inflateFromTag(name);
if (drawable == null) {
drawable = inflateFromClass(name);
}
drawable.inflate(mRes, parser, attrs, theme);
return drawable;
}
private Drawable inflateFromTag(@NonNull String name) {
switch (name) {
case "selector":
return new StateListDrawable();
case "animated-selector":
return new AnimatedStateListDrawable();
case "level-list":
return new LevelListDrawable();
case "layer-list":
return new LayerDrawable();
case "transition":
return new TransitionDrawable();
case "ripple":
return new RippleDrawable();
case "adaptive-icon":
return new AdaptiveIconDrawable();
case "color":
return new ColorDrawable();
case "shape":
return new GradientDrawable();
case "vector":
return new VectorDrawable();
case "animated-vector":
return new AnimatedVectorDrawable();
case "scale":
return new ScaleDrawable();
case "clip":
return new ClipDrawable();
case "rotate":
return new RotateDrawable();
case "animated-rotate":
return new AnimatedRotateDrawable();
case "animation-list":
return new AnimationDrawable();
case "inset":
return new InsetDrawable();
case "bitmap":
return new BitmapDrawable();
case "nine-patch":
return new NinePatchDrawable();
case "animated-image":
return new AnimatedImageDrawable();
default:
return null;
}
}
public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
try {
...
inflateChildElements(r, parser, attrs, theme);
...
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
private void inflateChildElements(Resources res, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
final VectorDrawableState state = mVectorState;
boolean noPathTag = true;
final Stack<VGroup> groupStack = new Stack<VGroup>();
groupStack.push(state.mRootGroup);
int eventType = parser.getEventType();
final int innerDepth = parser.getDepth() + 1;
while (eventType != XmlPullParser.END_DOCUMENT
&& (parser.getDepth() >= innerDepth || eventType != XmlPullParser.END_TAG)) {
if (eventType == XmlPullParser.START_TAG) {
final String tagName = parser.getName();
final VGroup currentGroup = groupStack.peek();
if (SHAPE_PATH.equals(tagName)) {
final VFullPath path = new VFullPath();
path.inflate(res, attrs, theme);
currentGroup.addChild(path);
if (path.getPathName() != null) {
state.mVGTargetsMap.put(path.getPathName(), path);
}
noPathTag = false;
state.mChangingConfigurations |= path.mChangingConfigurations;
} else if (SHAPE_CLIP_PATH.equals(tagName)) {
final VClipPath path = new VClipPath();
path.inflate(res, attrs, theme);
currentGroup.addChild(path);
if (path.getPathName() != null) {
state.mVGTargetsMap.put(path.getPathName(), path);
}
state.mChangingConfigurations |= path.mChangingConfigurations;
} else if (SHAPE_GROUP.equals(tagName)) {
VGroup newChildGroup = new VGroup();
newChildGroup.inflate(res, attrs, theme);
currentGroup.addChild(newChildGroup);
groupStack.push(newChildGroup);
if (newChildGroup.getGroupName() != null) {
state.mVGTargetsMap.put(newChildGroup.getGroupName(),
newChildGroup);
}
state.mChangingConfigurations |= newChildGroup.mChangingConfigurations;
}
} else if (eventType == XmlPullParser.END_TAG) {
final String tagName = parser.getName();
if (SHAPE_GROUP.equals(tagName)) {
groupStack.pop();
}
}
eventType = parser.next();
}
if (noPathTag) {
final StringBuffer tag = new StringBuffer();
if (tag.length() > 0) {
tag.append(" or ");
}
tag.append(SHAPE_PATH);
throw new XmlPullParserException("no " + tag + " defined");
}
}
到这里我们就知道了 VectorDrawable支持path, clip-path和group.当然 这个只是简单的解析,至于xml中的动画 渐变色这些通用标签VectorDrawable应该也是支持的, 如果后续有需要再查找那些标签在哪里调用,在哪里支持的
由于图片处理所以涉及到很多的内部机制以及jni的调用,具体的精髓还是应该在jni中的native方法,慢慢学习
|