Property Animation属性动画
Property Animation属性动画,所谓属性动画:改变一切能改变的对象的属性值
1.补间动画(tween animation)只能进行渐变,平移,旋转,缩放操作
2.补间动画执行的动画的时候只是重新绘制界面,view的原属性没有发生改变,属性动画会改变view的原有属性
3.补间动画只能作用在view控件上,属性动画可以作用在object上
为什么要引入属性动画?
Android之前的补间动画机制其实还算是比较健全的,在android.view.animation包下面有好多的类可以供我们操作,来完成一系列的动画效果,比如说对View进行移动、缩放、旋转和淡入淡出,并且我们还可以借助AnimationSet来将这些动画效果组合起来使用,除此之外还可以通过配置Interpolator来控制动画的播放速度等等等等。那么这里大家可能要产生疑问了,既然之前的动画机制已经这么健全了,为什么还要引入属性动画呢?
其实上面所谓的健全都是相对的,如果你的需求中只需要对View进行移动、缩放、旋转和淡入淡出操作,那么补间动画确实已经足够健全了。但是很显然,这些功能是不足以覆盖所有的场景的,一旦我们的需求超出了移动、缩放、旋转和淡入淡出这四种对View的操作,那么补间动画就不能再帮我们忙了,也就是说它在功能和可扩展方面都有相当大的局限性,那么下面我们就来看看补间动画所不能胜任的场景。
注意上面我在介绍补间动画的时候都有使用“对View进行操作”这样的描述,没错,补间动画是只能够作用在View上的。也就是说,我们可以对一个Button、TextView、甚至是LinearLayout、或者其它任何继承自View的组件进行动画操作,但是如果我们想要对一个非View的对象进行动画操作,抱歉,补间动画就帮不上忙了。可能有的朋友会感到不能理解,我怎么会需要对一个非View的对象进行动画操作呢?这里我举一个简单的例子,比如说我们有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了。显然,补间动画是不具备这个功能的,这是它的第一个缺陷。
然后补间动画还有一个缺陷,就是它只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,那如果我们希望可以对View的背景色进行动态地改变呢?很遗憾,我们只能靠自己去实现了。说白了,之前的补间动画机制就是使用硬编码的方式来完成的,功能限定死就是这些,基本上没有任何扩展性可言。
最后,补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个按钮,然后我们通过补间动画将它移动到了屏幕的右下角,现在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已。
也正是因为这些原因,Android开发团队决定在3.0版本当中引入属性动画这个功能,那么属性动画是不是就把上述的问题全部解决掉了?下面我们就来一起看一看。
新引入的属性动画机制已经不再是针对于View来设计的了,也不限定于只能实现移动、缩放、旋转和淡入淡出这几种动画操作,同时也不再只是一种视觉上的动画效果了。它实际上是一种不断地对值进行操作的机制,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性。所以我们仍然可以将一个View进行移动或者缩放,但同时也可以对自定义View中的Point对象进行动画操作了。我们只需要告诉系统动画的运行时长,需要执行哪种类型的动画,以及动画的初始值和结束值,剩下的工作就可以全部交给系统去完成了。
既然属性动画的实现机制是通过对目标对象进行赋值并修改其属性来实现的,那么之前所说的按钮显示的问题也就不复存在了,如果我们通过属性动画来移动一个按钮,那么这个按钮就是真正的移动了,而不再是仅仅在另外一个位置绘制了而已。
propertyName参数的使用:
(1)setRotationX、setRotationY与setRotation
setRotationX(float rotationX):表示围绕X轴旋转,rotationX表示旋转度数
setRotationY(rotationY):表示围绕Y轴旋转,rotationY表示旋转度数
setRotation(float rotation):表示围绕Z旋转,rotation表示旋转度数
(2)setTranslationX与setTranslationY
setTranslationX(float translationX):表示在X轴上的平移距离,以当前控件为原点,向右为正方向,参数translationX表示移动的距离。
setTranslationY(float translationY):表示在Y轴上的平移距离,以当前控件为原点,向下为正方向,参数translationY表示移动的距离。
(3)setScaleX与setScaleY
setScaleX(float scaleX):在X轴上缩放,scaleX表示缩放倍数
setScaleY(float scaleY):在Y轴上缩放,scaleY表示缩放倍数
(4)透明度:alpha
public void setAlpha(float alpha)
属性动画用java代码实现,不能设置动画执行后的保持状态,都是保持为执行之后的状态
使用java代码实现属性动画:
public class MainActivity extends Activity { private ImageView img; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); img = (ImageView) findViewById(R.id.img_houzi); img.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "点击了图片", 0).show();//能够一直响应单击事件 } }); } //下面都为单击事件 /** * 渐变动画,参数说明: * target: 指被绑定动画的对象 * propertyName: 动画的类型(见上面) * values: 可变参数,代表开始透明的状态到结束的透明度 * 默认保持为执行完动画之后的位置 */ public void onStartAlpha(View v){ //在当前位置渐变,以自己中心为参考渐变 ObjectAnimator alpha = ObjectAnimator.ofFloat(img, "alpha", 0f,0.5f,1f); alpha.setDuration(3000);//设置动画时间(ms) //设置动画插入器,减速,具体见博客:Tween Animation(渐变动画) alpha.setInterpolator(new DecelerateInterpolator()); //alpha.setRepeatCount(-1);//设置动画重复次数,-1代表无限 //alpha.setRepeatMode(Animation.REVERSE);//设置动画循环模式,REVERSE为相反 alpha.start();//启动动画 } /** * 平移动画,参数说明: * 参数1:代表执行动画的对象 * 参数2:见上面的propertyName参数的使用 */ public void onStartTranslate(View v){ ObjectAnimator translateX = ObjectAnimator.ofFloat(img, "translationX",0,200);//X轴的坐标 从相对自己的坐标0 到 坐标200 ObjectAnimator translateY = ObjectAnimator.ofFloat(img, "translationY",0,200);//Y轴的坐标 translateX.setDuration(3000); translateY.setDuration(3000); //默认保持为执行动画后的状态 translateX.start(); translateY.start(); } /** * 旋转动画 */ public void onStartRotate(View v){ //绕着空间系的Z抽旋转 ObjectAnimator rotationZ = ObjectAnimator.ofFloat(img,"rotation", 0,360); //绕着空间系的X抽旋转 ObjectAnimator rotationX = ObjectAnimator.ofFloat(img,"rotationX", 0,360); //绕着空间系的Y抽旋转 ObjectAnimator rotationY = ObjectAnimator.ofFloat(img,"rotationY", 0,360); rotationZ.setDuration(3000); rotationX.setDuration(3000); rotationY.setDuration(3000); rotationY.start(); rotationX.start(); rotationZ.start(); } /** * * 缩放动画 */ public void onStartScacle(View v){ ObjectAnimator scaleX = ObjectAnimator.ofFloat(img,"scaleX",0f,1f,0.5f,2f); ObjectAnimator scaleY = ObjectAnimator.ofFloat(img,"scaleY",0f,1f,0.5f,2f); scaleX.setDuration(4000); scaleY.setDuration(4000); scaleX.start(); scaleY.start(); } /** * 动画的集合(多种动画一起执行) */ public void onStartSet(View v){ //声名属性动画的集合 AnimatorSet animatorSet = new AnimatorSet(); //x缩放 ObjectAnimator scaleX = ObjectAnimator.ofFloat(img,"scaleX",0f,1f); //旋转 ObjectAnimator rotationX = ObjectAnimator.ofFloat(img, "rotationX", 0,360); //y平移 ObjectAnimator translateY = ObjectAnimator.ofFloat(img,"translationY", 0,200); //.before(rotationX);this比rotation之前执行 //.after(translateY);this比translateY后执行 //animatorSet.play(scaleX).with(translateY).with(rotationX);//同时执行动画 //animatorSet.play(scaleX).before(rotationX);//先scaleX后rotationX //先translateY后scaleX,再rotationX animatorSet.play(scaleX).before(rotationX).after(translateY); animatorSet.setDuration(5000);//切记设置集合的执行时间,不然不会同步 animatorSet.start(); } //改变背景颜色的动画实现如下,这也是渐变动画(tween animation)实现不了的 public void onColor(){ ObjectAnimator translationUp = ObjectAnimator.ofInt(img, "backgroundColor", Color.RED, Color.BLUE, Color.GRAY, Color.GREEN); translationUp.setInterpolator(new DecelerateInterpolator()); translationUp.setDuration(1500); translationUp.setRepeatCount(-1); translationUp.setRepeatMode(Animation.REVERSE); /* * ArgbEvaluator:这种评估者可以用来执行类型之间的插值整数值代表ARGB颜色。 * FloatEvaluator:这种评估者可以用来执行浮点值之间的插值。 * IntEvaluator:这种评估者可以用来执行类型int值之间的插值。 * RectEvaluator:这种评估者可以用来执行类型之间的插值矩形值。 * * 由于本例是改变View的backgroundColor属性的背景颜色所以此处使用ArgbEvaluator */ translationUp.setEvaluator(new ArgbEvaluator()); translationUp.start(); } }
2.使用xml文件实现,xml文件需要放在res/animator下
res/animator/set.xml: objectanimator标签(单个动画)
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="sequentially" > <!-- 上面参数表示顺序执行,渐变,从透明到不透明 --> <objectAnimator android:valueType="floatType" android:propertyName="alpha" android:valueFrom="0" android:valueTo="1" android:duration="3000" ></objectAnimator> <!-- 绕X轴旋转,0到180度,执行3秒 --> <objectAnimator android:valueType="floatType" android:propertyName="rotationX" android:valueFrom="0" android:valueTo="180" android:duration="3000" ></objectAnimator> <!-- 可以嵌套set <set ></set> --> </set>
在代码中加载:
//加载xml动画文件 Animator animator = AnimatorInflater.loadAnimator(this, R.animator.set); animator.setTarget(img);//将动画设置到目标上 animator.setDuration(3000);//设置执行时间 animator.start();
监听事件: animatorSet.addListener
插值器的使用:
/** setInterpolator(...) AccelerateDecelerateInterpolator 在动画开始与结束的地方速率改变比较慢,在中间的时候加速 AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速 AnticipateInterpolator 开始的时候向后然后向前甩 AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值 BounceInterpolator 动画结束的时候弹起 CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线 DecelerateInterpolator 在动画开始的地方快然后慢 LinearInterpolator 以常量速率改变 OvershootInterpolator 向前甩一定值后再回到原来位置 */
View中自带的动画方法:
属性动画-Property Animation之ViewPropertyAnimator 你应该知道的一切 使用起来更简单
final View viewById = findViewById(R.id.ll_context); viewById.animate().y(0); //移动到指定位置 new Handler().postDelayed(new Runnable() { @Override public void run() { //当使用viewById.animate().y(0)后;如果再使用translationY,平移就会出现问题,此时translationYBy才能正确代表偏移量 viewById.animate().translationYBy(50).setDuration(2000); //移动的偏移量 //viewById.animate().y(-viewById.getHeight()).setDuration(2000);//移动到指定坐标点 //viewById.animate().alpha(0).setDuration(2000);//透明度 //viewById.animate().scaleX(0.1f).setDuration(2000);//缩放 1表示原值 以中心点方向缩放 //viewById.animate().rotationX(360).setDuration(2000);//旋转 以中心点方向缩放 } },1000);
缩小后放大:
view.animate().apply { scaleX(0f) scaleY(0f) duration = 1000 withEndAction { //动画结束后执行 view.apply { scaleX(1f) scaleY(1f) duration = 1000 }.start() } }.start()
Android 自定义 View 属性动画
https://juejin.cn/post/6844903494256689165#heading-3
例子:
1.属性动画之ValueAnimator:
ValueAnimator valueAnimator = ValueAnimator.ofFloat(30).setDuration(1000); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float currentValue = (Float) animation.getAnimatedValue(); // 获得每次变化后的属性值 LogUtil.e("currentValue = " + currentValue);// [0,30] } }); valueAnimator.start();
2.抖动效果:
/** * 抖动动画 * @param view 需要抖动的view * @param shakeFactor 抖动左右旋转幅度 */ public fun shakeAnim(view: View?, shakeFactor: Float = 0.5f){ //X轴的缩放 val pvhScaleX = PropertyValuesHolder.ofKeyframe(View.SCALE_X, Keyframe.ofFloat(0f, 1f), Keyframe.ofFloat(.1f, .9f), Keyframe.ofFloat(.2f, .9f), Keyframe.ofFloat(.3f, 1.1f), Keyframe.ofFloat(.4f, 1.1f), Keyframe.ofFloat(.5f, 1.1f), Keyframe.ofFloat(.6f, 1.1f), Keyframe.ofFloat(.7f, 1.1f), Keyframe.ofFloat(.8f, 1.1f), Keyframe.ofFloat(.9f, 1.1f), Keyframe.ofFloat(1f, 1f) ) //Y轴的缩放 val pvhScaleY = PropertyValuesHolder.ofKeyframe(View.SCALE_Y, Keyframe.ofFloat(0f, 1f), Keyframe.ofFloat(.1f, .9f), Keyframe.ofFloat(.2f, .9f), Keyframe.ofFloat(.3f, 1.1f), Keyframe.ofFloat(.4f, 1.1f), Keyframe.ofFloat(.5f, 1.1f), Keyframe.ofFloat(.6f, 1.1f), Keyframe.ofFloat(.7f, 1.1f), Keyframe.ofFloat(.8f, 1.1f), Keyframe.ofFloat(.9f, 1.1f), Keyframe.ofFloat(1f, 1f) ) //左右摇动 val pvhRotate = PropertyValuesHolder.ofKeyframe(View.ROTATION, Keyframe.ofFloat(0f, 0f), Keyframe.ofFloat(.1f, -3f * shakeFactor), Keyframe.ofFloat(.2f, -3f * shakeFactor), Keyframe.ofFloat(.3f, 3f * shakeFactor), Keyframe.ofFloat(.4f, -3f * shakeFactor), Keyframe.ofFloat(.5f, 3f * shakeFactor), Keyframe.ofFloat(.6f, -3f * shakeFactor), Keyframe.ofFloat(.7f, 3f * shakeFactor), Keyframe.ofFloat(.8f, -3f * shakeFactor), Keyframe.ofFloat(.9f, 3f * shakeFactor), Keyframe.ofFloat(1f, 0f) ) ObjectAnimator.ofPropertyValuesHolder(view, pvhScaleX, pvhScaleY, pvhRotate).setDuration(1300).start(); }
另一种方式实现:
/** * 左右抖动 */ public static void shakeX(View view) { if (view == null) return; //左右抖动 TranslateAnimation anim = new TranslateAnimation(ConvertUtils.dp2px(5), 0, 0, 0); //上下 左右一起抖动 //TranslateAnimation anim = new TranslateAnimation(ConvertUtils.dp2px(5), 0, ConvertUtils.dp2px(5), 0); anim.setInterpolator(new CycleInterpolator(4f));//抖动次数 anim.setDuration(600); view.startAnimation(anim); }
5.0以上的揭露动画:
//img 对应效果图的 红色ImageView public void startAnim(View img){ //揭露动画 需要5.0以上 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { Animator animator = ViewAnimationUtils.createCircularReveal( img,//view是你需要这个效果的控件 img.getWidth() / 2 ,//动画中心x轴坐标 img.getHeight() / 2, //动画中心y轴坐标 0,//动画开始的半径 img.getWidth() / 2);//动画结束的半径 animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } }); animator.setStartDelay(1000);//动画延时多长时间开始 animator.setDuration(2000); animator.start(); } }
效果图:
如果改成这样: