是在Android3.0(API11)开始出现的,如果要在API11之前使用,要用动画库nineoldandroids来兼容以前的版本(在API11以前的版本内部是通过代理View动画来实现的,因此在Android低版本上,本质还是View动画,虽然看起来是属性动画)
ObjectAnimator 动画的执行类,后面详细介绍
ValueAnimator 动画的执行类,后面详细介绍
AnimatorSet 用于控制一组动画的执行:线性,一起,每个动画的先后执行等。
AnimatorInflater 用户加载属性动画的xml文件
TypeEvaluator 类型估值算法(作用:根据当前属性改变的百分比来计算改变后的属性值,系统预置的有IntEvaluator(针对整型),floatEvaluator(针对浮点),argbEvaluator(针对color属性),属性动画中插值器(Interpolator)和估值器(TypeEvalator)很重要)注意:自定义插值器需要实现Interpolator或者TimeInterpolator,自定义的算法素要实现TypeEvaluator(如果要对其他类型,非int,float,color做动画,那么必须要自定义类型的估值算法)
TimeInterpolator 时间插值(作用:根据时间流逝的百分比来计算出当前属性值改变的百分比)
Interceptor 插值器
android:propertyName —— 表示属性动画的作用对象的属性名称(只有ObJectAnimator有,ValueAnimator没有,注意这个属性是这个object有set和get方法的,不是随便乱来的)
android:duration—— 表示动画的时长
android:valueTo—— 表示属性的结束值
android:startOffset—— 表示动画的延迟时间,当动画开始后,需要延迟多少毫秒才会真正的播放动画
android:repeatCount—— 表示动画的重复次数(默认0,-1代表无限循环)
android:repeatMode—— 表示动画的重复模式(repeat,reverse)
android:valueType—— 表示android:propertyName所指定的属性类型,有”intType”和”floatType”两个可选项,分别表示属性的类型为整形和浮点型,另外如果android:propertyName 所指定的属性去表示的是颜色,那么不需要指定,android:valueType,系统会自动对颜色类型的属性做处理
android:valueFrom—— 表示属性的起始值
Animator sets: 动画集合,你可以定义一组动画,一起执行或者顺序执行。(里面有个属性Android:ordering默认值是"together",表示同时播放,也可以自己改成"sequentially",表示按照前后顺序依次播放)
Frame refresh delay:帧刷新延迟,对于你的动画,多久刷新一次帧;默认为10ms,但最终依赖系统的当前状态;基本不用管
<set> AnimatorSet (包含android:ordering(together,sequentially))
<animator> valueAnimator (相比objectAnimator只是少了一个Android:propertyName,其他都一样的)
<objectAnimator> objectAnimator
属性动画就是,动画的执行类来设置动画操作的对象的属性、持续时间,开始和结束的属性值,时间差值等,然后系统会根据设置的参数动态的变化对象的属性。
public class MainActivity extends Activity { private ImageView iv; private ObjectAnimator alaph; private ObjectAnimator tran; private ObjectAnimator rotate; private ObjectAnimator scale; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv = (ImageView) findViewById(R.id.iv); } /** * 透明度 * @param view */ public void alpha(View view){ // iv.setAlpha(alpha); // iv.getAlpha() alaph = ObjectAnimator.ofFloat(iv, "alpha", 0.0f,0.2f,0.4f,0.6f,0.8f,1.0f); alaph.setDuration(4000); alaph.setRepeatMode(ObjectAnimator.REVERSE); alaph.setRepeatCount(ObjectAnimator.INFINITE); alaph.start(); } /** * 旋转动画 * @param view */ public void rotate(View view){ //iv.setRotationY(rotationY) //rotaitionY 就是以y为轴转,rotationX就是以X为轴转 rotate = ObjectAnimator.ofFloat(iv, "rotationX", 0.0f,30f,60.0f,90f); rotate.setDuration(2000); rotate.setRepeatMode(ObjectAnimator.REVERSE); rotate.setRepeatCount(ObjectAnimator.INFINITE); rotate.start(); } /** * 缩放 * @param view */ public void scale(View view){ //iv.setScaleX() scale = ObjectAnimator.ofFloat(iv, "scaleY", 0.0f,0.2f,0.5f,2.0f); scale.setDuration(2000); scale.setRepeatMode(ObjectAnimator.REVERSE); scale.setRepeatCount(ObjectAnimator.INFINITE); scale.start(); } /** * 位移 * @param view */ public void trans(View view){ //iv.setScaleX() //iv.setTranslationX(); /** * public void end () Added in API level 11 Ends the animation. This causes the animation to assign the end value of the property being animated, then calling the onAnimationEnd(Animator) method on its listeners. This method must be called on the thread that is running the animation. *就是直接到最后 * */ //oa.end(); /** * public void cancel () Added in API level 11 Cancels the animation. Unlike end(), cancel() causes the animation to stop in its tracks, s ending an onAnimationCancel(Animator) to its listeners, followed by an onAnimationEnd(Animator) message. This method must be called on the thread that is running the animation. * 播放哪停到那,就停在那个地方 * */ tran = ObjectAnimator.ofFloat(iv, "translationX", 0.0f, 30f, 60f, 200f); tran.setDuration(2000); tran.setRepeatMode(ObjectAnimator.REVERSE); tran.setRepeatCount(ObjectAnimator.INFINITE); tran.start(); } public void stop(View view){ cancelObjectAnimator(); } public void set(View view){ System.out.println("---"); //动画的集合 AnimatorSet set = new AnimatorSet(); ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "rotation", 0.0f,30f,60.0f,90f); oa.setDuration(4000); ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "translationX", 0.0f,10f,20f,310f); oa2.setDuration(2000); //分步发生 set.playSequentially(oa,oa2); //先旋转再起飞 //一起发生 //set.playTogether(oa,oa2); //边转边飞 set.start(); } public void cancelObjectAnimator(){ // if(oa !=null){ // oa.cancel(); // oa = null; // } } public void endObjectAnimator(){ // if(oa !=null){ // oa.end(); // oa = null; // } } }
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering = "together"> <objectAnimator android:propertyName = "x" android:duration = "300" android:valueTo = "200" android:valueType = "intType" /> <objectAnimator android:propertyName = "y" android:duration = "300" android:valueTo = "300" android:valueType = "intType" /> </set>
但是实际开发中建议用代码实现属性动画
Public static interface AnimatorListener{
Void onAnimationStart(Animator animator);
Void onAnimationEnd(Animator animator);
Void onAnimationCancel(Animator animator);
Void onAnimationRepeat(Animator animator);
}
anim.addListener(new AnimatorListener() { @Override public void onAnimationStart(Animator animation) { Log.e(TAG, "onAnimationStart"); } @Override public void onAnimationRepeat(Animator animation) { // TODO Auto-generated method stub Log.e(TAG, "onAnimationRepeat"); } @Override public void onAnimationEnd(Animator animation) { Log.e(TAG, "onAnimationEnd"); } @Override public void onAnimationCancel(Animator animation) { // TODO Auto-generated method stub Log.e(TAG, "onAnimationCancel"); } }); anim.start(); }
它可以监听动画的开始,结束(可以用end,cancel),取消以及重复,
系统还提供了AnimatorListnerAdapter这个,他是AnimatorListner的适配器类,这样就可以有选择的实现上面的4个方法
anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { Log.e(TAG, "onAnimationEnd"); } }); AnimatorListenerAdapter继承了AnimatorListener接口,然后空实现了所有的方法
AnimatorUpdataeListener比较特殊,他会监听整个动画过程,动画师有许多帧组成的,每播放一帧,onAnimationUpdate就会调用一次(每播放一帧默认是10ms)
Public static interface AnimatorUpdateListener{
Void onAnimationUpdateListner(ValueAnimator animation)
}
属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切来说随着时间推移,所传递的值越来越接近最终值。(言简意赅的说就是在规定的一段时间里,不断地调用某个属性的set方法,最后达到要求的最终值)
所以动画生效的条件:
(1)object必须要提供setAbc方法,如果动画开始的时候没有传递初始值,那么还要提供getAbc方法,因为系统要去取abc属性的初始值(如果不满足,crash)
(2)object的setAbc对属性abc所做的改变必须能够通过某种方法反映出来,比如会带来UI的改变之类的(如不满足,动画无效果,但是不会crash)
例子:对Button的width设置属性没有效果?因为虽然内部提供了getWidth和setWidth,但是并不是改变视图的大小,他是TextView新添加的方法,View是没有这个方法的,
Button中的setWidth对应的是android:width是设置最大宽度和最小宽度,不是android:layout_width
1.给你的对象加上get和set,前提是得有权限(基本不能用,除非是自定义的)
2.用一个类来包装原始对象,间接为其提供get和set方法(一般都用)
3.采用ValueAnimator,监听动画过程,自己实现属性的改变(一般都用)。
//下面代码把2,3两个方法都写出来了
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button bt_main_one; private Button bt_main_two; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bt_main_one = (Button) findViewById(R.id.bt_main_one); bt_main_two = (Button) findViewById(R.id.bt_main_two); bt_main_one.setOnClickListener(this); bt_main_two.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.bt_main_one://第一种方法,让button的width变大
ViewWrapper wrapper = new ViewWrapper(bt_main_one); ObjectAnimator.ofInt(wrapper,"width",500).setDuration(1000).start(); break; case R.id.bt_main_two: Intent intent = new Intent(this, SecondActivity.class); startActivity(intent); break; } } private static class ViewWrapper{ private View mTarget; public ViewWrapper(View mTarget) { this.mTarget = mTarget; } public int getWidth(){ return mTarget.getLayoutParams().width; } public void setWidth(int width){ mTarget.getLayoutParams().width = width; mTarget.requestLayout(); } } }SecondActivity.java
public class SecondActivity extends AppCompatActivity implements View.OnClickListener { private Button mButton; //持有一个IntEvalute对象,等会下面估值要用 private IntEvaluator mEvaluator = new IntEvaluator(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); mButton = (Button) findViewById(R.id.bt_second); mButton.setOnClickListener(this); } @Override public void onClick(View v) { if(v == mButton){//第二种改变button的width的方法
performAnimate(mButton,mButton.getWidth(),500); } } public void performAnimate(final View target ,final int start ,final int end){ final ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //获得当前动画的进度值 ,整型,1-100之间 int currentValue = (int) valueAnimator.getAnimatedValue(); //获得当前进度占整个动画过程的比例,浮点型,0-1之间 float fraction = animation.getAnimatedFraction(); //直接调用整型估值器,通过比例计算出宽度,然后再设给Button//估值器的作用其实就是封装了数据随着时间按照一定比例变化的计算过程
mButton.getLayoutParams().width = mEvaluator.evaluate(fraction,start,end); mButton.requestLayout(); } }); valueAnimator.setDuration(2000).start(); } }
(1).OOM问题
当图片数量较多且较大时就极易出现OOM,尽量避免使用帧动画
(2).内存泄露
属性动画中有一种无线循环的动画(infinite),这类动画需要在Activity退出时及时停止,否则将导致Activity无法释放从而造成内存泄露,但是View动画(也就是补间动画)并不会出现这个问题
(3).注意属性动画要在API11,否则要导包nineload...
(4)View动画 有时候播放完成后用setVisibilty(View.gone)失效了,这个时候只要调用view.clearAnimation()清除view动画可解决这个问题
(5).在3.0以前的系统,view动画,属性动画,新的位置都无法触发单击事件(因为3.0以前属性动画实际还是View动画),但是老位置可以在3.0以后的系统,属性动画在新的位置也可以触发单击事件,但是view动画不行
(6)建议开启硬件加速