前面给咱们分析了Android
中的帧动画
和补间动画
的特色和用法
Android动画之补间动画用法最全详解
Android 动画之帧动画用法详解java
Android
官方在Anrdoid 3.0
之后又推出了一种新的动画即属性动画
,既然前面的帧动画
和补间动画
能帮助咱们实现大部分的Android动画效果,那么官方为何还要推出这种新的属性动画
呢?android
缘由1:web
补间动画
做用的对象是View
,也就是做用的对象是Android
中的控件,如ImageView
、Button
、TextView
等,也能够做用在布局上如LinearLayout
、ConstraintLayout
、RelativeLayout
等,可是对于一些不是View
的对象,没法对这些对象进行动画操做。好比咱们要对某个控件的某个属性作进行动画操做,如其颜色,这个颜色也能够当作一个对象,但其并非View
对象,补间动画
就没法实现,属性动画
能够对这个颜色值作动画, 能实现一些更加复杂的动画效果。canvas
缘由2:app
补间动画只是改变了View
的视觉效果,而不会真正去改变View的属性
好比咱们对一个图片进行AlphaAnimation
,并在动画先后打印其值ide
Log.i("MainActivity","动画开始前mImageView alpha="+mImageView.getAlpha()); animation = new AlphaAnimation(0, 1); animation.setDuration(2000); mImageView.startAnimation(animation); Log.i("MainActivity","动画结束后mImageView alpha="+mImageView.getAlpha());
从打印的结果能够看出,补间动画
并无改变View
的属性值,而属性动画
不但会帮助咱们实现View
动画的一些视觉效果,并且还能改变View
的属性值。svg
一、属性动画都是经过ValueAnimator
类和ObjectAnimator
类来完成,其中ObjectAnimator
类是对对象作动画,ValueAnimator
类是对值作动画。
二、PropertyValueHolder类
能够同时执行多个动画,AnimatorSet
l类能够将多个动画按必定的秩序前后执行。
三、TypeEvaluator
估值器和Interpolator
差值器函数
咱们了解了下面6个类的基本用法,就基本完全掌握了属性动画布局
ObjectAnimator
类是属性动画中很是重要的一个类,能够经过该类对View
不只能够实现一些基本的移、旋转、缩放和透明度四种基本变换动画,还能实现一些其余属性值的变换动画。
实现方式既能够经过Java代码,也能够经过XML方式来实现,下面咱们来分别介绍下两种方式基本用法。动画
首先咱们先来看一下ObjectAnimator
类最基本的方法
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { ObjectAnimator anim = new ObjectAnimator(target, propertyName); anim.setFloatValues(values); return anim; }
方法中第一个参数Object target
的做用对象一般是View
,也就是Android中的控件或布局。
方法中第二个参数String propertyName
一般是须要执行动画的属性,具体值以下表所示
属性 | 值的用法 |
---|---|
rotation | 以屏幕方向为轴的旋转度数 |
alpha | 透明度 |
translationX / translationY | X/Y方向的位移 |
scaleX /scaleY | X/Y方向的缩放倍数 |
rotationX / rotationY | 以X/Y轴为轴的旋转度数 |
方法中第三个参数float... values
表示属性的变换范围,该参数能够传多个值。
添加一些代码来看一下效果
ImageView imageView = findViewById(R.id.imageView); ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0f, 1f); animator.setDuration(5000); animator.start();
该动画效果表示控件ImageView
的透明度在5s
内由1
变换到0
,再由0
变回 1
。效果以下:
ObjectAnimator
的其余方法使用以下:
ImageView imageView = findViewById(R.id.imageView); ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0f, 1f); animator.setDuration(2000); //动画延迟500ms执行 animator.setStartDelay(500); //执行重复次数 +1 animator.setRepeatCount(3); // 设置动画重复播放模式 RESTART -执行完一遍后从新执行 // REVERSE -执行完一遍后 从末位置往前执行 animator.setRepeatMode(ValueAnimator.RESTART); //监听值变换 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Log.i("MainActivity","value:" +animation.getAnimatedValue()); } }); animator.start();
运行效果:
onAnimationUpdate
回调方法的部分值打印以下
用XML
实现对象动画
一、在res
目录下新建animator
文件夹
二、animator
文件夹下建立动画XML文件,如animator_alpha.xml
往该xml文件中输入以下代码
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:propertyName="alpha" android:valueFrom="1" android:valueTo="0" android:valueType="floatType" />
Java代码中经过加载该xml启动动画
ImageView imageView = findViewById(R.id.imageView); Animator animator = AnimatorInflater.loadAnimator(Main2Activity.this, R.animator.animator_alpha); animator.setTarget(imageView); animator.start();
值动画经过控制值的变化,以后 手动赋值给对象的属性,从而实现动画。
ValueAnimator
的核心方法以下
ValueAnimator ofFloat(float... values) -- 浮点型数值 ValueAnimator ofInt(int... values) -- 整型数值 ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) -- 自定义对象类型
下面咱们来添加值动画,在值动画的监听函数里 来获取值得变化,根据值的变化对控件设置相应的属性。这里的属性能够是控件的任意属性。
final ImageView imageView = findViewById(R.id.imageView); ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); anim.setDuration(5000); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float currentValue = (float) animation.getAnimatedValue(); Log.d("MainActivity", "cuurent value is " + currentValue); imageView.setAlpha(currentValue); } }); anim.start();
效果以下
从下面的打印结果中也能够看出,值动画返回了一系列值。
PropertyValueHolder
可让前面的一些动画同时执行。
ImageView imageView = findViewById(R.id.imageView); PropertyValuesHolder alphaProper = PropertyValuesHolder.ofFloat("alpha", 0.5f, 1f); PropertyValuesHolder scaleXProper = PropertyValuesHolder.ofFloat("scaleX", 0f, 1f); PropertyValuesHolder scaleYProper = PropertyValuesHolder.ofFloat("scaleY", 0f, 1f); PropertyValuesHolder translationXProper = PropertyValuesHolder.ofFloat("translationX", -100, 100); PropertyValuesHolder translationYProper = PropertyValuesHolder.ofFloat("translationY", -100, 100); PropertyValuesHolder rotationProper = PropertyValuesHolder.ofFloat("rotation", 0, 360); ValueAnimator animator = ObjectAnimator.ofPropertyValuesHolder(imageView, alphaProper, scaleXProper, scaleYProper,translationXProper,translationYProper,rotationProper); animator.setDuration(5000); animator.start();
前面的PropertyValueHolder
类能实现将多个动画同时执行,AnimatorSet
类不只能让多个动画同时执行,还能让多个动画按必定的顺序执行,同时也能穿插多个动画同时执行。
主要的方法以下:
after(Animator anim)
将现有动画插入到传入的动画以后执行
after(long delay)
将现有动画延迟指定毫秒后执行
before(Animator anim)
将现有动画插入到传入的动画以前执行
with(Animator anim)
将现有动画和传入的动画同时执行
ImageView imageView = findViewById(R.id.imageView); ObjectAnimator rotate = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f); ObjectAnimator translationX = ObjectAnimator.ofFloat(imageView, "translationX", -100, 100f); ObjectAnimator translationY = ObjectAnimator.ofFloat(imageView, "translationY", -100, 100f); ObjectAnimator scaleX = ObjectAnimator.ofFloat(imageView, "scaleX", 0, 1f); ObjectAnimator scaleY = ObjectAnimator.ofFloat(imageView, "scaleY", 0, 1f); ObjectAnimator alpha = ObjectAnimator.ofFloat(imageView, "alpha", 1f, 0f, 1f); AnimatorSet animSet = new AnimatorSet(); animSet.play(rotate) .with(alpha) .after(scaleX) .before(translationX) .after(1000) .before(translationY) .with(scaleY); animSet.setDuration(5000); animSet.start();
效果以下:
前面的动画属性的变换都是均匀变换,能够经过差值器(Interpolator)
来控制值变化的速率
ImageView imageView = findViewById(R.id.imageView); ObjectAnimator animator = ObjectAnimator.ofFloat(imageView, "alpha", 0f, 1f); animator.setDuration(5000); //加速查值器,参数越大,速度愈来愈快 animator.setInterpolator(new AccelerateInterpolator(5)); animator.start();
从下面的运行的效果能够看出,用了加速差值器之后,该图片alpha动画前期变化很慢,都后面变化愈来愈快。
系统咱们提供了九种默认的差值器分别以下:
动画名称 | 效果 |
---|---|
AccelerateInterpolator | 加速查值器,参数越大,速度愈来愈快 |
DecelerateInterpolator | 减速差值起,和加速查值器相反 |
AccelerateDecelerateInterpolator | 先加速后减速 |
AnticipateInterpolator | 前后退在加速前进 |
AnticipateOvershootInterpolator | 以X/Y轴为轴的旋转度数 |
BounceInterpolator | 弹球效果插值 |
CycleInterpolator | 周期运动插值 |
LinearInterpolator | 匀速插值 |
OvershootInterpolator | 先快速完成动画,再回到结束样式 |
咱们还能够经过自定义类实现Interpolator
接口来实现一些自定义的差值器效果。
在前面的值动画(ValueAnimator)
中和对象动画(ObjectAnimator)
有一个传对象的方法:
ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) ObjectAnimator ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values)
这些方法动都须要传一个TypeEvaluator,咱们先来看下这个类的源码
public interface TypeEvaluator<T> { public T evaluate(float fraction, T startValue, T endValue); }
从TypeEvaluator估值器
的源码能够看出该类的做用就是告诉动画,如何从起始值过分到结束值。
Android
源码中有好几个类实现来该接口,也就是系统提供的一些默认估值器, 咱们以FloatEvaluator
为例看下其实现代码。
public class FloatEvaluator implements TypeEvaluator<Number> { public Float evaluate(float fraction, Number startValue, Number endValue) { float startFloat = startValue.floatValue(); return startFloat + fraction * (endValue.floatValue() - startFloat); } }
从FloatEvaluator
的实现能够看出在evaluate
方法中用结束值减去初始值,算出它们之间的差值,而后乘以fraction
这个系数,再加上初始值,那么就获得当前动画的值了
咱们也能够以该方法为例 实现一个自定义的估值器实现一个背景颜色值的变化
咱们先定义一个默认估值器类MyTypeEvaluator
,该类自定义了颜色过渡的方式
package com.lucashu.animation; import android.animation.TypeEvaluator; public class MyTypeEvaluator implements TypeEvaluator<String> { private int mCurrentRed = -1; private int mCurrentGreen = -1; private int mCurrentBlue = -1; @Override public String evaluate(float fraction, String startValue, String endValue) { int startRed = Integer.parseInt(startValue.substring(1, 3), 16); int startGreen = Integer.parseInt(startValue.substring(3, 5), 16); int startBlue = Integer.parseInt(startValue.substring(5, 7), 16); int endRed = Integer.parseInt(endValue.substring(1, 3), 16); int endGreen = Integer.parseInt(endValue.substring(3, 5), 16); int endBlue = Integer.parseInt(endValue.substring(5, 7), 16); // 初始化颜色的值 if (mCurrentRed == -1) { mCurrentRed = startRed; } if (mCurrentGreen == -1) { mCurrentGreen = startGreen; } if (mCurrentBlue == -1) { mCurrentBlue = startBlue; } // 计算初始颜色和结束颜色之间的差值 int redDiff = Math.abs(startRed - endRed); int greenDiff = Math.abs(startGreen - endGreen); int blueDiff = Math.abs(startBlue - endBlue); int colorDiff = redDiff + greenDiff + blueDiff; if (mCurrentRed != endRed) { mCurrentRed = getCurrentColor(startRed, endRed, colorDiff, 0, fraction); } else if (mCurrentGreen != endGreen) { mCurrentGreen = getCurrentColor(startGreen, endGreen, colorDiff, redDiff, fraction); } else if (mCurrentBlue != endBlue) { mCurrentBlue = getCurrentColor(startBlue, endBlue, colorDiff, redDiff + greenDiff, fraction); } // 将计算出的当前颜色的值组装返回 String currentColor = "#" + getHexString(mCurrentRed) + getHexString(mCurrentGreen) + getHexString(mCurrentBlue); return currentColor; } /** * 根据fraction值来计算当前的颜色。 */ private int getCurrentColor(int startColor, int endColor, int colorDiff, int offset, float fraction) { int currentColor; if (startColor > endColor) { currentColor = (int) (startColor - (fraction * colorDiff - offset)); if (currentColor < endColor) { currentColor = endColor; } } else { currentColor = (int) (startColor + (fraction * colorDiff - offset)); if (currentColor > endColor) { currentColor = endColor; } } return currentColor; } /** * 将10进制颜色值转换成16进制。 */ private String getHexString(int value) { String hexString = Integer.toHexString(value); if (hexString.length() == 1) { hexString = "0" + hexString; } return hexString; } }
再自定义一个View,在该类中画一个矩形框
public class MyView extends View { private String color; private Paint mPaint; public MyView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.WHITE); } public String getColor() { return color; } public void setColor(String color) { this.color = color; mPaint.setColor(Color.parseColor(color)); invalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawRect(0, 0, 500,500, mPaint); } }
自顶一个View在布局文件中添加以下
<com.lucashu.animation.MyView android:id="@+id/myview" android:layout_width="200dp" android:layout_height="200dp" android:layout_marginBottom="100dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" />
将在估值器加到动画中,该动画做用在咱们自定义的View上
MyView imageView = findViewById(R.id.myview); ObjectAnimator anim = ObjectAnimator.ofObject( imageView,"color", new MyTypeEvaluator(), "#0000FF","#FF0000"); anim.setDuration(5000); anim.start();
效果以下: