Android_动画

动画

Android动画分类

  • 补间动画:提供开始结束数值和动画时间,系统自动生成中间动画
  • 逐帧动画:快速播放图片形成动画
  • 属性动画:通过不断修改控件属性形成动画效果

1. 补间动画

1.1 简介

分为平移translate、缩放scale、旋转rotate和透明度alpha

特点

  • 优点:简单、方便
  • 缺点:仅仅控制整体效果,没法控制属性

使用场景

  • 常规动画效果
  • 特殊应用场景:
    • Activity切换效果
    • Fragment切换效果
    • ViewGroup中子元素出场效果

1.2 基本使用

xml方式

  1. 在res/anim中创建动画效果xml文件
<!--
平移 —— <translate>
旋转 —— <rotate>
缩放 —— <scale>
透明度 —— <alpha>
-->
<translate ... 
	// 通用属性
    android:duration="3000" 动画持续时间ms
    android:startOffset="1000" 动画延迟开始时间ms
    android:fillBefore="true" 播放完是否停留在开始状态
    android:fillAfter="false" ...是否停留在结束状态
    android:fillEnabled="true" 是否应用fillBefore值
    android:repeatMode="restart" 选择重复播放动画模式,restart、reverse
    android:repeatCount="0" 重放次数,infinite为无限重放
    android:interpolator="..." 插值器
    ...(特有属性) />
<!--
特有属性
平移
	fromXDelta、fromYDelta / toXDelta、toYDelta
旋转
	fromDegrees、toDegrees 旋转角度
	pivotX、pivotY
	轴点,可取数字、百分比、百分比p
缩放
	fromXScale、fromYScale / toXScale、toYScale
	1表示原始大小
	pivotX、pivotY
透明度
	fromAlpha、toAlpha
	取值范围(-1~1)
-->
  1. Animation anim = AnimationUtils.loadAnimation(context, 动画文件)
  2. 实现动画的控件.startAnimation(anim)

Java方式

  1. 实例相应的类平移TranslateAnimation、旋转RotateAnimation、缩放ScaleAnimation、透明度AlphaAnimation,其构造方法参数对应其标签的特有属性
  2. 其他设置,比如anim.setDuration(3000)
  3. 实现动画的控件.startAnimation(anim)

1.3 特殊场景使用

使用系统封装的动画效果

// Activity使用
public void onCreate(Bundle savedInstanceState) {
    //							   淡入			  淡出
    //								↓				↓
    overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
    super.onCreate(savedInstanceState);
}
public void finish() {
    super.finish();
    overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}

// Fragment使用
FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.setTransition(FragmentTransaction.系统预设);
	// TRANSIT_NONE	无动画
	// TRANSIT_FRAGMNET_OPEN 标准打开动画效果
	// TRANSIT_FRAGMNET_CLOSE 标砖关闭动画效果
ft.setCustomAnimations(R.anim.in_from_right, R.anim.out_to_left); // 自定义动画

注意:进入退出页面,一个需要动画、另一个不需要动画,也必须设置时间相同的、没有任何变化的动画,否则会出现黑屏

自定义效果 创建一个动画xml文件,然后如上使用

ViewGroup子元素动画

xml设置

  1. 设置子元素动画
  2. 设置View Group动画文件
<!-- res/anim/anim_layout -->
<layoutAnimation xmlns="..."
	android:delay="0.5" 子元素延迟时间,数值为与子元素动画中设置入场动画的比例
    android:animationOrder="normal" 子元素动画顺序
                 normal 顺序显示 / reverse 倒序显示 / random 随机显示
    android:animation="设置为步骤1中子元素出场动画"/>
  1. 为ViewGroup指定android:layoutAniamtion属性

Java设置

  1. 加载子元素出场动画
  2. LayoutAnimationController controller = new LayoutAnimationController(anim)
  3. 设置属性,比如controller.setDelay(0.5f)
  4. 加载动画的Viewgroup.setLayoutAnimation(controller)

1.4 高级使用

组合动画

  • xml:使用<set>包裹多个动画标签实现,set中可以设置公共属性
  • Java:将其他动画类实例添加到AnimationSet类实例中(addAnimation方法)

2. 插值器

2.1 基本介绍

设置属性值从初始值过度到结束之的变化规律(匀速、加速、减速等),实现非线性动画效果

内置插值器

