精确控制Android的VectorDrawable动画

22

更新:已找到解决方案!向下滚动查看我的被接受答案!

我想要对一个图像的多个元素进行动画,并将动画链接到ViewPagers位置(因此多个元素会随着当前正在拖动的页面而变形或飞入/飞出)。

那么,有没有一种精确控制动画当前帧的方法?例如,假设我有这个设置:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:interpolator="@android:anim/decelerate_interpolator"
        android:duration="800"
        android:propertyName="scaleY"
        android:valueFrom="0"
        android:valueTo="1" />
    <objectAnimator
        android:interpolator="@android:anim/decelerate_interpolator"
        android:duration="800"
        android:propertyName="scaleX"
        android:valueFrom="0"
        android:valueTo="1" />
    <objectAnimator
        android:interpolator="@android:anim/decelerate_interpolator"
        android:duration="800"
        android:propertyName="rotation"
        android:valueFrom="0"
        android:valueTo="360" />
</set>

动画矢量文件:

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/pre_signup_1" >
    <target
        android:name="plus_button"
        android:animation="@anim/pre_signup_1_plus_container" />
    <target
        android:name="plus"
        android:animation="@anim/pre_signup_1_plus_sign" />
</animated-vector>

运行动画的Java代码:

ImageView mImage1 = (ImageView) findViewById(R.id.image_1);
AnimatedVectorDrawableCompat animated = (AnimatedVectorDrawableCompat) mImage1.getDrawable();

animated.start();

是否有一种方法可以像setCurrentDuration(400)这样控制动画,这将预计将动画的当前状态设置为其一半?也许有一种方法将矢量可绘制对象拆分成层,并以编程方式对它们进行动画处理?提前致谢!

3个回答

13

看起来可以访问VectorDrawableCompat中的路径和分组,并以任何您想要的方式进行动画/变形!

经过一些研究,我复制了android.support.graphics.drawable包中的以下类:AndroidResourcesPathParserTypedArrayUtilsVectorDrawableCommonVectorDrawableCompat

接下来,我们需要在VectorDrawableCompat类内将以下方法和类公开:getTargetByNameVGroupVFullPath

接下来,在VectorDrawableCompat类中删除检查Android版本的块(Build.VERSION.SDK_INT >= 23)。不知道为什么,但如果你不这样做,动画在Android API 23及以上版本上不起作用(需要更多研究)。

我可能错过了一些私有方法,但如果遇到问题,只需使它们公开即可。

现在,我们可以访问VectorDrawable的层次结构!以下是一个小例子,根据ViewPager的位置缩放矢量组:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="276dp"
    android:height="359dp"
    android:viewportWidth="276"
    android:viewportHeight="359">

    <group
        android:pivotX="205.5"
        android:pivotY="214.5"
        android:name="my_group">
        <path
            android:strokeColor="#4D394B"
            android:strokeWidth="7"
            android:strokeLineJoin="bevel"
            android:fillColor="#1ED761"
            android:pathData="M206.5,180 C186.9,180,171,195.9,171,215.5 S186.9,251,206.5,251
C226.1,251,242,235.1,242,215.5 S226.1,180,206.5,180 Z" />
        <path
            android:fillColor="#4D394B"
            android:pathData="M210,211 L210,190 L202,190 L202,211 L181,211 L181,219 L202,219 L202,241 L210,241
L210,219 L232,219 L232,211 Z" />
    </group>
</vector>

以下是用于动画组的代码:

ImageView myImageView;
VectorDrawableCompat.VGroup myVectorGroup;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_welcome);

    myImageView = (ImageView) findViewById(R.id.image_1);

    VectorDrawableCompat vectorDrawable = VectorDrawableCompat.create(getResources(), R.drawable.my_vector_drawable, null);
    vectorDrawable.setAllowCaching(false); // Important to allow image updates
    myVectorGroup = (VectorDrawableCompat.VGroup) vectorDrawable.getTargetByName("my_group");

    myImageView.setImageDrawable(vectorDrawable);

    mViewPager = (ViewPager) findViewById(R.id.pager);
    mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            if(myVectorGroup != null && position < 1) {
                myVectorGroup.setScaleX(1f - positionOffset);
                myVectorGroup.setScaleY(1f - positionOffset);

                myImageView.invalidate();
            }
        }
    });
}

我需要进行更多测试以确定兼容性,但现在它能够工作!


2
还没有尝试过,但是做得很好。很遗憾AnimatedVectorDrawable在开箱即用时缺少这种能力。 - Eurig Jones
1
@Arthur,我没有看到任何插值路径数据的代码,你修改了现有的vectorDrawable代码后。你能否分享整个代码,以便清楚地了解您如何在API 15以上的早期棒棒糖设备中实现pathData变形? - Aftab Ali
我必须说“非常感谢!”非常感谢你的技巧。它对我有用! - hungtdo
Android支持库v26.1.0没有VectorDrawableCompat.VGroup - Vipul Asri
据我所读,目前仍无法像 setCurrentDuration(400) 一样控制 AnimatedVectorDrawable 的当前状态。您只能使用 setScale()setRotation()setTranslate()。例如,您不能根据滑动进度强制执行动画的当前状态。 - DawidJ
显示剩余3条评论

2
我相信您无法通过编程方式使用 AnimatedVectorDrawable/AnimatedVectorDrawableCompat 来完成此操作。
一种解决方法是使用 Level List drawable 并对其 levels 进行动画处理: 但是,您需要为 levels 准备大量的图像以模拟平滑过渡。在此处,我仅使用了三个 levels 进行演示。
首先,您需要像这样定义可绘制级别列表:res/drawable/my_level_list.xml
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/level0"
    android:maxLevel="0"/>
    <item android:drawable="@drawable/level1"
        android:maxLevel="1"/>

    <item android:drawable="@drawable/level2"
        android:maxLevel="2"/>
</level-list>

然后在布局文件中:
<ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:src="@drawable/my_level_list" />

然后在您的活动中:
int MIN_LEVEL=0;
int MAX_LEVEL=2;

ImageView img=(ImageView)findViewById(R.id.img);

Drawable myLevelSetDrawable= img.getDrawable();
ObjectAnimator anim=ObjectAnimator.ofInt(myLevelSetDrawable,"level",MIN_LEVEL,MAX_LEVEL);
anim.setInterpolator(new BounceInterpolator());
AnimatorSet set=new AnimatorSet();
set.play(anim);
set.setDuration(1000);
set.start();

希望有所帮助。

1
根据文档,动态矢量图似乎没有跟踪动画状态或跳转到除animationStart和animationEnd以外的任何状态的方法。您可能需要使用自定义解决方案来实现此效果。这篇博客文章介绍了一种使用Android小部件状态跟踪实现类似效果的方法。

也许有另一种方法来使对象来回运动?我不是指矢量组,而是可能使用单独的“ImageView”? - Arthur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接