Android 动画总结

动画相关面试总结:Android 动画相关面试总结

动画分类

Android动画分为视图动画属性动画

动画系列文章:动画入门和进阶文章

在这里插入图片描述


视图动画

包括:逐帧动画(frame-by-frame animation)、补间动画(tweened animation)
作用:能为View对象添加动画效果。

优点:使用简单,并且设置需要的时间较短。

缺点

只能对公开对象的部分添加动画效果(例如:可以对View缩放和旋转添加动画,但无法对背景颜色这样做);
只能对绘制视图的位置进行修改,而不能修改实际的视图本身(例如:View位移到了上方,但是其点击区域还是在原来的地方)。


属性动画

包括:ValueAnimator、ObjectAnimator 、AnimatorSet

作用:可以为任何对象(视图和非视图)的任何属性添加动画效果,也可以产生动画过程的数据。

优点:相比视图动画,使用相对复杂,设置需要的时间更长。

缺点

可以为任何对象(视图和非视图)的任何属性添加动画效果

能够修改实际对象本身


视图动画与属性动画的一些区别

视图动画继承自Animation,在android.view.animation包中。

Animation内部是没有估值器(Evaluator)的,只有插值器(Interpolator)!子类也没有估值器的踪影

属性动画继承自Animator,在android.animation包中。

ValueAnimator有插值器(Interpolator),并且数据单元PropertyValuesHolder中会有估值器(Evaluator)的设置。

AnimatorSet也是继承自Animator,所有AnimatorSet是属于属性动画。


为什么视图动画只能修改视图而无法改变自身属性?
//启动方式1 ===========================
view.startAnimation(animation);

//View#startAnimation
public void startAnimation(Animation animation) {
		animation.setStartTime(Animation.START_ON_FIRST_FRAME); //Animation.START_ON_FIRST_FRAME就是-1
		setAnimation(animation);
		invalidateParentCaches();
		invalidate(true); //让view重绘
}

//启动方式2 ===========================
view.setAnimation(animation);
animation.start();

//Animation#start:没设置开始动画的时间,需要等到下次view重绘
public void start() {
		setStartTime(-1);
}

//Animation#startNow:设置了开始时间,能非常快就执行动画(相当于立即执行动画)
public void startNow() {
    setStartTime(AnimationUtils.currentAnimationTimeMillis());
}


//两种启动方式其实都没啥区别。都是设置给view设置了animation之后,就等待动画执行。动画并不会马上执行的,需要等到view的下次重绘才会执行。
  
view#draw() -> view#applyLegacyAnimation() -> animation#getTransformation() -> animation#applyTransformation()

  	//View#draw
		boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
        ...
        final Animation a = getAnimation();
        if (a != null) {
            more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
            ...
        }

       ...

        if (more && hardwareAcceleratedCanvas) {
            if (a.hasAlpha() && (mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) {
                //还有动画继续通知
                invalidate(true);
            }
        }

        ...

        return more;
    }

		//View#applyLegacyAnimation
		private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
            Animation a, boolean scalingRequired) {
        ...
        final boolean initialized = a.isInitialized();
        if (!initialized) {
            ...
            onAnimationStart();
        }

        final Transformation t = parent.getChildTransformation();
        boolean more = a.getTransformation(drawingTime, t, 1f);
        if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
            if (parent.mInvalidationTransformation == null) {
                parent.mInvalidationTransformation = new Transformation();
            }
            invalidationTransform = parent.mInvalidationTransformation;
            a.getTransformation(drawingTime, invalidationTransform, 1f);
        } else {
            invalidationTransform = t;
        }
        ...
        return more;
    }

		//Animation#applyLegacyAnimation
		public boolean getTransformation(long currentTime, Transformation outTransformation,
            float scale) {
        mScaleFactor = scale;
        return getTransformation(currentTime, outTransformation);
    }

		//Animation#getTransformation
		public boolean getTransformation(long currentTime, Transformation outTransformation) {
        ...
        if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
            ...
            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
            applyTransformation(interpolatedTime, outTransformation); //依靠子类重写该方法,实现具体逻辑
        }

        ...
        return mMore;
    }

		//AlphaAnimation#applyTransformation
		@Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final float alpha = mFromAlpha;
        t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
    }

