Android——动画简述,属性动画用法概述
动画简述
Android动画大致分为帧动画、补间动画和属性动画三种
一、帧动画(Drawable Animation)
二、补间动画(Tween Animation)
补间动画一共可以分成四类:
- 透明度动画 – AlphaAnimation
- 缩放动画 – ScaleAnimation
- 旋转动画 – RotateAnimation
- 位移动画 – TranslateAnimation
**注:**用xml
实现补间动画,需要将xml放到res
下的anim
目录,Android工程默认是没有anim
文件夹的,在读取文件之前需要先把anim
文件夹以及文件建好
用代码实现补间动画主要是通过构造xxxAnimation实例,然后去设置动画的一些属性
通用的xml属性及对应方法如下:
xml属性 | 方法 | 解释 |
---|---|---|
android:duration | setDuration(long durationMillis) | 设置动画持续时间,单位毫秒 |
android:fillAfter | setFillAfter(boolean fillAfter) | 如果设置为true,控件动画结束时,将保持动画最后时的状态 |
android:fillBefore | setFillBefore(boolean fillBefore) | 如果设置为true,控件动画结束时,还原到开始动画前的状态 |
android:repeatCount | setRepeatCount(int repeatCount) | 设置动画的重复次数,当传入值小于0时,例如Animation.INFINITE(其实就是-1),无限重复 注:传入值为重复次数,例如传入1,动画会播放两遍 |
android:repeatMode | setRepeatMode(int repeatMode) | 设置动画的重复类型,例如 Animation.RESTAR:倒序回放动画 Animation.RESTAR:重新播放动画 注:该方法必须与setRepeatCount方法一起使用 |
(1)透明度动画 – AlphaAnimation
xml实现:
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromAlpha="1.0"
android:toAlpha="0.1"
android:duration="2000"/>
ImageView image = findViewById(R.id.image);
Button button1 = findViewById(R.id.button);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Animation animation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.anim_alpha);
image.startAnimation(animation);
}
});
代码实现:
public AlphaAnimation(float fromAlpha, float toAlpha) {
...
}
和上述xml效果相同
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView image = findViewById(R.id.image);
Animation alphaAnimation = new AlphaAnimation(1.0F, 0.1F);
alphaAnimation.setDuration(2000);
image.setAnimation(alphaAnimation);
}
}
xml属性 | 代码对应参数 | 含义 |
---|---|---|
android:fromAlpha | fromAlpha | 起始透明度(透明度的范围为:0-1,完全透明-完全不透明) |
android:toAlpha | toAlph | 结束透明度 |
(2)缩放动画 – ScaleAnimation
xml实现:
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:fromXScale="0.2"
android:toXScale="1.5"
android:fromYScale="0.2"
android:toYScale="1.5"
android:pivotX="50%"
android:pivotY="50%"
android:duration="2000"/>
attrs.xm中TranslateAnimation内容如下,即:
- 输入float:单位为像素
- 输入fraction(百分数):即当前组件尺寸的百分比
- dimension:可以输入xxdp
<declare-styleable name="ScaleAnimation">
<attr name="fromXScale" format="float|fraction|dimension" />
<attr name="toXScale" format="float|fraction|dimension" />
<attr name="fromYScale" format="float|fraction|dimension" />
<attr name="toYScale" format="float|fraction|dimension" />
<attr name="pivotX" />
<attr name="pivotY" />
</declare-styleable>
代码实现:
public ScaleAnimation(float fromX, float toX, float fromY, float toY,
int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) {
...
}
xml属性 | 对应参数 | 解释 |
---|---|---|
android:fromXScale | fromX | 沿着X轴缩放的起始比例 |
android:toXScale | toX | 沿着X轴缩放的结束比例 |
android:fromYScale | fromY | 沿着Y轴缩放的起始比例 |
android:toYScale | toY | 沿着Y轴缩放的结束比例 |
android:pivotX | pivotXValue | 缩放的中点X坐标,即距离当前视图左边缘的位置 xml输入浮点数则默认单位sp,输入分数(百分数)则为当前视图尺寸的百分比 代码实现是和pivotXType结合使用 |
pivotXType | Animation.ABSOLUTE:输入的为绝对的像素个数 Animation.RELATIVE_TO_SELF:输入的浮点数需要与组件宽度相乘 Animation.RELATIVE_TO_PARENT:输入浮点数需要与该组件的父组件宽度相乘 | |
android:pivotY | pivotYValue | 与pivotXValue同理 |
pivotYType | 与pivotXType同理 |
(3)位移动画 – TranslateAnimation
xml实现
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromXDelta="0"
android:toXDelta="0"
android:fromYDelta="0"
android:toYDelta="-320"
android:duration="2000"/>
attrs.xm中TranslateAnimation内容如下:
<declare-styleable name="TranslateAnimation">
<attr name="fromXDelta" format="float|fraction|dimension" />
<attr name="toXDelta" format="float|fraction|dimension" />
<attr name="fromYDelta" format="float|fraction|dimension" />
<attr name="toYDelta" format="float|fraction|dimension" />
</declare-styleable>
代码实现
public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
int fromYType, float fromYValue, int toYType, float toYValue) {
......
}
xml属性 | 对应参数 | 解释 |
---|---|---|
fromXType | ||
android:fromXDelta | fromXValue | |
toXType | ||
android:toXDelta | toXValue | |
fromYType | ||
android:fromYDelta | fromYValue | |
toYType | ||
android:toYDelta | toYValue |
(4)旋转动画 – RotateAnimation
xml实现:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromDegrees="0"
android:toDegrees="360"
android:duration="1000"
android:pivotX="50%"
android:pivotY="50%"/>
attrs.xm中TranslateAnimation内容如下:
<declare-styleable name="RotateDrawable">
<attr name="visible" />
<attr name="fromDegrees" format="float" />
<attr name="toDegrees" format="float" />
<attr name="pivotX" format="float|fraction" />
<attr name="pivotY" format="float|fraction" />
<attr name="drawable" />
</declare-styleable>
代码实现:
public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
int pivotYType, float pivotYValue) {
......
}
xml属性 | 对应参数 | 解释 |
---|---|---|
android:fromDegrees | fromDegrees | 旋转的起始角度 |
android:toDegrees | toDegrees | 旋转的结束角度 |
(5)动画融合
可以将多种动画结合到一起:
xml实现:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator"
android:shareInterpolator="true" >
<scale
android:duration="2000"
android:fromXScale="0.2"
android:fromYScale="0.2"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.5"
android:toYScale="1.5" />
<rotate
android:duration="1000"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"/>
<translate
android:duration="2000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="320"
android:toYDelta="0" />
<alpha
android:duration="2000"
android:fromAlpha="1.0"
android:toAlpha="0.1" />
</set>
代码实现:
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView image = findViewById(R.id.image);
Animation alphaAnimation = new AlphaAnimation(0.1f, 1.0f);
alphaAnimation.setDuration(2000);
Animation scaleAnimation = new ScaleAnimation(0.2f, 1.5f, 0.2f, 1.5f,
ScaleAnimation.RELATIVE_TO_SELF,
0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(2000);
Animation translateAnimation = new TranslateAnimation(TranslateAnimation.ABSOLUTE,
0, Animation.ABSOLUTE, 320,
Animation.ABSOLUTE, 0,
Animation.ABSOLUTE, 0);
translateAnimation.setDuration(2000);
Animation rotateAnimation = new RotateAnimation(0, 360,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(1000);
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(translateAnimation);
animationSet.addAnimation(rotateAnimation);
animationSet.setFillAfter(true);
image.startAnimation(animationSet);
}
}
三、属性动画(Property Animation)
- 补间动画作用的对象是View,也就是Android中的组件,如ImageView、Button、TextView等,当然也可以作用在布局上,如LinearLayout、ConstraintLayout、RelativeLayout等,但是对于一些不是View的对象,将无法进行动画操作。比如想要对某个组件的某个属性做进行动画操作,例如颜色,这个颜色也可以看成一个对象,但其并不是View对象,补间动画就无法实现,但属性动画可以对这个颜色值做动画
- 补间动画只是改变了
View
的视觉效果,而不会真正去改变View的属性。而属性动画不但会帮助我们实现View
动画的一些视觉效果,而且还能改变View
的属性值
属性动画使用类如下所示:主要使用类是:ValueAnimator
类 和 ObjectAnimator
类
- ObjectAnimator
- ValueAnimator
- PropertyValueHolder
- AnimatorSet
- Interpolator
- TypeEvaluator
(1)对象动画 – ObjectAnimator
Object中有很多重载的ofFloat、ofInt和ofObject方法
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setIntValues(values);
return anim;
}
public static ObjectAnimator ofObject(Object target, String propertyName,
TypeEvaluator evaluator, Object... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setObjectValues(values);
anim.setEvaluator(evaluator);
return anim;
}
- 第一个参数
Object target
:作用对象通常是View
,也就是Android中的组件或布局 - 第二个参数
String propertyName
:需要执行动画的属性,具体值一般有以下选择:- rotation
- alpha
- translationX / translationY:X/Y轴方向的旋转
- scaleX /scaleY:X/Y轴方向上的缩放
- rotationX / rotationY:X/Y轴方向的平移
- 第三个参数: 表示属性的变换范围,该参数可以传多个值,传入多个值的时候为开始值、中间值和结束值,当传入两个值的时候为开始值和结束值
ofObject方法在(7)估值器 – TypeEvaluator中再学习
使用这种方式实现上面图片透明度动画:
xml实现:
**注:**在res
目录下新建animator
文件夹,在其中创建动画xml文件
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="alpha"
android:valueFrom="1"
android:valueTo="0.2"
android:duration="2000"
android:valueType="floatType"/>
Java代码中通过加载该xml启动动画:
public class TestProActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pro);
ImageView image = findViewById(R.id.image_pro);
Animator animator = AnimatorInflater.loadAnimator(TestProActivity.this, R.animator.pro_animation_alpha);
animator.setTarget(image);
animator.start();
}
}
代码实现:
ImageView image = findViewById(R.id.image_pro);
Button button1 = findViewById(R.id.object);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ObjectAnimator animator = ObjectAnimator.ofFloat(image, "alpha", 1F, 0F, 1F);
animator.setDuration(2000);
animator.setStartDelay(500);
animator.setRepeatCount(1);
animator.setRepeatMode(ObjectAnimator.RESTART);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
// Log.d("TAG", "mafangda onAnimationUpdate: --- " + animation.getAnimatedValue());
Log.d("TAG", "mafangda onAnimationUpdate: --- " + image.getAlpha());
}
});
animator.start();
}
});
以上面的ObjectAnimator中ofFloat为例,继续看一下源码:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
// 这里就是通过属性名可以自动推导出setter和getter方法,例如传入属性为Foo,那么对应的方法即setFoo和getFoo方法
// 这里注释中提示为了获得调用由属性名称确定的 setter 函数的最佳性能,请使用 float 或 int 类型的值,并使这些属性的 setter 函数返回 void
// 其他属性类型和返回类型也可以工作,但由于反射机制,会在处理请求时产生更多开销
public void setPropertyName(@NonNull String propertyName) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
从上面的源码中我们可以知道,动画更新过程中是通过不断调用setPropertyName更新元素的属性
我们可以测试一下,自定义一个ImageView的子类如下,在这里我们自定义了一个value属性,但是在getter和setter方法中调用了ImageView中的getAlpha和setAlpha方法,即通过setValue和getValue应该可以获得自定义View的透明度。
@SuppressLint("AppCompatCustomView")
public class TestView extends ImageView {
float value;
public TestView(Context context) {
super(context);
}
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public float getValue() {
Log.d("TAG", "mafangda getValue() called value -- " + this.value);
return super.getAlpha();
}
public void setValue(float value) {
Log.d("TAG", "mafangda setValue() called with: value = [" + value + "]");
this.value = value;
super.setAlpha(value);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<com.example.animation.TestView
android:id="@+id/testView"
android:layout_width="120dp"
android:src="@drawable/xi_an"
android:layout_height="120dp"/>
</LinearLayout>
主程序,动画无限循环
@SuppressLint("MissingInflatedId")
public class TestObjectAnimator extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_object_animator);
TestView ballView = findViewById(R.id.testView);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(ballView, "value",
1F, 0F, 1F);
objectAnimator.setDuration(2000);
objectAnimator.setRepeatCount(-1);
objectAnimator.start();
}
}
注:如果操作组件的该属性对应的setter方法中没有调用View的重绘,可以使用下面手动重新绘制view
@SuppressLint("MissingInflatedId")
public class TestObjectAnimator extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_object_animator);
TestView ballView = findViewById(R.id.testView);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(ballView, "rotationX",
0, 100, 0);
objectAnimator.setDuration(5000);
objectAnimator.setRepeatCount(-1);
objectAnimator.start();
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
//ballView.postInvalidate();
//ballView.invalidate();
}
});
}
}
在这里,如果希望一个View的动画是既可以缩小,又可以降低透明度(淡出效果),同时修改scaleX、scaleY和alpha属性,后面我们可以使用AnimatorSet实现,现在可以如下操作:
xml布局,就一个图片
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<ImageView
android:id="@+id/imageView"
android:layout_width="120dp"
android:layout_height="120dp"
android:src="@drawable/xi_an" />
</LinearLayout>
java程序:
@SuppressLint("MissingInflatedId ")
public class TestObjectAnimator extends Activity {
private ImageView imageView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.object_animator);
imageView = findViewById(R.id.imageView);
// 在这里我们随便填写一个UI组件没有的属性
@SuppressLint("ObjectAnimatorBinding") ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "mfd",1F, 0F);
objectAnimator.setDuration(5000);
objectAnimator.setRepeatCount(-1);
objectAnimator.start();
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
Float value = (Float) animation.getAnimatedValue();
imageView.setAlpha(value);
imageView.setScaleX(value);
imageView.setScaleY(value);
}
});
}
}
结果:
可以看到,设置属性的字符串我们随便填写一个UI组件没有的属性,只需要动画按照时间插值和持续时间不断的计算那个值,然后我们自己手动调用
(2)值动画 – ValueAnimator
值动画通过控制值的变化,赋值给对象的属性,从而实现动画。
ValueAnimator
的核心方法如下:
// 以整数值进行动画处理
public static ValueAnimator ofInt(int... values)
// 以浮点数值进行动画处理
public static ValueAnimator ofFloat(float... values)
// 允许创建一个在动画过程中改变任意类型值的ValueAnimator
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
添加值动画,在值动画的监听函数里来获取值得变化,根据值的变化对控件设置相应的属性。这里的属性可以是控件的任意属性。
Button button2 = findViewById(R.id.value);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ValueAnimator animator = ValueAnimator.ofFloat(0.2F, 1F);
animator.setDuration(3000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
Log.d("TAG", "mafangda onAnimationUpdate: value -- " + value);
image.setAlpha(value);
}
});
animator.start();
}
});
效果如下所示:
可以发现,ValueAnimator在使用方法上与ObjectAnimator 非常相似,区别在于不需要填写设置的属性,类似上面写的UI淡出示例的使用方法,
(3)监听动画事件
对于动画,一般都是一些辅助效果,比如我要删除个元素,我可能希望是个淡出的效果,但是最终还是要删掉,并不是你透明度没有了,还占着位置,所以我们会使用到动画生命周期中的一些节点。
ValueAnimator animator = ValueAnimator.ofFloat(1.0F, 0F);
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(@NonNull Animator animation) {
}
@Override
public void onAnimationEnd(@NonNull Animator animation) {
}
@Override
public void onAnimationCancel(@NonNull Animator animation) {
}
@Override
public void onAnimationRepeat(@NonNull Animator animation) {
}
});
通过添加Animator.AnimatorListener我们就可以监听到动画的开始、结束、被取消、重复等事件,但是有时候我们只是想知道整个动画的某个节点,这样的代码我们不能接受,我们可以使用AnimatorListenerAdapter
AnimatorListenerAdapter源码如下:,我们可以发现AnimatorListenerAdapter继承自Animator.AnimatorListener,并且对空实现了其中的所有方法,这样我们想知道动画的哪个节点可以单独重写对应的方法,并不需要重写所有方法
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener,
Animator.AnimatorPauseListener {
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationPause(Animator animation) {
}
@Override
public void onAnimationResume(Animator animation) {
}
}
(4)PropertyValueHolder
PropertyValueHolder可以让动画同时执行
Button button3 = findViewById(R.id.property_value_holder);
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PropertyValuesHolder alphaProper = PropertyValuesHolder.ofFloat("alpha", 0.2f, 1f);
PropertyValuesHolder scaleXProper = PropertyValuesHolder.ofFloat("scaleX", 0.5f, 1.2f);
PropertyValuesHolder scaleYProper = PropertyValuesHolder.ofFloat("scaleY", 0.5f, 1.2f);
PropertyValuesHolder translationXProper = PropertyValuesHolder.ofFloat("translationX", -100, 100);
PropertyValuesHolder translationYProper = PropertyValuesHolder.ofFloat("translationY", -100, 100);
PropertyValuesHolder rotationProper = PropertyValuesHolder.ofFloat("rotation", 0, 360);
ValueAnimator animator = ObjectAnimator.ofPropertyValuesHolder(image, alphaProper,
scaleXProper, scaleYProper, translationXProper, translationYProper, rotationProper);
animator.setDuration(5000);
animator.start();
}
});
结果如下所示:
(5)动画组合 – AnimatorSet
PropertyValueHolder
类能实现将多个动画同时执行,AnimatorSet
类不仅能让多个动画同时执行,还能让多个动画按一定的顺序执行,同时也能穿插多个动画同时执行。
主要的方法如下:
// 将现有动画延迟指定毫秒后执行
public Builder after(long delay) {
// setup a ValueAnimator just to run the clock
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(delay);
after(anim);
return this;
}
// 将现有动画插入到传入的动画之后执行
public Builder after(Animator anim)
// 将现有动画和传入的动画同时执行
public Builder with(Animator anim)
// 将现有动画插入到传入的动画之前执行
public Builder before(Animator anim)
示例:
Button button4 = findViewById(R.id.animator_set);
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ObjectAnimator rotate = ObjectAnimator.ofFloat(image, "rotation", 0f, 360f);
ObjectAnimator translationX = ObjectAnimator.ofFloat(image, "translationX", -100, 100f);
ObjectAnimator translationY = ObjectAnimator.ofFloat(image, "translationY", -100, 100f);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(image, "scaleX", 0, 1f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(image, "scaleY", 0, 1f);
ObjectAnimator alpha = ObjectAnimator.ofFloat(image, "alpha", 1f, 0f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate)
.with(alpha)
.after(scaleX)
.before(translationX)
.after(1000)
.before(translationY)
.with(scaleY);
animSet.setDuration(5000);
animSet.start();
}
});
结果如下所示:按照after -> with -> before 的顺序合并后播放
(6)插值器 – Interpolator
前面的动画属性的变换都是均匀变换,可以通过差值器Interpolator
来控制值变化的速率
Button button5 = findViewById(R.id.interpolator);
button5.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ObjectAnimator translationX = ObjectAnimator.ofFloat(image, "translationX", -200f, 200f);
translationX.setDuration(5000);
//加速查值器,参数越大,速度越来越快
translationX.setInterpolator(new AccelerateInterpolator(5));
translationX.start();
}
});
系统提供的插值器:
名称 | 效果 |
---|---|
AccelerateInterpolator | 加速插值器:参数越大,速度越来越快 |
DecelerateInterpolator | 减速插值器:和加速插值器相反 |
AccelerateDecelerateInterpolator | 先加速后减速 |
AnticipateInterpolator | 先后退在加速前进 |
AnticipateOvershootInterpolator | 以X/Y轴为轴的旋转度数 |
BounceInterpolator | 在动画结束时产生弹跳效果 |
CycleInterpolator | 周期运动插值 |
LinearInterpolator | 匀速插值 |
OvershootInterpolator | 先快速完成动画,再回到结束样式 |
一般不会自己设计插值器,系统提供的插值器足够使用
(7)估值器 – TypeEvaluator
值动画(ValueAnimator)和 对象动画(ObjectAnimator)中有一个传对象的方法:
public static ObjectAnimator ofObject(Object target, String propertyName,
TypeEvaluator evaluator, Object... values)
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
TypeEvaluator接口:
public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
}
从TypeEvaluator
的源码可以看出该类的作用就是告诉动画,如何从起始值过度到结束值
Android
源码中有好几个类实现来该接口,也就是系统提供的一些默认估值器, 以FloatEvaluator
为例:
public class FloatEvaluator implements TypeEvaluator<Number> {
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
从FloatEvaluator
的实现可以看出在evaluate
方法中用结束值减去初始值,算出它们之间的差值,然后乘以fraction
这个系数,再加上初始值,那么就得到当前动画设置的值了
四、示例
实现一个例子,两个小球,一个做抛物线运动,一个垂直落下
分析一下:貌似只和时间有关系,但是根据时间的变化,横向和纵向的移动速率是不同的
结果:此时就要重写TypeValue的时候了,因为我们在时间变化的同时,需要返回给对象两个值,x当前位置,y当前位置
实现:
xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="220dp" >
<ImageView
android:id="@+id/ball1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ball"/>
<ImageView
android:id="@+id/ball2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ball"/>
</FrameLayout>
<Button
android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Start" />
</LinearLayout>
java程序:
public class TestValueAnimator extends Activity {
private ImageView ball1;
private ImageView ball2;
private ValueAnimator ball1Animator;
private ValueAnimator ball2Animator;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.value_animator);
ball1 = findViewById(R.id.ball1);
ball2 = findViewById(R.id.ball2);
// 为第一个小球自由落体设置动画
ball1Animator = new ValueAnimator();
ball1Animator.setDuration(3000);
ball1Animator.setObjectValues(new PointF(0, 0));
ball1Animator.setInterpolator(new LinearInterpolator());
ball1Animator.setTarget(ball1);
ball1Animator.setEvaluator(new TypeEvaluator<PointF>() {
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
PointF point = new PointF();
point.x = 0;
point.y = 0.5F * 10 * (fraction * 20) * (fraction * 20);
return point;
}
});
ball1Animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
PointF point = (PointF) animation.getAnimatedValue();
ball1.setX(point.x);
ball1.setY(point.y);
}
});
// 为第二个小球有水平速度,做抛物线运动设置动画
TypeEvaluator<PointF> pointFTypeEvaluator = new TypeEvaluator<PointF>() {
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
PointF point = new PointF();
point.x = 100 * fraction * 20;
point.y = 0.5F * 10 * (fraction * 20) * (fraction * 20);
return point;
}
};
ball2Animator = ValueAnimator.ofObject(pointFTypeEvaluator, new PointF(0, 0));
ball2Animator.setDuration(3000);
ball2Animator.setInterpolator(new LinearInterpolator());
ball2Animator.setTarget(ball2);
ball2Animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
PointF point = (PointF) animation.getAnimatedValue();
ball2.setX(point.x);
ball2.setY(point.y);
}
});
// 点击开始 两个小球同时运动
Button startButton = findViewById(R.id.start);
startButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ball1Animator.start();
ball2Animator.start();
;
}
});
}
}
**注:**这里fraction是插值器产生的随时间变化的系数,变化区间是[0,1],所以这里乘了一个20作为系数,不然变化太小导致看不到动画
动画如下:
原文地址:https://blog.csdn.net/weixin_50995731/article/details/142236708
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!