我使用路径变形(仅在API 21及更高版本上可用)编写了一个可绘制的动画矢量。我有一个使用简单旋转API的21岁以下的后备动画。我使用动画矢量可绘制支持库(com.android.support:animated-vector-drawable:25.3.1
)。使用compat库甚至在API 22设备上绘制的动画矢量
这里是我开始动画:
mBinding.switchIcon.setImageResource(R.drawable.ic_animated_vertical_arrow_down_to_up_32dp);
final Drawable animation = mBinding.switchIcon.getDrawable();
if (animation instanceof Animatable) {
((Animatable) animation).start();
}
这适用于API 19和24罚款,但不会对API 22也23工作(我没有一个API 21设备去测试)。
API 19的情况是逻辑的:动画很简单,完全由支持库处理,它工作。大。我期望任何API 21及以上的设备选择内置的矢量可绘制实现。但是,在调试时,我可以看到animation
实际上是AnimatedVectorDrawableCompat
的一个实例:因此,它不支持路径变形,并且动画不起作用。
那么为什么它在API 24上工作?那么,animation
是AnimatedVectorDrawable
的一个实例。因此,路径变形可以正常工作。
所以我的问题是:为什么不API 21-23设备拿起内置的实现,并依赖于支持库,而一个API 24设备不把它捡起来?
作为一个侧面说明,强制设备挑内置的实施确实明显作品:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { AnimatedVectorDrawable drawable = (AnimatedVectorDrawable) getDrawable(R.drawable.ic_animated_vertical_arrow_down_to_up_32dp); mBinding.switchIcon.setImageDrawable(drawable); } else { mBinding.switchIcon.setImageResource(R.drawable.ic_animated_vertical_arrow_down_to_up_32dp); } final Drawable animation = mBinding.switchIcon.getDrawable(); if (animation instanceof Animatable) { ((Animatable) animation).start(); }
我也发现了这个(可能)相关的问题对谷歌的错误-tracker:https://issuetracker.google.com/issues/37116940
使用调试器,我可以确认在API 22(也可能是23)上,支持库确实将工作委托给SD K的
AnimatorSet
。我真的不明白行为改变。
关于接下来
这些都是一些笔记我想可能是有趣的分享,这是我花了,而在这个问题上的技术说明调查。在接受的答案中总结了相互之间的技术性差异。
这里是我使用的AVD,以供参考:
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:alpha="1">
<group android:name="group">
<path
android:name="path"
android:pathData="@string/vertical_arrow_up_path"
android:strokeColor="#000000"
android:strokeWidth="2"
android:strokeLineCap="square"/>
</group>
</vector>
</aapt:attr>
<target android:name="path">
<aapt:attr name="android:animation">
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="path"
android:propertyName="pathData"
android:duration="300"
android:valueFrom="@string/vertical_arrow_up_path"
android:valueTo="@string/vertical_arrow_down_path"
android:valueType="pathType"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"/>
</aapt:attr>
</target>
</animated-vector>
而且两者路径资源:
<string name="vertical_arrow_up_path" translatable="false">M 7.41 10 L 12 14.585 L 16.59 10</string>
<string name="vertical_arrow_down_path" translatable="false">M 7.41 14.585 L 12 10 L 16.59 14.585</string>
上的API 22设备,无论是建-in和支持版本(25.3。1)似乎从我的AVD上面膨胀了相同的Animator
,虽然有着不同的等级。
的支持版本(25.3.1),该AnimatorSet
只有一个节点:含有本身单个动画的AnimatorSet
,看似匹配在AVD的XML描述的ObjectAnimator
。其referent
设置为VectorDrawableCompat
,属性名称为pathData
,值列表包含带有两个关键帧的单个PropertyValuesHolder
,匹配我的开始和结束路径。结果:不起作用。
通过内置的版本(SDK 22),这是不完全一样(但AnimatorSet
不完全在同一个地方,所以......):在AnimatedVectorDrawableState
,该mAnimators
列表中有1元,也就是直接ObjectAnimator
(与支持版本相同的值)。结果:作品。
我能看到的唯一相关差异是PropertyValuesHolder
中的ValueAnimator
。由于它对drawable有一定的参考,我想可能有一些typecheck忽略VectorDrawable
类的支持库版本。但那是纯粹的猜测。我会继续挖掘...
我终于得到它(并接受@ LewisMcGeary的答案,因为我没有在这个问题上,我一直在寻找问题背后的技术位提)。这是发生了什么。如前所述,在API 21-23上,支持库正在接管SDK的实现,以避免所述实现中的错误。所以我们使用AnimatedVectorDrawableCompat
和其他[whatever]Compat
类。一旦矢量本身被加载,这就是动画的轮到。
无论我们所使用的API级别如何(至少在21+以上,但我猜它在19岁以下都是同样的事情),动画被委托给SDK的ObjectAnimator
。要为原始类型设置动画效果,ObjectAnimator
有一个调用函数的内部映射来更改值。但是,对于复杂类型,它依赖于具有具有的特定方法签名以存在于动画对象上。这里的方法映射值类型对应的方法来调用,从PropertyValuesHolder
(SDK,API 22):
private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
// TODO: faster implementation...
Method returnVal = null;
String methodName = getMethodName(prefix, mPropertyName);
Class args[] = null;
if (valueType == null) {
try {
returnVal = targetClass.getMethod(methodName, args);
} catch (NoSuchMethodException e) {
// Swallow the error, log it later
}
} else {
args = new Class[1];
Class typeVariants[];
if (valueType.equals(Float.class)) {
typeVariants = FLOAT_VARIANTS;
} else if (valueType.equals(Integer.class)) {
typeVariants = INTEGER_VARIANTS;
} else if (valueType.equals(Double.class)) {
typeVariants = DOUBLE_VARIANTS;
} else {
typeVariants = new Class[1];
typeVariants[0] = valueType;
}
for (Class typeVariant : typeVariants) {
args[0] = typeVariant;
try {
returnVal = targetClass.getMethod(methodName, args);
if (mConverter == null) {
// change the value type to suit
mValueType = typeVariant;
}
return returnVal;
} catch (NoSuchMethodException e) {
// Swallow the error and keep trying other variants
}
}
// If we got here, then no appropriate function was found
}
if (returnVal == null) {
Log.w("PropertyValuesHolder", "Method " +
getMethodName(prefix, mPropertyName) + "() with type " + valueType +
" not found on target class " + targetClass);
}
return returnVal;
}
有趣的部分是for
环设法满足任何潜在的typeVariants
我们的目标类。在此特定情况下,typeVariants
仅包含一个Class
对象:android.util.PathParser$PathDataNode
。我们试图调用方法的类(targetClass
)是我们的Compat类:android.support.graphics.drawable.VectorDrawableCompat$VFullPath
。我们正在寻找的方法(methodName
)是setPathData
。
可悲的是,VectorDrawableCompat$VFullPath.setPathData
的签名不匹配:public void android.support.graphics.drawable.VectorDrawableCompat$VPath.setPathData(android.support.graphics.drawable.PathParser$PathDataNode[])
我们只有typeVariants
数组中有一个项目,returnVal
结束是null
,并在年底,ObjectAnimator
绝对没有办法知道如何更新我们的VectorDrawableCompat
的路径数据。
那么从哪里来的typeVariants
的内容? android.util.PathParser$PathDataNode
而不是支持一个?这是因为动画充气的方式。正如我们所看到的,AnimatedVectorDrawableCompat
正在将许多工作委托给SDK,这就是为什么有些东西不适用于API 19和更低版本。当读取其XML的target
节点,所述Animator
由SDK充气:
Animator objectAnimator = AnimatorInflater.loadAnimator(mContext, id);
的AnimatorInflater
来自SDK,并且因此膨胀一个android.util.PathParser$PathDataNode
代替android.support.graphics.drawable.PathParser$PathDataNode
的。我想这是唯一可能的解决将是谷歌在支持库的AnimatorInflater
整合...
因此,我们正处在一个艰难的位置在这里。 Google承认SDK 21-23的VectorDrawable
实现包含错误(我注意到在某些SVG上的API 22上存在一些绘图问题),但我们无法使用支持库中的所有内容。所以,请记住,测试19(或以下),21,22,23 和 24(或以上)是当它涉及到VectorDrawable
的只有我的强制性...
编辑:截至今天( 09/06/2017),Google发布了支持库25.4,后者在API 14+上支持路径变体。我想这个问题现在自动解决了(我还没有测试过)。
那么,这就是最初想到的,但即使在API 22上,也使用了平台的AnimatedVectorDrawable委托。我注意到动画在22到24个思想之间转移到了AnimatorSet的区别。今晚我会更深入地研究它。 –
我做了更多的研究并更新了更多细节。据我所知,在API 22设备上,compat 25和SDK实现最终都读取了同一个“ObjectAnimator” - 一个针对'VectorDrawable',另一个针对'VectorDrawableCompat'。我猜测该平台的ObjectAnimator会进行一些类型检查,并且无法识别compat版本(我还没有证明这一点)。 –
所以,我接受了你的答案,因为你实际上回答了我问的问题(即使它不是我真正想要的*,这是技术性的解释,但我猜你读不出我的想法:-))。我发现了这个问题的技术原因,我将其添加到问题中。 –