/* 总结:
绘制子view都会先对画布状态进行保存save(),绘制完后又会恢复restore(),所以一个view的绘制不会影响另外一个子view的绘制。但如果该view是viewgroup,会影响到其所有的子view的绘制。
动画是通过父view来不断调整子view的画布canvas坐标系来实现的,发生动画的其实是父View而不是该view。所以 补间动画其实只是调整了子view画布canvas的坐标系,其实并没有修改任何属性,所以只能在原位置才能处理触摸事件。

当view调用了 View.startAnimation() 时动画并没有马上就执行,会触发遍历view树的绘制,
调用到 View 的 draw() 方法,如果 View 有绑定动画,那么会去调用applyLegacyAnimation(),
内部调用 getTransformation() 来根据当前时间计算动画进度,紧接着调用 applyTransformation() 并传入动画进度来应用动画。
getTransformation() 会返回动画是否执行完成的状态, applyLegacyAnimation() 会根据 getTransformation() 的返回值
来决定是否通知 ViewRootImpl 再发起一次遍历请求,遍历 View 树绘制,重复上面的步骤,直到动画结束。
*/

视图动画介绍

帧动画

由N张静态图片收集通过轮播,让人看感觉像是会动一样。

AnimationDrawable - 帧动画的使用
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false"> 
    <item android:drawable="@drawable/ic_play_pink_00" android:duration="50" />
    <item android:drawable="@drawable/ic_play_pink_01" android:duration="50" />
    <item android:drawable="@drawable/ic_play_pink_02" android:duration="50" />
    <item android:drawable="@drawable/ic_play_pink_03" android:duration="50" />
    <item android:drawable="@drawable/ic_play_pink_04" android:duration="50" />
</animation-list>

oneshot:true:只播放一次;false:循环播放。

AnimationDrawable drawable = (AnimationDrawable) mContext.getResources().getDrawable(R.drawable.anim_living);
drawable.start();
imageView.setImageDrawable(drawable);
帧动画优化 - 避免oom

在帧动画资源特别多的情况下,此时用AnimationDrawable就不是那么理想了。因为AnimationDrawable使用一个Drawable数组来存储每一帧的图像,会直接把全部图片加载进内存。在内存紧张的情况下会出现卡顿甚至OOM。


减少帧数、压缩图片

降低帧动画的帧数,别搞那么高帧率了,能用就行。

将所有资源图片压缩一波,什么png转webp统统走一遍。

如果播放完了给View来一波stopAnimation()、setDrawable(null)等。


bitmap优化

http://stackoverflow.com/questions/8692328/causing-outofmemoryerror-in-frame-by-frame-animation-in-android


surfaceView

https://blog.csdn.net/qq_25804863/article/details/65634864

https://www.jianshu.com/p/edcca8d3dd00


补间动画

补间动画开发者只需指定动画开始,以及动画结束"关键帧", 而动画变化的"中间帧"则由系统计算并补齐


补间动画类型
  • AlphaAnimation:透明度渐变效果

    对应xml标签<alpha>

  • ScaleAnimation:缩放渐变效果

    对应xml标签<scale>

  • TranslateAnimation:位移渐变效果

    对应xml标签<translate>

  • RotateAnimation:旋转渐变效果

    对应xml标签<rotate>

  • AnimationSet:组合渐变,就是前面多种渐变的组合

    对应xml标签<set>


插值器 - Interpolator

插值器是用来控制动画变化的速率

android提供的插值器

  • LinearInterpolator:动画以均匀的速度改变
  • AccelerateInterpolator:在动画开始的地方改变速度较慢,然后开始加速
  • AccelerateDecelerateInterpolator:在动画开始、结束的地方改变速度较慢,中间时加速
  • CycleInterpolator:动画循环播放特定次数,变化速度按正弦曲线改变: Math.sin(2 * mCycles * Math.PI * input)
  • DecelerateInterpolator:在动画开始的地方改变速度较快,然后开始减速
  • AnticipateInterpolator:反向,先向相反方向改变一段再加速播放
  • AnticipateOvershootInterpolator:开始的时候向后然后向前甩一定值后返回最后的值
  • BounceInterpolator: 跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100
  • OvershottInterpolator:回弹,最后超出目的值然后缓慢改变到目的值

当然我们也可以通过实现Interpolator 接口来自定义插值器。


自定义Interpolator的步骤

Step1:实现Interpolator接口,重写getInterpolation方法;

