Java 即使在API 22设备上也可以使用compat库绘制动画矢量
我使用路径变形(仅在API21及以上版本中可用)编写了一个可绘制的动画向量。我有一个后备动画,使用了API低于21的简单旋转。我正在使用动画矢量可绘制支持库(Java 即使在API 22设备上也可以使用compat库绘制动画矢量,java,android,animation,android-vectordrawable,Java,Android,Animation,Android Vectordrawable,我使用路径变形(仅在API21及以上版本中可用)编写了一个可绘制的动画向量。我有一个后备动画,使用了API低于21的简单旋转。我正在使用动画矢量可绘制支持库(com.android.support:animated vector drawable:25.3.1) 下面是我如何开始动画的: mBinding.switchIcon.setImageResource(R.drawable.ic_animated_vertical_arrow_down_to_up_32dp); final Drawa
com.android.support:animated vector drawable:25.3.1
)
下面是我如何开始动画的:
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设备)
API19案例是合乎逻辑的:动画很简单,由支持库完美处理,可以正常工作。太好了
我希望任何API21及以上的设备都会选择内置的矢量可绘制实现。但是,在调试时,我可以看到动画
实际上是AnimatedVectorDrawableCompat
的一个实例:因此,它不支持路径变形,动画也不工作
那么为什么它在API24上工作呢?嗯,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(); }
- 我也在Google bug tracker上发现了这个(可能)相关的问题:
- 使用调试器,我可以确认在API 22(可能还有23)上,支持库确实将工作委托给SDK的
。我真的不明白这种行为的改变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>
有趣的部分是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
而不是support?这是因为动画的膨胀方式<正如我们所看到的,code>AnimatedVectorDrawableCompat将大部分工作委托给SDK,这就是为什么有些东西在API19及以下版本上不起作用的原因。读取XML的目标
节点时,SDK会对动画制作程序
进行充气:
Animator objectAnimator = AnimatorInflater.loadAnimator(mContext, id);
AnimatorInflater
来自SDK,因此正在膨胀一个android.util.PathParser$PathDataNode
,而不是android.support.graphics.drawable.PathParser$PathDataNode
。我想唯一可能的解决办法是谷歌在支持库中集成AnimatorInflater
所以我们现在处境艰难。谷歌承认SDK21-23的
VectorDrawable
实现包含bug(我注意到一些SVG的API 22上存在一些绘图问题),但我们也不能使用支持库中的所有内容。因此,请记住,对于VectorDrawable
s,在19(或以下)、21、22、23和24(或以上)上进行测试只是强制性的
编辑:截至今天(2017年6月9日),谷歌发布了支持库25.4,支持API 14+上的路径变形。我想这个问题现在已经自动解决了(我还没有测试过)。AnimatedVectorDrawableCompat只在版本为API 24或更高版本时(在编写本文时)才在内部进行版本检查并委托给系统实现 至于原因,它似乎与您链接的问题中提到的一样,以避免早期API的内置实现出现问题 对于最近的一个,它指的是关于渲染问题
不幸的是,这确实意味着修复某些东西也会删除其他功能(例如路径变形)。我认为您在问题中使用的方法类型实际上是目前唯一可以绕过此问题的方法。好吧,这是o最初的想法,但即使在API 22上,也使用了平台的
动画矢量绘制
委托。我注意到转移到AnimatorSet
的动画在22和24之间存在差异。今晚我会更深入地探讨这个问题。我做了更多的研究,并用更多的细节更新了这个问题。据我所见,在API 22设备上,compat 25和SDK实现最终读取相同的ObjectAnimator
——一个目标是VectorDrawable
,另一个目标是VectorDrawable compat
。我猜平台的ObjectAnimator
进行了一些类型检查,无法识别compat版本(我还没有证据)。因此,我接受了你的回答,因为你实际上回答了我提出的问题(即使这不是我真正想要的,这是技术问题)
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;
}
Animator objectAnimator = AnimatorInflater.loadAnimator(mContext, id);