package com.cheng.animationstudy.activity; import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ArgbEvaluator; import android.animation.IntEvaluator; import android.animation.Keyframe; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.TypeEvaluator; import android.animation.ValueAnimator; import android.content.Context; import android.content.Intent; import android.graphics.PointF; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.view.animation.BounceInterpolator; import android.view.animation.LinearInterpolator; import android.widget.Button; import android.widget.Toast; import com.cheng.animationstudy.C; import com.cheng.animationstudy.R; import com.cheng.utils.Logger; public class PropertyAnimActivity extends AppCompatActivity { private View mAnimView; private Button mMenuBtn; private Button mItemBtn1; private Button mItemBtn2; private Button mItemBtn3; private Button mItemBtn4; private Button mItemBtn5; private Button mTestTranslateBtn; private Button mTestRotateBtn; private Button mTestScaleBtn; private Button mTestAlphaBtn; private Button mTestKeyFrameBtn; private Button mTestPropertyValuesHolderBtn; private boolean mIsMenuOpen = false; private boolean mTestSwitch = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_propertyanim); Logger.TAG = "PropertyAnimActivity"; initView(); } private void initView() { this.mAnimView = this.findViewById(R.id.view_anim); this.mMenuBtn = (Button) this.findViewById(R.id.btn_menu); this.mItemBtn1 = (Button) this.findViewById(R.id.btn_item1); this.mItemBtn2 = (Button) this.findViewById(R.id.btn_item2); this.mItemBtn3 = (Button) this.findViewById(R.id.btn_item3); this.mItemBtn4 = (Button) this.findViewById(R.id.btn_item4); this.mItemBtn5 = (Button) this.findViewById(R.id.btn_item5); this.mTestTranslateBtn = (Button) this.findViewById(R.id.btn_testtranslate); this.mTestRotateBtn = (Button) this.findViewById(R.id.btn_testrotate); this.mTestScaleBtn = (Button) this.findViewById(R.id.btn_testscale); this.mTestAlphaBtn = (Button) this.findViewById(R.id.btn_testalpha); this.mTestKeyFrameBtn = (Button) this.findViewById(R.id.btn_testkeyframe); this.mTestPropertyValuesHolderBtn = (Button) this.findViewById(R.id.btn_testpropertyvaluesholder); logValueBtnProperty(mTestTranslateBtn.getId()); } public void onClick(View v) { if (v == mMenuBtn) { if (!mIsMenuOpen) { mIsMenuOpen = true; doAnimateOpen(mItemBtn1, 0, 5, C.Int.MENU_DURATION); doAnimateOpen(mItemBtn2, 1, 5, C.Int.MENU_DURATION); doAnimateOpen(mItemBtn3, 2, 5, C.Int.MENU_DURATION); doAnimateOpen(mItemBtn4, 3, 5, C.Int.MENU_DURATION); doAnimateOpen(mItemBtn5, 4, 5, C.Int.MENU_DURATION); } else { mIsMenuOpen = false; doAnimateClose(mItemBtn1, 0, 5, C.Int.MENU_DURATION); doAnimateClose(mItemBtn2, 1, 5, C.Int.MENU_DURATION); doAnimateClose(mItemBtn3, 2, 5, C.Int.MENU_DURATION); doAnimateClose(mItemBtn4, 3, 5, C.Int.MENU_DURATION); doAnimateClose(mItemBtn5, 4, 5, C.Int.MENU_DURATION); } } else { Toast.makeText(this.getApplicationContext(), "你点击了按钮:" + v, Toast.LENGTH_SHORT).show(); } } /** * 打开菜单的动画 * @param view 执行动画的view * @param index view在动画序列中的顺序 * @param total 动画序列的个数 * @param radius 动画半径 */ private void doAnimateOpen(View view, int index, int total, int radius) { if (view.getVisibility() != View.VISIBLE) { view.setVisibility(View.VISIBLE); } double degree = Math.PI * index / ((total - 1) * 2); int translationX = (int) (radius * Math.cos(degree)); int translationY = (int) (radius * Math.sin(degree)); Logger.d(String.format("degree = %f, translationX = %d, translationY = %d", degree, translationX, translationY)); AnimatorSet set = new AnimatorSet(); // 包含平移、缩放和透明度动画 set.playTogether( ObjectAnimator.ofFloat(view, "translationX", 0, translationX), ObjectAnimator.ofFloat(view, "translationY", 0, translationY), ObjectAnimator.ofFloat(view, "scaleX", 0f, 1f), ObjectAnimator.ofFloat(view, "scaleY", 0f, 1f), ObjectAnimator.ofFloat(view, "alpha", 0f, 1f) ); // 动画周期为600ms set.setDuration(C.Int.MENU_DURATION * 2).start(); } /** * 关闭菜单的动画 * @param view 执行动画的view * @param index view在动画序列中的顺序 * @param total 动画序列的个数 * @param radius 动画半径 */ private void doAnimateClose(final View view, int index, int total, int radius) { if (view.getVisibility() != View.VISIBLE) { view.setVisibility(View.VISIBLE); } double degree = Math.PI * index / ((total - 1) * 2); int translationX = (int) (radius * Math.cos(degree)); int translationY = (int) (radius * Math.sin(degree)); Logger.d(String.format("degree = %f, translationX = %d, translationY = %d", degree, translationX, translationY)); AnimatorSet set = new AnimatorSet(); // 包含平移、缩放和透明度动画 set.playTogether( ObjectAnimator.ofFloat(view, "translationX", translationX, 0), ObjectAnimator.ofFloat(view, "translationY", translationY, 0), ObjectAnimator.ofFloat(view, "scaleX", 1f, 0f), ObjectAnimator.ofFloat(view, "scaleXY", 1f, 0f), ObjectAnimator.ofFloat(view, "alpha", 1f, 0f) ); // 为动画加上事件监听,当动画结束的时候,把当前veiw隐藏 set.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { Logger.d("onAnimationStart"); } @Override public void onAnimationEnd(Animator animator) { Logger.d("onAnimationEnd"); view.setVisibility(View.GONE); } @Override public void onAnimationCancel(Animator animator) { Logger.d("onAnimationCancel"); } @Override public void onAnimationRepeat(Animator animator) { Logger.d("onAnimationRepeat"); } }); set.setDuration(C.Int.MENU_DURATION * 2).start(); } public void showSimpleUse(View v) { /* ObjectAnimator是有条件限制的 1. 对象应该有一个setter方法:set<PropertyName>(驼峰命名法)(提供一个属性) 2. 该对象要有相应属性的 getter方法:get<PropertyName>,如果有getter方法,其应返回值类型应与相应的setter方法的参数类型一致 如果不满足上述条件,解决方法: 1. 给你的对象加上get和set方法,如果有权限的话 2. 用一个类来包装原始对象,间接为其提供get和set方法 3. 采用ValueAnimator,监听动画过程,自己实现属性的改变 */ // 像ofFloat之类的工场方法,第一个参数为对象名(这个也就是我们的view类了), // 第二个为属性名,后面的参数为可变参 数,如果values… // 参数只设置了一个值的话,那么会假定为目的值,属性值的变化范围为当前值到目的值 ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(v, "alpha", 1.0f, 0.1f); objectAnimator.setDuration(C.Int.ANIM_DURATION); objectAnimator.start(); } public void toNext(View v) { Intent intent = new Intent(PropertyAnimActivity.this, PropertyAnimAdvancedActivity.class); startActivity(intent); } public void toNineOldAndroids(View v) { Intent intent = new Intent(PropertyAnimActivity.this, NineOldAndroidsActivity.class); startActivity(intent); } public void testTranslation(View v) { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTestTranslateBtn,"translationX", 0f,100.0f); showPropertyAnim01(objectAnimator, v.getId()); } public void testRotate(View v) { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTestRotateBtn,"rotation", 0f,360f); showPropertyAnim01(objectAnimator, v.getId()); } public void testScale(View v) { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTestScaleBtn,"scaleX", 0f,1.0f); showPropertyAnim01(objectAnimator, v.getId()); } public void testAlpha(View v) { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTestAlphaBtn,"alpha", 0f,1.0f); showPropertyAnim01(objectAnimator, v.getId()); } public void testColor(View v) { ValueAnimator colorAnim = ObjectAnimator.ofInt(v, "backgroundColor", /*Red*/0xFFFF8080, /*Blue*/0xFF8080FF); colorAnim.setDuration(C.Int.ANIM_DURATION * 3); colorAnim.setEvaluator(new ArgbEvaluator()); colorAnim.setRepeatCount(ValueAnimator.INFINITE); colorAnim.setRepeatMode(ValueAnimator.REVERSE); colorAnim.start(); } public void testAnimSet(View v) { AnimatorSet set = new AnimatorSet(); set.playTogether( ObjectAnimator.ofFloat(v, "rotationX", 0, 360), ObjectAnimator.ofFloat(v, "rotationY", 0, 180), ObjectAnimator.ofFloat(v, "rotation", 0, -90), ObjectAnimator.ofFloat(v, "translationX", 0, 90), ObjectAnimator.ofFloat(v, "translationY", 0, 90), ObjectAnimator.ofFloat(v, "scaleX", 1, 1.5f), ObjectAnimator.ofFloat(v, "scaleY", 1, 0.5f), ObjectAnimator.ofFloat(v, "alpha", 1, 0.25f, 1) ); set.setDuration(C.Int.ANIM_DURATION * 5).start(); } public void testKeyFrame(View v) { if (mTestSwitch) { /* keyFrame是一个 时间/值 对,通过它可以定义一个在特定时间的特定状态, 而且在两个keyFrame之间可以定义不同的Interpolator,就相当多个动画的拼接, 第一个动画的结束点是第二个动画的开始点。KeyFrame是抽象类, 要通过ofInt(),ofFloat(),ofObject()获得适当的 KeyFrame, 然后通过PropertyValuesHolder.ofKeyframe获得PropertyValuesHolder对象 */ Keyframe kf0 = Keyframe.ofInt(0, 400); Keyframe kf1 = Keyframe.ofInt(0.25f, 200); Keyframe kf2 = Keyframe.ofInt(0.5f, 400); Keyframe kf3 = Keyframe.ofInt(0.75f, 100); Keyframe kf4 = Keyframe.ofInt(1f, 300); PropertyValuesHolder propertyValuesHolder = PropertyValuesHolder.ofKeyframe("width", kf0, kf1, kf2, kf3, kf4); ObjectAnimator widthAnim = ObjectAnimator.ofPropertyValuesHolder(mTestKeyFrameBtn, propertyValuesHolder); widthAnim.setDuration(C.Int.ANIM_DURATION * 2); widthAnim.start(); /* 上述代码的意思为:设置 valueBtn 对象的width属性值使其: 开始时 Width=400 动画开始1/4时 Width=200 动画开始1/2时 Width=400 动画开始3/4时 Width=100 动画结束时 Width=500 第一个参数为时间百分比,第二个参数是在第一个参数的时间时的属性值。 定义了一些Keyframe后,通过PropertyValuesHolder类的方法ofKeyframe封装, 然后通过ObjectAnimator.ofPropertyValuesHolder获得Animator */ } else { // 设置在动画开始时,旋转角度为0度 Keyframe kf0 = Keyframe.ofFloat(0f, 0f); // 设置在动画执行50%时,旋转角度为360度 Keyframe kf1 = Keyframe.ofFloat(0.5f, 360f); // 设置在动画结束时,旋转角度为0度 Keyframe kf2 = Keyframe.ofFloat(1.0f, 0f); // 使用PropertyValuesHolder进行属性名称和值集合的封装 PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2); // 通过ObjectAnimator进行执行 ObjectAnimator.ofPropertyValuesHolder(v, pvhRotation) .setDuration(C.Int.ANIM_DURATION * 2) .start(); } mTestSwitch = !mTestSwitch; } public void testPropertyValuesHolder(View v) { if (mTestSwitch) { PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 600f); // 同时进行X轴和Y轴的动画 ObjectAnimator xyAnimator = ObjectAnimator.ofPropertyValuesHolder(mTestPropertyValuesHolderBtn, pvhX, pvhY); xyAnimator.setDuration(C.Int.ANIM_DURATION * 2); xyAnimator.start(); } else { int left = v.getLeft(); int right = v.getRight(); // 将view左边增加10像素 PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", left, left + 10); // 将view右边减少10像素 PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", right, right - 10); // 在X轴缩放从原始比例1f,缩小到最小0f,再放大到原始比例1f PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofFloat("scaleX", 1f, 0f, 1f); // 在Y轴缩放从原始比例1f,缩小到最小0f,再放大到原始比例1f PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofFloat("scaleY", 1f, 0f, 1f); // 将PropertyValuesHolder交付给ObjectAnimator进行构建 ObjectAnimator customAnim = ObjectAnimator.ofPropertyValuesHolder(v, pvhLeft, pvhRight, pvhScaleX, pvhScaleY); customAnim.setDuration(C.Int.ANIM_DURATION * 2); customAnim.start(); } mTestSwitch = !mTestSwitch; } public void testWrapperforProperty(View v) { ViewWrapper wrapper = new ViewWrapper(v); // 5s内让Button的宽度增加到500px ObjectAnimator.ofInt(wrapper, "width", v.getWidth(), 500).setDuration(C.Int.ANIM_DURATION * 5).start(); } public void testValueAnimator(final View v) { performAnimate(v, v.getWidth(), 500, mTestSwitch); mTestSwitch = !mTestSwitch; } public void testObjectAnimator(View v) { if (v.getId() == R.id.sdi_objectanimator_btn) { // 简单示例:View的横向移动 ObjectAnimator.ofFloat(mAnimView, "translationX", 0.0f, -200.0f) .setDuration(C.Int.ANIM_DURATION * 2) .start(); } else { // 复合示例:View弹性落下然后弹起,执行一次 ObjectAnimator yBouncer = ObjectAnimator.ofFloat(mAnimView, "y", mAnimView.getY(), 400.0f); yBouncer.setDuration(C.Int.ANIM_DURATION * 2); // 设置插值器(用于调节动画执行过程的速度) yBouncer.setInterpolator(new BounceInterpolator()); // 设置重复次数(缺省为0,表示不重复执行) yBouncer.setRepeatCount(1); // 设置重复模式(RESTART或REVERSE),重复次数大于0或INFINITE生效 yBouncer.setRepeatMode(ValueAnimator.REVERSE); // 设置动画开始的延时时间(200ms) yBouncer.setStartDelay(200); yBouncer.start(); } } public void testAnimatorSet(View v) { /* AnimatorSet bouncer = new AnimatorSet(); bouncer.play(bounceAnim).before(squashAnim1); bouncer.play(squashAnim1).with(squashAnim2); bouncer.play(squashAnim1).with(stretchAnim1); bouncer.play(squashAnim1).with(stretchAnim2); bouncer.play(bounceBackAnim).after(stretchAnim2); ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f); fadeAnim.setDuration(250); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.play(bouncer).before(fadeAnim); animatorSet.start(); */ } public void testAnimatorUpdateListener(final View v) { if (mTestSwitch) { // 1. 在回调中手动更新View对应属性: ValueAnimator.AnimatorUpdateListener listener = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 当前的分值范围为0.0f -> 1.0f // 分度值是动画执行的百分比,区别于AnimatedValue float fraction = animation.getAnimatedFraction(); // 以下的效果为View从完全透明到不透明 v.setAlpha(fraction); // Y方向向下移动300px的距离 v.setTranslationY(fraction * 300.0f); } }; ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f); animator.addUpdateListener(listener); animator.setDuration(C.Int.ANIM_DURATION * 2); animator.start(); } else { MyAnimationView myAnimationView = new MyAnimationView(PropertyAnimActivity.this); ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f); animator.addUpdateListener(myAnimationView); animator.setDuration(C.Int.ANIM_DURATION * 2); animator.start(); } mTestSwitch = !mTestSwitch; } // 2. 在自定义View内部用于引发重绘 private class MyAnimationView extends View implements ValueAnimator.AnimatorUpdateListener { public MyAnimationView(Context context) { super(context); } @Override public void onAnimationUpdate(ValueAnimator animation) { // 手动触发界面重绘 invalidate(); } } public void testAnimatorListener(final View v) { // 1. 实现AnimatorListener中的方法可在动画执行全程进行其他任务的回调执行。 // 2. 也可以添加AnimatorListener的实现类AnimatorListenerAdapter,仅重写需要的监听即可 // 将View透明度从当前的1.0f更新为0.5f,在动画结束时移除该view ObjectAnimator anim = ObjectAnimator.ofFloat(v, "alpha", 0.5f); anim.setDuration(C.Int.ANIM_DURATION * 3); anim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { // 动画开始时调用 } @Override public void onAnimationEnd(Animator animation) { // 动画结束时调用 ViewGroup parent = (ViewGroup) v.getParent(); if (parent != null) { parent.removeView(v); } } @Override public void onAnimationCancel(Animator animation) { // 动画取消时调用 } @Override public void onAnimationRepeat(Animator animation) { // 动画重复时调用 } }); anim.start(); } public void testAnimatorInfalter(View v) { // 加载xml动画 Animator animator = AnimatorInflater.loadAnimator(PropertyAnimActivity.this, R.anim.sda_animatorset_anim); animator.setTarget(mTestSwitch?v:mAnimView); animator.start(); mTestSwitch = !mTestSwitch; } public void testTypeEvaluator(final View v) { // 类型估值 - 抛物线示例 TypeEvaluator<PointF> typeEvaluator = new TypeEvaluator<PointF>() { @Override public PointF evaluate(float fraction, PointF startValue, PointF endValue) { float time = fraction * 3; Logger.e(time + ""); // x方向120px/s,y方向0.5 * 200 * t * t PointF pointF = new PointF(); pointF.x = 120 * time; pointF.y = 0.5f * 200 * time * time; return pointF; } }; ValueAnimator valueAnimator = ValueAnimator.ofObject(typeEvaluator, new PointF(0, 0)); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.setDuration(C.Int.ANIM_DURATION * 3); valueAnimator.start(); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { PointF pointF = (PointF) animation.getAnimatedValue(); v.setX(pointF.x); v.setY(pointF.y); } }); } public void testTimeInterpolator(View v) { /* 1. 几种常见的插值器: Interpolator对象 资源ID 功能作用 AccelerateDecelerateInterpolator @android:anim/accelerate_decelerate_interpolator 先加速再减速 AccelerateInterpolator @android:anim/accelerate_interpolator 加速 AnticipateInterpolator @android:anim/anticipate_interpolator 先回退一小步然后加速前进 AnticipateOvershootInterpolator @android:anim/anticipate_overshoot_interpolator 在上一个基础上超出终点一小步再回到终点 BounceInterpolator @android:anim/bounce_interpolator 最后阶段弹球效果 CycleInterpolator @android:anim/cycle_interpolator 周期运动 DecelerateInterpolator @android:anim/decelerate_interpolator 减速 LinearInterpolator @android:anim/linear_interpolator 匀速 OvershootInterpolator @android:anim/overshoot_interpolator 快速到达终点并超出一小步最后回到终点 2. 自定义插值器 a.实现Interpolator(TimeInterpolator)接口; b.重写接口函数float getInterpolation(floatinput)。 */ } private void showPropertyAnim01(ObjectAnimator objectAnimator, final int btnId) { objectAnimator.setDuration(C.Int.ANIM_DURATION); objectAnimator.start(); objectAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { logValueBtnProperty(btnId); } }); } private void logValueBtnProperty(int btnId) { switch (btnId) { case R.id.btn_testtranslate: Logger.i("mTestTranslateBtn.getLeft()=" + mTestTranslateBtn.getLeft()); Logger.i("mTestTranslateBtn.getX()=" + mTestTranslateBtn.getX()); Logger.i("mTestTranslateBtn.getTranslationX()=" + mTestTranslateBtn.getTranslationX()); break; case R.id.btn_testrotate: Logger.i("mTestRotateBtn.getRotation()=" + mTestRotateBtn.getRotation()); Logger.i("mTestRotateBtn.getX()=" + mTestRotateBtn.getX()); Logger.i("mTestRotateBtn.getRotationX()=" + mTestRotateBtn.getRotationX()); break; case R.id.btn_testscale: break; case R.id.btn_testalpha: break; } } private void performAnimate(final View target, final int start, final int end, boolean flag) { if (flag) { ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { // 持有一个IntEvaluator对象,方便下面估值的时候使用 private IntEvaluator mEvaluator = new IntEvaluator(); @Override public void onAnimationUpdate(ValueAnimator animator) { // 获得当前动画的进度值,整型,1-100之间 int currentValue = (int) animator.getAnimatedValue(); Logger.d("Current value: " + currentValue); // 计算当前进度占整个动画过程的比例,浮点型,0-1之间 float fraction = currentValue / 100f; // 直接调用整型估值器通过比例计算出宽度,然后再设给Button target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end); target.requestLayout(); } }); valueAnimator.setDuration(C.Int.ANIM_DURATION * 5); valueAnimator.start(); } else { ValueAnimator animator = ValueAnimator.ofFloat(0f, 200.0f); // 设置作用对象 animator.setTarget(target); animator.setDuration(C.Int.ANIM_DURATION * 2); // 添加动画更新监听 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 获取当前值 float currentValue = (float) animation.getAnimatedValue(); // 设置横向偏移量 target.setTranslationX(currentValue); target.setTranslationY(currentValue); } }); animator.start(); } } /** * 一个包装类用来包装原始对象,间接为其提供get和set方法 */ private static class ViewWrapper { private View mTarget; public ViewWrapper(View target) { this.mTarget = target; } public int getWidth() { return mTarget.getLayoutParams().width; } public void setWidth(int width) { mTarget.getLayoutParams().width = width; mTarget.requestLayout(); } } /* 在Property Animation中,改变的是对象的实际属性, 而且Property Animation不止可以应用于View,还可以应用于任何对象 ValueAnimator类就是这个的包含Property Animation的动画所以核心功能, 如动画的时间,开始,结束的属性值等等, 对于ValueAnimator来说我们一般都不会直接使用这个, 我们都是直接使用ValueAnimator的子类就是ObjectAnimator 可以监听动画的开始和结束,可以使用AnimatorListener监听 objectAnimator.addListener(new AnimatorListener(){}} 也可以使用AnimatorListenerAdapter这个适配器来进行监听 objectAnimator.addListener(new AnimatorListenerAdapter(){}} 想要同时实现很多个动画就需要AnimatorSet这个类来实现 AnimatorSet提供了before,with,after方法 经常使用一些动画属性 1. translationX,translationY,x,y 这些 translationX和x,TranslationY和y是有区别的 无论啥样应用动画,getLeft的值是不会变的,而TranslationX的值 是为最终位置于布局时初始位置的差,即“最终位置-getLeft()", 而x为最终位置之和,即”getLeft()+getTranslationX()“, 所以当getLeft为0的时候就会发现两个值是相等的 2. rotation,totationX,rotationY 旋转,rotation用于2D旋转角度,3D中用到后两个 3. scaleX,scaleY 缩放 4. alpha 透明度 */ }