精确控制 Android 的 VectorDrawable 动画
Precise control over Androids VectorDrawable animations
更新:找到解决方案!向下滚动查看我接受的答案!
我想为一个图像的多个元素设置动画,并且 link 动画到 ViewPagers 位置(因此多个元素变形或飞行 in/out 取决于当前被拖动的页面)。
那么,有没有办法精确控制动画的当前帧呢?例如,假设我有这个集合:
<?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)
那样控制动画,这大概会将动画的当前状态设置为一半?也许有一种方法可以将可绘制的矢量拆分为图层并以编程方式为它们设置动画?提前致谢!
就 AnimatedVectorDrawables 的文档而言,除了 animationStart 和 animationEnd 之外,无法跟踪动画状态或跳转到任何状态。
您可能必须使用自定义解决方案才能实现此效果。 This blog post 解释了一种使用 Android 的小部件状态跟踪来实现类似效果的方法。
我相信您不能以编程方式使用 AnimatedVectorDrawable/AnimatedVectorDrawableCompat 执行此操作。
解决方法是使用 Level List drawable 并为其 级别设置动画 :
但是你需要大量的图像来模拟平滑的过渡,我在这里只使用三个级别进行演示。
首先,您将像这样定义可绘制级别列表: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" />
然后在你的 activity:
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();
希望对您有所帮助。
看起来可以随心所欲地访问 VectorDrawableCompat
和 animate/morph 中的路径和组!
经过一些研究,我最终从 android.support.graphics.drawable
包中复制了以下 classes:AndroidResources
、PathParser
、TypedArrayUtils
、VectorDrawableCommon
和 VectorDrawableCompat
.
接下来我们需要在VectorDrawableCompat
class中classespublic做如下方法:getTargetByName
,VGroup
和 VFullPath
.
接下来在 VectorDrawableCompat
class 中删除检查 Android 版本 (Build.VERSION.SDK_INT >= 23
) 的块。不知道为什么,但如果你不这样做,动画将无法在 android API 23 岁及以上(需要更多研究)。
我可能遗漏了几个私有方法,但如果您 运行 遇到问题,只需将它们制作 public 即可。
所以,现在我们可以访问 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();
}
}
});
}
我需要更多的测试来确定兼容性,但现在可以用了!
更新:找到解决方案!向下滚动查看我接受的答案!
我想为一个图像的多个元素设置动画,并且 link 动画到 ViewPagers 位置(因此多个元素变形或飞行 in/out 取决于当前被拖动的页面)。
那么,有没有办法精确控制动画的当前帧呢?例如,假设我有这个集合:
<?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)
那样控制动画,这大概会将动画的当前状态设置为一半?也许有一种方法可以将可绘制的矢量拆分为图层并以编程方式为它们设置动画?提前致谢!
就 AnimatedVectorDrawables 的文档而言,除了 animationStart 和 animationEnd 之外,无法跟踪动画状态或跳转到任何状态。
您可能必须使用自定义解决方案才能实现此效果。 This blog post 解释了一种使用 Android 的小部件状态跟踪来实现类似效果的方法。
我相信您不能以编程方式使用 AnimatedVectorDrawable/AnimatedVectorDrawableCompat 执行此操作。
解决方法是使用 Level List drawable 并为其 级别设置动画 : 但是你需要大量的图像来模拟平滑的过渡,我在这里只使用三个级别进行演示。
首先,您将像这样定义可绘制级别列表: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" />
然后在你的 activity:
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();
希望对您有所帮助。
看起来可以随心所欲地访问 VectorDrawableCompat
和 animate/morph 中的路径和组!
经过一些研究,我最终从 android.support.graphics.drawable
包中复制了以下 classes:AndroidResources
、PathParser
、TypedArrayUtils
、VectorDrawableCommon
和 VectorDrawableCompat
.
接下来我们需要在VectorDrawableCompat
class中classespublic做如下方法:getTargetByName
,VGroup
和 VFullPath
.
接下来在 VectorDrawableCompat
class 中删除检查 Android 版本 (Build.VERSION.SDK_INT >= 23
) 的块。不知道为什么,但如果你不这样做,动画将无法在 android API 23 岁及以上(需要更多研究)。
我可能遗漏了几个私有方法,但如果您 运行 遇到问题,只需将它们制作 public 即可。
所以,现在我们可以访问 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();
}
}
});
}
我需要更多的测试来确定兼容性,但现在可以用了!