Step2:在getInterpolation方法利用参数input得到动画进度,通过input计算出你想要的进度并返回。

//如系统提供的加速插值器
public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
    private final float mFactor;
    private final double mDoubleFactor;

    public AccelerateInterpolator() {
        mFactor = 1.0f;
        mDoubleFactor = 2.0;
    }

       public AccelerateInterpolator(float factor) {
        mFactor = factor;
        mDoubleFactor = 2 * mFactor;
    }
    //重点,如果按照默认值,动画的完成状态的变化是动画进度变化(运行时间)的平方。Input=0.1 返回0.01,input=0.9 返回0.81,input=1 返回1
    public float getInterpolation(float input) {
        if (mFactor == 1.0f) {
            return input * input;
        } else {
            return (float)Math.pow(input, mDoubleFactor);
        }
    }
 }


属性动画介绍

ValueAnimator

ValueAnimator是属性动画中重要且最基本的类。ValueAnimator直接子类有两个ObjectAnimatorTimeAnimator

ValueAnimator只对数值进行计算。要想实现动画,需要监听值变化的回调来获取数值,最后操作于目标对象身上。


ValueAnimator提供的创建方法

ValueAnimator.ofInt(int … values)//处理整形参数
ValueAnimator.ofFloat(float … values)//处理浮点型
ValueAnimator. ofArgb(int… values) //处理颜色
ValueAnimator.ofObject(TypeEvaluator evaluator, Object… values)//处理object对象,需要自定义估值器
ValueAnimator.ofPropertyValuesHolder(PropertyValuesHolder… values) //处理PropertyValuesHolder

ValueAnimator的使用
//Step1:利用上面的函数生成ValueAnimator对象
ValueAnimator v = ValueAnimator.ofFloat(0, 1);
v.setDuration(1000);
//Step2:设置动画的监听 addUpdateListener(ValueAnimator.AnimatorUpdateListener listener)
v.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
		@Override
		public void onAnimationUpdate(ValueAnimator animation) {
      	//Step3:利用监听函数获取当前动画的值getAnimatedValue(),设置给View实现动画
				float f = (float) animation.getAnimatedValue();
				view.setAlpha(f);
		}
});
v.start();

估值器 - Evaluator

估值器是用来计算出属性在当前进度的具体值。也就是说插值器是计算进度百分比,而估值器是利用这个百分比,计算出具体值。

每个属性动画都必须有估值器。

//ValueAnimator#ofArgb - 就有ArgbEvaluator
public static ValueAnimator ofArgb(int... values) {
		ValueAnimator anim = new ValueAnimator();
		anim.setIntValues(values);
		anim.setEvaluator(ArgbEvaluator.getInstance());
		return anim;
}

//那为什么ofInt跟ofFloat就没有设置呢?
public static ValueAnimator ofInt(int... values) {
		ValueAnimator anim = new ValueAnimator();
		anim.setIntValues(values);
		return anim;
}

//PropertyValuesHolder#init 因为int/float的估值器在创建PropertyValuesHolder的时候就设置了
void init() {
		if (mEvaluator == null) {
				mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : (mValueType == Float.class) ? sFloatEvaluator : null;
		}
		if (mEvaluator != null) {
			mKeyframes.setEvaluator(mEvaluator);
		}
}

自定义估值器如果使用步骤

Step1:实现TypeEvaluator接口,重写evaluate方法;

Step2:根据evaluate方法提供的参数计算出具体值。

//IntEvaluator.java
public class IntEvaluator implements TypeEvaluator<Integer> {
  	// 参数说明
		// fraction:插值器getInterpolation()的返回值
		// startValue:动画的初始值
		// endValue:动画的结束值
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}

属性动画数据存储单元 - PropertyValuesHolder

PropertyValuesHolder是属性动画的数据存储单元。是用来保存某个属性property对应的一组值,这些值对应了一个动画周期中的所有关键帧。

在属性动画中,不管我们调用哪个ofXXX,最终都会被封装成PropertyValuesHolder,然后保存在ValueAnimator的mValues中。

同时利用PropertyValuesHolder也可以在属性动画中实现类似AnimatorSet的多动画执行的功能。具体可以使用ofPropertyValuesHolder方法。


PropertyValuesHolder的使用

