目录:
1.属性动画原理
2.差值器和估值器
3.动画的常用操作,组合
补间动画和属性动画区别,几种动画的区别
具体的案例

1.属性动画原理
作用: 属性动画可以改变view的属性和View里面的某个对象操作
其实就是利用插值器和估值器,来计算出各个时刻View的属性,然后通过改变View的属性来,实现View的动画效果(set和get方法改变view的属性)。
到这里,属性动画的整个过程以及原理都分析完了。下面来总结一下这个过程:
(1) 动画是由许多的关键帧组成的,这是一个动画能够动起来的最基本的原理。
(2) 属性动画的主要组成是 PropertyValuesHolder,而 PropertyValuesHolder 又封装了关键帧。
(3) 动画开始后,其监听了 Choreographer 的 vsync,使得其可以不断地调用 doAnimationFrame() 来驱动动画执行每一个关键帧。
(4) 每一次的 doAnimationFrame() 调用都会去计算时间插值,而通过时间插值器计算得到 fraction 又会传给估值器,使得估值器可以计算出属性的当前值。
(5) 最后再通过 PropertyValuesHolder 所记录下的 Setter 方法,以反射的方式来修改目标属性的值。当属性值一帧一帧的改变后,形成连续后,便是我们所见到的动画。
比较好的总结:
在调用start()方法或,属性动画会执行一些初始化工作,并通知动画流程开始;
然后通过AnimationHandler将动画回调注册进Choreographer的工作队列中,一次监听屏幕刷新,直至动画结束(这里是每帧处理一次,当回调列表数量大于0时就继续注册监听)
View的属性动画:
1.插值器:作用是根据时间的流逝的百分比来计算属性改变的百分比(比如时间擦值器:TimeInterPolater)
2.估值器:在1的基础上由这个东西来计算出属性到底变化了多少数值的类
问题: 属性动画更新时会回调onDraw吗?
属性动画更新时会回调onDraw吗?不会,因为它内部是通过AnimationHandler中的Choreographer机制来实现的更新
其核心方法为ValueAnimator.doAnimationFrame:
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
//帧回调,在此方法里通知给所有的动画
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
//仅当有动画时才向Provider注册帧回调
if (mAnimationCallbacks.size() == 0) {
//默认为MyFrameCallbackProvider,实现是通过持有的Choreographer进行postFrameCallback
getProvider().postFrameCallback(mFrameCallback);
}
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
}
public final boolean doAnimationFrame(long frameTime) {
...
if (mPaused) {
mPauseTime = frameTime;
//如果暂停,移除callback,在调用resume时再次注册
removeAnimationCallback();
return false;
} else if (mResumed) {
mResumed = false;
if (mPauseTime > 0) {
mStartTime += (frameTime - mPauseTime);
}
}
if (!mRunning) {
if (mStartTime > frameTime && mSeekFraction == -1) {
return false;
} else {
//在延迟开启的情况下,此时实际开始动画
mRunning = true;
//初始化动画
startAnimation();
}
}
if (mLastFrameTime < 0) {
if (mSeekFraction >= 0) {
long seekTime = (long) (getScaledDuration() * mSeekFraction);
mStartTime = frameTime - seekTime;
mSeekFraction = -1;
}
mStartTimeCommitted = false;
}
mLastFrameTime = frameTime;
final long currentTime = Math.max(frameTime, mStartTime);
//在此计算动画时间
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
//结束回调
endAnimation();
}
return finished;
}
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
...
//实际计算动画值
animateValue(currentIterationFraction);
}
return done;
}
2.差值器和估值器
2.1 Evaluator其实就是一个转换器,他能把小数进度转换成对应的数值位置
估值器原理:系统的一些估值器
@RestrictTo(LIBRARY_GROUP_PREFIX)
public class ArgbEvaluator implements TypeEvaluator {
private static final ArgbEvaluator sInstance = new ArgbEvaluator();
/**
* Returns an instance of <code>ArgbEvaluator</code> that may be used in
* {@link ValueAnimator#setEvaluator(TypeEvaluator)}. The same instance may
* be used in multiple <code>Animator</code>s because it holds no state.
*
* @return An instance of <code>ArgbEvalutor</code>.
*/
public static ArgbEvaluator getInstance() {
return sInstance;
}
/**
* This function returns the calculated in-between value for a color
* given integers that represent the start and end values in the four
* bytes of the 32-bit int. Each channel is separately linearly interpolated
* and the resulting calculated values are recombined into the return value.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue A 32-bit int value representing colors in the
* separate bytes of the parameter
* @param endValue A 32-bit int value representing colors in the
* separate bytes of the parameter
* @return A value that is calculated to be the linearly interpolated
* result, derived by separating the start and end values into separate
* color channels and interpolating each one separately, recombining the
* resulting values in the same way.
*/
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
float startB = ((startInt) & 0xff) / 255.0f;
int endInt = (Integer) endValue;
float endA = ((endInt >> 24) & 0xff) / 255.0f;
float endR = ((endInt >> 16) & 0xff) / 255.0f;
float endG = ((endInt >> 8) & 0xff) / 255.0f;
float endB = ((endInt) & 0xff) / 255.0f;
// convert from sRGB to linear
startR = (float) Math.pow(startR, 2.2);
startG = (float) Math.pow(startG, 2.2);
startB = (float) Math.pow(startB, 2.2);
endR = (float) Math.pow(endR, 2.2);
endG = (float) Math.pow(endG, 2.2);
endB = (float) Math.pow(endB, 2.2);
// compute the interpolated color in linear space
float a = startA + fraction * (endA - startA);
float r = startR + fraction * (endR - startR);
float g = startG + fraction * (endG - startG);
float b = startB + fraction * (endB - startB);
// convert back to sRGB in the [0..255] range
a = a * 255.0f;
r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
}
2.2 Android 四种特殊Interpolator源码解析 动画原理解析 插值器的意义其实就相当于物理公式中的加速度参数
基类最重要的方法:getInterpolation()
public interface TimeInterpolator {
float getInterpolation(float input);
}
3.动画的常用操作,组合
3.1 常用的类
1).ValueAnimator,ValueAnimator有个缺点,就是只能对数值对动画计算
2). ObjectAnimator: 为了能让动画直接与对应控件相关联,以使我们从监听动画过程中解放出来,谷歌的开发人员在ValueAnimator的基础上,又派生了一个类ObjectAnimator;
3). AnimatorSet: 用于把不同的动画进行组合
4).playTogether:表示将所有动画一起播放
4. 补间动画和属性动画区别,几种动画的区别
4.1 这个是补间动画的原理:通过矩阵的变换。
工作原理:在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果。
ValueAnimator:通过不断控制值的变化(初始值->结束值),将值手动赋值给对象的属性,再不断调用View 的invalidate()方法,去不断onDraw 重绘
view,达到动画的效果。
Animation框架定义了透明度,旋转,缩放和位移几种常见的动画,而且控制的是整个View,实现原理是每次绘制视图时View所在的ViewGroup中的drawChild函数获取该View的Animation的Transformation值,然后调用canvas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧,如果动画没有完成,继续调用invalidate()函数,启动下次绘制来驱动动画,动画过程中的帧之间间隙时间是绘制函数所消耗的时间,可能会导致动画消耗比较多的CPU资源,最重要的是,动画改变的只是显示,并不能相应事件。
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
...
//可以理解为是否还有动画的标志位
boolean more = false;
...
//动画最终用到的transform
Transformation transformToApply = null;
...
//获取view的动画
final Animation a = getAnimation();
if (a != null) {
//判断是否需要应用动画的核心方法,如果有动画则会重新调用绘制
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
transformToApply = parent.getChildTransformation();
}
...
//如果apply不为空(有动画时赋值),则将动画变换矩阵运用到canvas上
if (transformToApply != null) {
if (concatMatrix) {
if (drawingWithRenderNode) {
renderNode.setAnimationMatrix(transformToApply.getMatrix());
} else {
canvas.translate(-transX, -transY);
canvas.concat(transformToApply.getMatrix());
canvas.translate(transX, transY);
}
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
float transformAlpha = transformToApply.getAlpha();
if (transformAlpha < 1) {
alpha *= transformAlpha;
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
}
...
return more;
}
getTransformation内部会计算动画的进度并将view传递的transformation进行设值,最终返回给view.draw方法应用到canvas画布。
4.2 补间动画还有一个致命的缺陷,
就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。
问题: 能否兼容点击事件?
老版本的不能捕捉点击事件,其他的动画,只是一个影像而已!
4.3 其他几种实现动画的方式
1). setX跟setTranslationX区别
5. 具体的案例
public class DouYinLayout extends RelativeLayout {
public DouYinLayout(Context context) {
this(context, null);
}
public DouYinLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
init();
}
public DouYinLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private CircleView circleViewRedLeft, circleViewBlueRight;
private float translationX = 30;
/**
* 把2个点添加进去
*/
private void init() {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(CENTER_IN_PARENT);//设置位置居中
circleViewRedLeft = new CircleView(getContext());
circleViewBlueRight = new CircleView(getContext());
addView(circleViewRedLeft, layoutParams);
addView(circleViewBlueRight, layoutParams);
circleViewRedLeft.changeCorlor(Color.RED);
circleViewBlueRight.changeCorlor(Color.BLUE);
oneAnimation();
}
private void oneAnimation() {
//动起来:中间的先向左移动。然后向右移动。然后不停的循环
ObjectAnimator animatorleft = ObjectAnimator.ofFloat(circleViewRedLeft, "translationX", 0, -translationX);//left move
ObjectAnimator animatorRight = ObjectAnimator.ofFloat(circleViewBlueRight, "translationX", 0, translationX);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(animatorleft, animatorRight);
animatorSet.setDuration(300);//
animatorSet.start();
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
circleViewRedLeft.changeCorlor(Color.BLUE);
circleViewBlueRight.changeCorlor(Color.RED);
twoAnimation();
}
});
}
private void twoAnimation() {
ObjectAnimator animatorleft = ObjectAnimator.ofFloat(circleViewRedLeft, "translationX", -translationX, 0);//left move
ObjectAnimator animatorRight = ObjectAnimator.ofFloat(circleViewBlueRight, "translationX", translationX, 0);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(animatorleft, animatorRight);
animatorSet.setDuration(300);//
animatorSet.start();
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
circleViewRedLeft.changeCorlor(Color.RED);
circleViewBlueRight.changeCorlor(Color.BLUE);
oneAnimation();
}
});
}
}