在将属性动画之前,我们先看一个例子:
public class MainActivity extends Activity {
private Button button;
private Animation animation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
animation = new TranslateAnimation(0, 0, 0, 320);
animation.setDuration(3000);
animation.setFillAfter(true);
button.startAnimation(animation);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("TTT", "按钮被点击");
}
});
}
}
代码很简单,只是简单的移动一个按钮,最后按钮的位置保持在动画结束的地方。
这个时候我们去点击按钮,就会发现并不会触发按钮上的监听器,但是点击按钮原来的位置,则会触发。
所以可以看出来,补间动画只是单纯的改变的一下(设置动画组件)的外观,其本质还是没有改变(不知道这么说是否合适,领会精神即可),因为还是只可以在原来位置点击,Android 提供了另外一种动画:属性动画:PropertyAnimation,这种动画不光可以给 View 设置,它可以给任何对象设置,并且不单单是改变组件的显示效果,而可以直接修改其属性。
按照官方的说法是,属性动画可以在一个事件段内,改变一个对象的属性值,从而定义一个动画,而不用关心该对象是否需要在屏幕上绘制
我们需要先了解 PropertyAnimation 的几个概念:
Animator:创建属性动画的基类,一般使用它的两个子类,或者在自定义属性动画时候让我们的类继承它。
ValueAnimator:属性动画主要的时间引擎,它负责计算各个帧的属性值。它定义了属性动画的绝大部分核心功能,包括计算各帧的相关属性值,负责处理更新事件,按属性值的类型控制计算规则。
属性动画主要由两个方面组成
- 计算各帧的相关属性值
- 为指定对象设置这些计算后的值。
ValueAnimator 值负责第一方面的内容,因此程序员必须根据 ValueAnimator 计算并监听值更新来更新对象的相关属性值。
ObjectAnimator:Animator 的子类,允许程序员对指定对象的属性执行动画。在实际应用中,ObjectAnimator 使用起来更加简单,因此更加常用。在少数场景下,由于 ObjectAnimator 存在一些限制,可能需要考虑使用 ValueAnimator。
AnimatorSet:Animator 的子类,用于组合多个 Animator,并指定多个 Animator 是按次序播放,还是同时播放。
除此之外,属性动画还需要利用 Evaluator(计算器),该工具类控制属性动画如何计算属性值。Android 提供了如下 Evaluator。
IntEvaluator:用于计算int类型属性值的计算器
FloatEvaluator:用于计算float类型属性值的计算器
ArgbEvaluator:用于计算以十六进制形式表示的颜色值的计算器
TypeEvaluator:它是计算器接口,开发者可以通过实现该接口来实现自定义计算器。如果需要对 int、float 或颜色值以外类型的属性执行属性动画,可能需要实现 TypeEvaluator 接口来实现自定义计算器。
上面已经说了,属性动画是通过直接修改对象的属性,通过实现动画效果的,例如我们在 10 秒之内,逐渐的修改一张图片的宽度和高度,提现出来的效果自然就是图片慢慢缩小的动画效果。
所以说,有一定规律的属性值就是重中之重了,属性动画中,ValueAnimator 可以帮助我们获取到这些逐渐改变的值。
这里是 ValueAnimator 对象的常用方法:
获取 ValueAnimator 对象的方法:
设置动画属性的方法:
获取动画属性的方法:
其他方法:
那我们来一个例子,演示一下 ValueAnimator 的使用:
public class MainActivity extends Activity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ValueAnimator animator = ValueAnimator.ofInt(button.getWidth(), getWindowManager().getDefaultDisplay().getWidth());
animator.setDuration(500);
animator.setStartDelay(1000);
animator.start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int values = (int) animation.getAnimatedValue();
Log.d("TTT", "values = " + values);
button.setWidth(values);
}
});
}
});
}
}
可以看到,点击按钮之后,按钮会在半秒之内,宽度 match_parent 效果。
在上一个例子当中,我们通过调用 Button 的 setWidth 方法改变了按钮的宽度,Android 当中的 View 有一系列方法可以改变他的外观:
我们动态的修改 View 的上述方法,不就可以实现动画效果了么。
转一个网上的例子:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn_one;
private Button btn_two;
private Button btn_three;
private Button btn_four;
private LinearLayout ly_root;
private ImageView img_babi;
private int width;
private int height;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindViews();
}
private void bindViews() {
ly_root = (LinearLayout) findViewById(R.id.ly_root);
btn_one = (Button) findViewById(R.id.btn_one);
btn_two = (Button) findViewById(R.id.btn_two);
btn_three = (Button) findViewById(R.id.btn_three);
btn_four = (Button) findViewById(R.id.btn_four);
img_babi = (ImageView) findViewById(R.id.img_babi);
btn_one.setOnClickListener(this);
btn_two.setOnClickListener(this);
btn_three.setOnClickListener(this);
btn_four.setOnClickListener(this);
img_babi.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_one:
lineAnimator();
break;
case R.id.btn_two:
scaleAnimator();
break;
case R.id.btn_three:
raAnimator();
break;
case R.id.btn_four:
circleAnimator();
break;
case R.id.img_babi:
Toast.makeText(MainActivity.this, "不愧是coder-pig~", Toast.LENGTH_SHORT).show();
break;
}
}
//定义一个修改ImageView位置的方法
private void moveView(View view, int rawX, int rawY) {
int left = rawX - img_babi.getWidth() / 2;
int top = rawY - img_babi.getHeight();
int width = left + view.getWidth();
int height = top + view.getHeight();
view.layout(left, top, width, height);
}
//定义属性动画的方法:
//按轨迹方程来运动
private void lineAnimator() {
width = ly_root.getWidth();
height = ly_root.getHeight();
ValueAnimator xValue = ValueAnimator.ofInt(height,0,height / 4,height / 2,height / 4 * 3 ,height);
xValue.setDuration(3000L);
xValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 轨迹方程 x = width / 2
int y = (Integer) animation.getAnimatedValue();
int x = width / 2;
moveView(img_babi, x, y);
}
});
xValue.setInterpolator(new LinearInterpolator());
xValue.start();
}
//缩放效果
private void scaleAnimator(){
//这里故意用两个是想让大家体会下组合动画怎么用而已~
final float scale = 0.5f;
AnimatorSet scaleSet = new AnimatorSet();
ValueAnimator valueAnimatorSmall = ValueAnimator.ofFloat(1.0f, scale);
valueAnimatorSmall.setDuration(500);
ValueAnimator valueAnimatorLarge = ValueAnimator.ofFloat(scale, 1.0f);
valueAnimatorLarge.setDuration(500);
valueAnimatorSmall.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float scale = (Float) animation.getAnimatedValue();
img_babi.setScaleX(scale);
img_babi.setScaleY(scale);
}
});
valueAnimatorLarge.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float scale = (Float) animation.getAnimatedValue();
img_babi.setScaleX(scale);
img_babi.setScaleY(scale);
}
});
scaleSet.play(valueAnimatorLarge).after(valueAnimatorSmall);
scaleSet.start();
//其实可以一个就搞定的
// ValueAnimator vValue = ValueAnimator.ofFloat(1.0f, 0.6f, 1.2f, 1.0f, 0.6f, 1.2f, 1.0f);
// vValue.setDuration(1000L);
// vValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
// @Override
// public void onAnimationUpdate(ValueAnimator animation) {
// float scale = (Float) animation.getAnimatedValue();
// img_babi.setScaleX(scale);
// img_babi.setScaleY(scale);
// }
// });
// vValue.setInterpolator(new LinearInterpolator());
// vValue.start();
}
//旋转的同时透明度变化
private void raAnimator(){
ValueAnimator rValue = ValueAnimator.ofInt(0, 360);
rValue.setDuration(1000L);
rValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int rotateValue = (Integer) animation.getAnimatedValue();
img_babi.setRotation(rotateValue);
float fractionValue = animation.getAnimatedFraction();
img_babi.setAlpha(fractionValue);
}
});
rValue.setInterpolator(new DecelerateInterpolator());
rValue.start();
}
//圆形旋转
protected void circleAnimator() {
width = ly_root.getWidth();
height = ly_root.getHeight();
final int R = width / 4;
ValueAnimator tValue = ValueAnimator.ofFloat(0,
(float) (2.0f * Math.PI));
tValue.setDuration(1000);
tValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 圆的参数方程 x = R * sin(t) y = R * cos(t)
float t = (Float) animation.getAnimatedValue();
int x = (int) (R * Math.sin(t) + width / 2);
int y = (int) (R * Math.cos(t) + height / 2);
moveView(img_babi, x, y);
}
});
tValue.setInterpolator(new DecelerateInterpolator());
tValue.start();
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ly_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/btn_one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="动画1" />
<Button
android:id="@+id/btn_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="动画2" />
<Button
android:id="@+id/btn_three"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="动画3" />
<Button
android:id="@+id/btn_four"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="动画4" />
<ImageView
android:id="@+id/img_babi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/dog" />
</LinearLayout>