2017-04-27 58 views
4

我使用路径变形(仅在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上工作?那么,animationAnimatedVectorDrawable的一个实例。因此,路径变形可以正常工作。

所以我的问题是:为什么不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+上支持路径变体。我想这个问题现在自动解决了(我还没有测试过)。

回答

1

AnimatedVectorDrawableCompat在内部执行版本检查,并且只有在版本为API 24或更高版本(写入时)时才委托给系统实现。

至于推理,它似乎与您链接到的问题中提到的一样,以避免早期API的内置实现出现问题。

对于最近的一个,here's the git commit,它涉及this issue in the issue tracker关于渲染问题。

不幸的是,这意味着修复一些东西也会删除其他功能(例如路径变形)。我认为您在问题中使用的方法类型目前是解决此问题的唯一选择。

+1

那么,这就是最初想到的,但即使在API 22上,也使用了平台的AnimatedVectorDrawable委托。我注意到动画在22到24个思想之间转移到了AnimatorSet的区别。今晚我会更深入地研究它。 –

+0

我做了更多的研究并更新了更多细节。据我所知,在API 22设备上,compat 25和SDK实现最终都读取了同一个“ObjectAnimator” - 一个针对'VectorDrawable',另一个针对'VectorDrawableCompat'。我猜测该平台的ObjectAnimator会进行一些类型检查,并且无法识别compat版本(我还没有证明这一点)。 –

+0

所以,我接受了你的答案,因为你实际上回答了我问的问题(即使它不是我真正想要的*,这是技术性的解释,但我猜你读不出我的想法:-))。我发现了这个问题的技术原因,我将其添加到问题中。 –