作用 资源ID Java类
加速 @android:anim/accelerate_interpolator AccelerateInterpolator
快速完成,超出在回到结束样式 …overshoot… OvershootInterpolator
加速 -> 减速 …accelerate_decelerate… AccelerateDecelerateInterpolator
先退后在加速前进 …anticipate… AnticipateInterpolator
先退后在加速前进,超过终点再回到重点 …anticipate_overshoot… AnticipateOvershootInterpolator
最后阶段弹球效果 …bounce… BounceInterpolator
减速 …decelerate… DecelerateInterpolator
匀速 …linear… LinearInterpolator
周期运动 …cycle… CycleInterpolator

2.2 自定义插值器

根据动画进度计算出当前属性值改变的百分比

  • 实现Interpolator或TimeInterpolater接口,重写getInterpolation()方法
  • 属性动画实现TimeInterpolater,用于兼容Interpolator接口,使得过去Interpolator实现列都可以直接用在属性动画中
public interface Interpolator {
    float getInterpolator(float input) {
        // input 变化范围(0~1),随动画进度均匀变化,开始为0,结束为1
        return xxx;	// 返回值用于估值器继续计算fraction值
    }
}
// LinearInterpolator
return input;
// AccelerateDecelerateInterpolator
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;

3. 估值器

设置属性值从开始值过度到结束值的变化具体数值

系统内置估值器

  • IntEvaluator 以整型的形式进行过度
  • FloatEvaluator 以浮点型的形式进行过度
  • ArgbEvaluator 以Argb类型的形式进行过度

3.1 自定义估值器

实现TypeEvaluator接口,重写evaluate()方法

public class ObjectEvaluator implements TypeEvaluator{  

    @Override  
    public Object evaluate(float fraction, Object startValue, Object endValue) {  
        // 参数说明
        // fraction:表示动画完成度(根据它来计算当前动画的值),也是插值器getInterpolation()的返回值
        // startValue:动画的初始值
        // endValue:动画的结束值
        
        // 返回对象动画过渡逻辑计算后的值
        // 即赋给动画属性的具体数值
        return value;  
    } 

4. 属性动画

视图动画存在缺陷:作用对象局限(View)、仅改变视觉效果、动画效果单一

属性动画

  • 可以作用于任意Java对象
  • 可以自定义动画效果

工作原理:不对对属性值进行修改,不断将值赋给对象属性

主要通过ValueAnimator和ObjectAnimator类实现

4.1 ValueAnimator

通过不断控制值的变化,再不断手动赋给对象属性

  • ValueAnimator.ofInt(int value) 内置估值器IntEvaluator
  • ValueAnimator.ofFloat(float value) 内置估值器FloatEvaluator
  • ValueAnimator.ofObject(int value)

xml设置

  1. 在res/animator创建动画xml文件
    1. 使用<animator>标签
    2. 特有属性:valueFrom、valueTo、valueType(变化值类型,floatType & intType)
    3. 公共属性和补间动画中相同
  2. Animator animator = AnimatorInflater.loadAnimator(context, 动画xml)
  3. animator.setTarget(view) 设置动画对象
  4. animator.start()

Java设置

  1. ValueAniamtor anim = ValueAnimator.toInt(0, 3)
    1. public static ValueAnimator ofInt(int... values)
  2. 设置其他属性
  3. 将改变的值手动赋值给对象的属性值,通过动画的更新监听器
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    public void onAnimaionUpdate(ValueAnimator animation) {
        int currentValue = (Integer) animation.getAnimatedValue();
        // 以按钮宽度改变为例
        btn.getLayoutParams().width = currentValue;
        btn.requestLayout();	// 刷新视图,重新绘制
    }
})
  1. anim.start()

ValueAnimator.ofObject()

ValueAnimator anim = ValueAnimator.ofObject(new 自定义估值器, object1, object2)

需要自定义估值器

4.2 ValueAnimator实例说明

实现一个圆形从左上角移动到右下角

// Point.kt
class Point(val x: Float, val y: Float);

自定义估值器

// PointEvaluator.kt
class PointEvaluator : TypeEvaluator<Point> {
    override fun evaluate(fraction: Float, start: Point, end: Point): Point {
        val x = start.x + fraction * (end.x - start.x)
        val y = start.y + fraction * (end.y - start.y)
        
        val point = Point(x, y)
        return point
    }

}

将属性动画作用到自定义View当中

public class MyView extends View {
    public static final float RADIUS = 70f;
    private Point currentPoint;
    private Paint mPaint;

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
    }

    protected void onDraw(Canvas canvas) {
        if (currentPoint == null) {
            currentPoint = new Point(RADIUS, RADIUS);
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
            // 创建开始动画对象点和结束动画对象点
            Point start = new Point(RADIUS, RADIUS);
            Point end = new Point(700f, 1000f);
            // 创建动画对象,设置初始值和结束值
            ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), start, end);
            anim.setDuration(5000);
            anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                public void onAnimationUpdate(ValueAnimator animation) {
                    currentPoint = (Point) animation.getAnimatedValue();
                    // 重新绘制
                    invalidate();
                }
            });
            anim.start();
        } else {
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }
    }
}