//可以通过Keyframe类实现添加关键帧数据。
Keyframe ka0 = Keyframe.ofFloat(0f, 0f);
Keyframe ka1 = Keyframe.ofFloat(1f, 1f);
PropertyValuesHolder p0 = PropertyValuesHolder.ofKeyframe("alpha", ka0, ka1);

Keyframe ks0 = Keyframe.ofFloat(0f, 0.2f);
Keyframe ks1 = Keyframe.ofFloat(1f, 1.0f);
PropertyValuesHolder p1 = PropertyValuesHolder.ofKeyframe("scaleX", ks0, ks1);

ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(view, p0, p1);
objectAnimator.setDuration(2500);
objectAnimator.start();

ObjectAnimator

由于ValueAnimator只能对数值进行计算,如果要实现动画还需要设置监听器,在回调函数内手动给对象设值。这样使用起来十分不友好。由此诞生了ObjectAnimator。

ObjectAnimator继承自ValueAnimator,在构建ObjectAnimator的时候就可以传入目标对象,让ObjectAnimator帮我们完成动画,简化了我们的代码实现。


ObjectAnimator提供的创建方法

ofArgb(Object target, String propertyName, int... values)
ofArgb(T target, Property<T, Integer> property, int... values)
ofFloat(Object target, String xPropertyName, String yPropertyName, Path path)
ofFloat(T target, Property<T, Float> property, float... values)
ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path)
ofFloat(Object target, String propertyName, float... values)

ofInt(T target, Property<T, Integer> xProperty, Property<T, Integer> yProperty, Path path)
ofInt(T target, Property<T, Integer> property, int... values)
ofInt(Object target, String propertyName, int... values)
ofInt(Object target, String xPropertyName, String yPropertyName, Path path)

ofMultiFloat(Object target, String propertyName, float[][] values)
ofMultiFloat(Object target, String propertyName, Path path)
ofMultiFloat(Object target, String propertyName, TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, T... values)
ofMultiInt(Object target, String propertyName, int[][] values)
ofMultiInt(Object target, String propertyName, Path path)
ofMultiInt(Object target, String propertyName, TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, T... values)

ofObject(T target, Property<T, V> property, TypeEvaluator<V> evaluator, V... values)
ofObject(Object target, String propertyName, TypeConverter<PointF, ?> converter, Path path)
ofObject(T target, Property<T, V> property, TypeConverter<PointF, V> converter, Path path)
ofObject(T target, Property<T, P> property, TypeConverter<V, P> converter, TypeEvaluator<V> evaluator, V... values)
ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values)

ofPropertyValuesHolder(Object target, PropertyValuesHolder... values)

ObjectAnimator的使用
//给view设置透明度
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, "alpha", 0.1f,1.0f);
objectAnimator.setDuration(1000);
objectAnimator.setInterpolator(new LinearInterpolator());

ObjectAnimator的注意

1、必须保证目标对象有对应的属性,并且需要提供该属性的setter方法。ObjectAnimator是靠反射来给目标对象设值的。

"alpha" -> 会反射 setAlpha(系统会帮你强制首字母大写之后加set,再进行反射)

2、如果value…只设置了一个值,需要提供属性的setter方法。ObjectAnimator会认为这是结束值,并且会反射属性的getter方法,获取属性的初始值作为开始值。

3、目标对象的属性必须跟ObjectAnimator.ofXXX的类型相同

4、ObjectAnimator内部会主动调用目标对象的setter方法,但是这并不会导致view的重绘,需要你在setter方法中主动调用invalidate方法。

或者设置动画监听函数在onAnimationUpdate的回调中调用invalidate方法。如果你要自己定义对象的setter方法,就要主动调用invalidate方法。


AnimatorSet

AnimatorSet可以指定一组动画的执行顺序,让它们可以一起执行,顺序执行,延迟执行。

https://blog.csdn.net/u010126792/article/details/85683405


View的属性动画类 - ViewPropertyAnimator

ViewPropertyAnimator内部依然是ValueAnimator实现动画。


ViewPropertyAnimator的基本函数

setDuration(); //设置动画时长
setInterpolator(); //设置插值器
setStartDelay(); //设置延迟开始时间
start(); //立刻开始动画
cancel(); //取消动画


ViewPropertyAnimator的使用

//使用非常简单,直接在View#animate()
view.animate()
			.translationX(10)
			.setDuration(1000)
			.start();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值