每款产品都会有动画的身影,怎么才能做出绚丽的,多样的动画。那么这篇文章会对你有所帮助,别把动画想得很难,其实它很简单,我们一起来看一看:
看完后面的内容,制作出上面两种动画效果已经不是问题。我至今还是苦恼的一件事件,没有找到好的录制工具,和转换gif工具,如果你知道,请给我留言,真是万分感谢。。。嘻嘻请给我免费的那种
在 Android 3.0之前,支持两种动画模式,补间动画 (tween animation)以及 帧动画 (frame animation)。基于前面两种动画的局限性在 Android 3.0中引入了一个新的动画系统 属性动画(property animation),就是今天的主角。
Property Animation很浅显的就是通过动画的方式改变对象的属性了,我们要先来了解它的几个属性:
属性 | 含义 |
---|---|
Duration | 动画的持续时间,默认300ms |
Time interpolation | 时间插入器 (AccelerateInterpolator DecelerateInterpolator等) |
RepeatCount | 重复的次数 默认是0 |
RepeatMode | 重复的模式 (正向:ValueAnimator.RESTART 反向:ValueAnimator.REVERSE 默认是 RESTART ) |
AnimatorSet | 用于控制一组动画的执行:比如插入器为线性,执行顺序的先后顺序 |
AnimatorInflater | 用户加载属性动画的xml文件 |
Animator sets | 动画集合,你可以定义一组动画,一起执行或者顺序执行 |
ObjectAnimator | 动画的执行类 |
ValueAnimator | 动画的执行类 |
TypeEvaluator | 类型估值,主要用于设置动画操作属性的值。 |
PropertyValuesHolder | 动画效果类 |
我们可以通过动画的执行类来设置动画操作对象的属性、执行时间、执行次数、模式,开始和结束的属性值等,然后系统会根据设置的参数来动态改变对象的属性。(对象一般为控件)
我们通过简单的代码来实现动画。
效果图:
布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/ivAnim"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_centerInParent="true"
android:src="@mipmap/face" />
<Button
android:id="@+id/bntAnim"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:onClick="bntAnim"
android:text="属性动画"
android:textColor="#ffffff" />
RelativeLayout>
Activity代码:
package com.github.ws.animationdemo;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
/**
* Created by Administrator on 4/5 0005.
*/
public class ObjectAnimatorActivity extends Activity {
private ImageView ivAnim;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.object_animator);
ivAnim = (ImageView) findViewById(R.id.ivAnim);
}
void bntAnim(View v) {
ObjectAnimator animator = ObjectAnimator.ofFloat(ivAnim, "rotationY", 0f, 90f);
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.setRepeatCount(2);
animator.setDuration(1000);
animator.start();
}
}
如果你要使用反向animator.setRepeatMode(ValueAnimator.REVERSE);
请设置 animator.setRepeatCount(2);
值大于0。
对应ObjectAnimator
1、除了ObjectAnimator.ofFloat
方法,还有
这几个方法都是设置动画作用的元素、作用的属性、动画开始、结束、以及中间的任意个属性值。当对于属性值,只设置一个的时候,会认为当然对象该属性的值为开始(getPropName反射获取),然后设置的值为终点。如果设置两个,则一个为开始、一个为结束。如果设置三个,则一个是开始、一个是中间值、一个是结束值~~~
动画更新的过程中,会不断调用setPropName更新元素的属性,所有使用ObjectAnimator更新某个属性,必须得有getter(设置一个属性值的时候)和setter方法~
2、如果你操作对象的该属性方法里面,比如上例的setRotationY如果内部没有调用view的重绘,则你需要自己按照下面方式手动调用。
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
ivAnim.invalidate();// 或者 ivAnim.postInvalidate();
}
});
3、上面的例子只有一个属性变化,如果我们想多个属性变化呢?例如:我希望一个view可以缩小,还可以淡出,3个属性变化(scaleX,scaleY,alpha)
效果图:
Activity代码:
void bntAnim(View v) {
ObjectAnimator animator = ObjectAnimator.ofFloat(ivAnim, "alpha", 1.0f, 0.0f);
animator.setDuration(2000);
animator.start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float ft = (float) valueAnimator.getAnimatedValue(); // 1.0 到 0.0变化
ivAnim.setScaleX(ft);
ivAnim.setScaleY(ft);
}
});
}
4、如果我想实现抛物线的效果,水平方向100px/s,垂直方向自由落体,我们一起来看看用ObjectAnimator实现:
效果图:
布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/ivAnim"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_centerInParent="true"
android:src="@mipmap/face" />
<Button
android:id="@+id/bntAnim"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:onClick="bntAnim"
android:text="属性动画"
android:textColor="#ffffff" />
RelativeLayout>
Activity文件:
package com.github.ws.animationdemo;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
/**
* Created by Administrator on 4/5 0005.
*/
public class PaoActivity extends Activity {
private ImageView ivPao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pro);
ivPao = (ImageView) findViewById(R.id.ivFace);
}
void bntPao(View v) {
//注意 pao 属性是我随便起的 主要是为了传值 10.0f 是屏幕的宽度 除以 水平的速度 自己去计算
ObjectAnimator animator = ObjectAnimator.ofFloat(ivPao, "pao", 0f, 10.0f);
animator.setDuration(3000);
animator.start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// ft 值 为上面传过来的 0.0 到 10.0
float ft = (float) valueAnimator.getAnimatedValue();
ivPao.setX(100 * ft);
ivPao.setY(0.5f * 10 * ft * ft ); //加速度公式
}
});
}
}
5、使用propertyValuesHolder来实现一个动画多个效果:
public void pvh(View view) {
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 1f,
0f, 1f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 1f,
0, 1f);
PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", 1f,
0, 1f);
ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ).setDuration(1000).start();
}
看到这里是不是觉得ObjectAnimator
很强大也很简单,使用ObjectAnimator
你也可以做出一些很绚丽的效果。
和ObjectAnimator用法很类似,我们简单来实现view垂直移动的动画:
public void verticalMove(View view) {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1920);//1080是我手机屏幕的宽度
animator.setTarget(ivPao);
animator.setDuration(1000).start();
}
我们能够很清楚的看到,没有设置操作的属性,运行起来是没有任何效果的。这就是ValueAnimator
跟ObjectAnimator
的区别之处,就不需要有getter和setter方法,灵活性就比较高了。你一定会发现我们可以在addUpdateListener
来操作属性:
public void verticalMove() {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1920f);//1920是我手机屏幕的高度
animator.setTarget(ivPao);
animator.setDuration(2000).start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
ivPao.setTranslationY((float) valueAnimator.getAnimatedValue());
}
});
}
效果图:
下面我们再看来一个例子使用TypeValue
来实现抛物线的效果,分析一下,好像只和时间有关系,根据时间的变化,横向和纵向的移动速率是不同的,我们该咋实现呢?此时就要重写TypeValue的时候了,因为我们在时间变化的同时,需要返回给对象两个值,x当前位置,y当前位置。我这里就不贴布局文件了,直接来看代码:
public void paoMove() {
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(3000);
valueAnimator.setObjectValues(new PointF(0, 0));
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setEvaluator(new TypeEvaluator() {
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
//fraction 表示当前的时间变化率 在区间0到1变化
// fraction = t / duration t表示当前时间 在区间0到3变化
PointF point = new PointF();
//3秒内运动的距离为屏幕的宽度 我屏幕的宽度是1080 所以速度为 1080/3
point.x = 360 * fraction * 3;
//这里要计算加速度 (fraction * 3) * (fraction * 3)最大值为9 计算加速度为 42 左右
point.y = 0.5f * 10 * 42 * (fraction * 3) * (fraction * 3);
return point;
}
});
valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
PointF point = (PointF) valueAnimator.getAnimatedValue();
ivPao.setX(point.x);
ivPao.setY(point.y);
}
});
}
我们自定义了TypeValue,每次根据当前每次根据当前时间返回一个PointF对象(PointF和Point的区别就是x,y的单位一个是浮点型float,一个是整形int;的t前 和Rec过intF包含了当前的x,y值,然后通过监听获取x,y值并设置属性。TypeValue根据你具体需求而定
说到监听事件,我们自然会想到Listener
,那么动画属性的监听又是怎么样的呢?
//淡化并删除
public void deleteView() {
ObjectAnimator animator = ObjectAnimator.ofFloat(ivPao, "alpha", 1.0f, 0.0f);
animator.setDuration(2000);
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
//动画开始
}
@Override
public void onAnimationEnd(Animator animator) {
//动画结束
ViewGroup parent = (ViewGroup) ivPao.getParent();
if (parent != null)
parent.removeView(ivPao);//是移除了view 并不会占位
}
@Override
public void onAnimationCancel(Animator animator) {
//被取消
}
@Override
public void onAnimationRepeat(Animator animator) {
//动画被重复
}
});
animator.start();
}
是不是每次我们都需要重写这么多方法呢?我们可以这么做:
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//super.onAnimationEnd(animation);
}
});
效果图:
animator还有cancel()和end()方法:cancel动画立即停止;end动画直接到最终状态。
AnimatorSet是一组动画的集合,还可以设置动画的先后顺序。我们直接看代码:
public void propertyList() {
ObjectAnimator anim1 = ObjectAnimator.ofFloat(ivPao, "scaleX", 1.0f, 2f);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(ivPao, "scaleY", 1.0f, 2f);
ObjectAnimator anim3 = ObjectAnimator.ofFloat(ivPao, "rotationX", 0f, 360f);
ObjectAnimator anim4 = ObjectAnimator.ofFloat(ivPao, "rotationY", 0f, 360f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
//super.onAnimationStart(animation);
ivPao.setPivotX(0);
ivPao.setPivotY(0);
ivPao.invalidate();
}
});
animatorSet.play(anim1).with(anim2);
animatorSet.play(anim3).after(anim2);
animatorSet.play(anim3).with(anim4);
animatorSet.start();
}
注意:after()
方法值的是anim3在anim2之后(上面的例子就是先缩放然后翻转)。
我们还可以使用playTogether两个动画同时执行,当然还有playSequentially依次执行。animSet.play().with();也是支持链式编程的,但是不要过长,多写点代码。
最后来看看效果图:
由于篇幅原因,Android Property Animator (属性动画)第二篇中我会实现文章开头的动画效果。
源码地址