将自定义View放到布局中

ofObject()本质上还是操作值,只不过采用将多个值封装到一个对象中,对多个值一起操作

与ObjectAnimator区别

  • ValueAnimator不断改变值,手动赋值给对象的属性从而实现动画效果,间接对对象属性操作
  • ObjectAnimator不断改变值,自动赋值给对象的属性从而实现动画效果,直接对对象属性操作

4.3 ObjectAniamtor

继承自ValueAnimator

Java设置

  1. ObjectAnimator anim = ObjectAnimator.ofFloat(object, proprty, values)
    1. Object object 操作对象
    2. String property 操纵的对象属性
    3. float… values 动画初始值 & 结束值
    4. ofObject还有一个参数是自定义估值器
  2. 其他设置
  3. anim.start()

xml设置

  1. 在res/animtor创建动画xml文件
    1. 使用<objectAniamtor>标签
    2. 相关属性:valueFrom、valueTo、valueType、propertyName(对象变化属性名称)
  2. Animator anim = AnimatorInflater.loadAnimator(context, 动画xml文件)
  3. anim.setTraget(动画对象)
  4. anim.start()

第二个参数作用是让ObjectAnimator类根据传入属性名去寻找改对象对应属性名的set()、get()方法,从而进行对象属性值的赋值

原理解析

@Override  
public void start() {  
    AnimationHandler handler = sAnimationHandler.get();  

    ...
    
    super.start();  
   // 调用父类的start()
   // 因为ObjectAnimator类继承ValueAnimator类,所以调用的是ValueAnimator的star()
   // 经过层层调用,最终会调用到 自动赋值给对象属性值的方法 ->关注1
}  

/*
 * 关注1:自动赋值给对象属性值的逻辑方法
 */
// 步骤1:初始化动画值
private void setupValue(Object target, Keyframe kf) {  
    if (mProperty != null) {  
        kf.setValue(mProperty.get(target));  
        // 初始化时,如果属性的初始值没有提供,则调用属性的get()进行取值
    }  
        kf.setValue(mGetter.invoke(target));   
    }  
}  

// 步骤2:更新动画值
// 当动画下一帧来时(即动画更新的时候),setAnimatedValue()都会被调用
void setAnimatedValue(Object target) {  
    if (mProperty != null) {  
        mProperty.set(target, getAnimatedValue());  
        // 内部调用对象该属性的set(),从而将新的属性值设置给对象属性
    }   
}  

采用ObjectAnimator实现动画效果,需要操作的对象就必须有该属性的get、set方法

4.4 ObjectAnimator实例说明

实现一个圆形颜色渐变

设置对象类谁能够的set、get方法

  • 直接法:继承原始类,直接加上该属性的get、set方法
    • 该对象本身没有这个属性时,采用直接法
  • 间接法:包赚原始动画对象,间接给对象加上该属性的get、set方法,即用一个类来包装原始对象
    • 当属性存在set方法但set方法无法带来预期的UI变化时使用间接法

5. 属性动画使用技巧

5.1 组合动画

使用AnimatorSet类

  • play(anim) 播放当前动画
  • after(delay) 将现有动画延迟x毫秒执行
  • with(anim) 将现有动画和传入动画同时执行
  • after(anim) 将现有动画插入到传入动画之后执行
  • before(anim) …

xml设置:前面讲解过

5.2 快捷使用

ViewPropertyAnimator类

View.animate().xxx().xxx();
// ViewPropertyAnimator的功能建立在animate()上
// 调用animate()方法返回值是一个ViewPropertyAnimator对象,之后的调用的所有方法都是通过该实例完成
// 调用该实例的各种方法来实现动画效果
// ViewPropertyAnimator所有接口方法都使用连缀语法来设计,每个方法的返回值都是它自身的实例
// 因此调用完一个方法后可直接连缀调用另一方法,即可通过一行代码就完成所有动画效果
View.animate().alpha(0f).setDuration(5000).setInterpolator(new BounceInterpolator());

5.3 监听动画

  • Animation类监听动画开始 / 结束 / 重复时的操作
  • addListener()中设置

5.4 动画适配器

解决并不需要监听动画所有时刻的问题,解决实现接口繁琐问题

//							动画适配器
//								↓
anim.addListener(new AnimatorListenerAdapter() {
    public void onAnimationStart(Animator animation) {
        // 只需要重写自己需要的动画时刻
    }
})

版权声明:本文为Vis_jerome原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>