Android开发补间动画、逐帧动画、属性动画

动画

参考:https://www.jianshu.com/p/2412d00a0ce4
分为三种:补间动画、逐帧动画、属性动画

一、补间动画(Tweened Animation)

作用对象:视图控件View
原理: 通过确定开始的视图样式以及结束的视图样式,中间的动画变化过程有系统补全来确定一个动画
优点:简单、方便,已封装好基础动画效果
缺点:仅控制整体实体效果,无法控制属性
动画样式: 平移动画(Translate)缩放动画(scale)旋转动画(rotate)透明度动画(alpha)
应用场景:视图中,标准、基础的动画效果。Activity,Fragment的切换效果。ViewGroup中子元素的出场效果
数值上的理解:从-100到100的值,以“%”结尾,表示百分比相对于自身;从-100到100的值,以“%p”结尾,表示百分比相对于父容器。例如平移开始位置在自身中间则是50%,如平时开始位置在父容器的则是50%p

1.平移动画(Translate)

TranslateAnimation类
示例代码:


如上代码中,虽然通过fillBefore,fillAfter设置了停留在动画之后,但是控件还是在原来位置,点击动画后的位置无反应。如果不设置fillBefore,fillAfter,动画结束后会还是显示在动画前的位置
Kotlin代码中使用

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_second)

    var translateAnim = AnimationUtils.loadAnimation(this,R.anim.view_anim)

    tv_content.startAnimation(translateAnim)

    tv_content.setOnClickListener {
         Toast.makeText(this, "好的", Toast.LENGTH_SHORT).show()
    }
}

纯Kotlin代码:

var translateAnim = TranslateAnimation(0f,500f,0f,0f)

translateAnim.duration = 3000

tv_content.startAnimation(translateAnim)  

2.缩放动画(Scale)

ScaleAnimation类
xml定义





注意:fromXScale和fromYScale都要写,pivotX只是改变开始缩放的始点,不加缩放的始点是view的左上角,
android:pivotX=”50″是控件view的x方向上加50像素
android:pivotX=”50%”是控件view的x方向上加view控件宽的50%
android:pivotX=”50%p”是控件view的x方向上加父布局控件宽的50%

3.旋转动画(Rotate)

RotateAnimation类



android:fromDegrees=”0″ // 动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)

4.透明度动画(Alpha)

AlphaAnimation类





Activity的切换效果

实现效果从右向左滑动
in_from_right.xml




  

out_to_left.xml





一般用在Activity的成对出现,但至少保持动画时间是一样的
首先要了解Activity的位置
x = 0%p是代表Activity刚好在屏幕中间
x = 100%p是代表Activity在屏幕的右侧
x = -100%p是代表Activity在屏幕的左侧

Fragment的切换效果(几乎用不到)

 // 采用`FragmentTransavtion`的 setCustomAnimations()进行设置

FragmentTransaction fragmentTransaction = mFragmentManager
.beginTransaction();

fragmentTransaction.setCustomAnimations(
R.anim.in_from_right,
R.anim.out_to_left);

// 此处的自定义动画效果同Activity,此处不再过多描述

组合动画

采用标签

监听动画

anim.addListener(new AnimatorListenerAdapter() {  
    // 向addListener()方法中传入适配器对象AnimatorListenerAdapter()
    // 由于AnimatorListenerAdapter中已经实现好每个接口
    // 所以这里不实现全部方法也不会报错
    @Override  
    public void onAnimationStart(Animator animation) {  
        // 如想只想监听动画开始时刻,就只需要单独重写该方法就可以
    }  
});  

二、逐帧动画(Frame Animation)

作用对象:视图控件View
原理:将动画拆分为帧的形式,且定义每一帧是一张图片,按顺序播放
特点:简单、方便
缺点:容易引起OOM,因会用大量以及尺寸较大的图片资源
应用场景: 较为复杂的个性化动画效果

三、属性动画(Property Animation)

作用对象:任意Java对象
原理: 在一定时间间隔内,通过不断改变值以及赋值给对象的属性,从而实现该对象在该属性上的动画效果
特点:作用对象进行了拓展,不只是View对象,甚至无对象也可以,不只是4种基本变换,还有其他动画效果
缺点:用起来稍微复杂点
应用场景: 与属性相关、更加复杂的动画效果,比如通过改变View的颜色属性达到的动画效果

逐帧动画和补间动画存在一定的缺点:

1:作用对象局限于View
即补间动画只能作用View上,没法对非View的对象进行操作。有些情况的动画效果只是视图的某个属性,比如通过视图的颜色动态变化,从而实现动画效果,而不是针对整个View视图进行动画操作
2:没有改变View的属性,只是改变视觉效果
如移动View,虽然看起来View的位置变了,但实际没变,还是原来的
3:动画效果单一
补间动画只能实现平移、旋转、缩放 & 透明度这些简单的动画需求,一旦遇到相对复杂的动画效果,即超出了上述4种动画效果,那么补间动画则无法实现

ValueAnimator类

原理:通过不断控制值的变化,再不断手动赋给对象的属性,从而实现动画效果
kotlin代码:正常流程

Log.i("tv_content",tv_content.width.toString()+"t"+tv_content.layoutParams.width)
    //value动画
    val valueAnimator = ValueAnimator.ofInt(tv_content.layoutParams.width,500)

    valueAnimator.duration = 2000

    valueAnimator.addUpdateListener {

        val currentValue:Int = it.animatedValue as Int
        println(currentValue)
        //获取不断改变的值给view相应的属性
        tv_content.layoutParams.width = currentValue
        tv_content.requestLayout()
    }
    valueAnimator.start()  

在xml中定义


kotlin用xml中定义的

//用xml的value动画
    var valueAnimator = AnimatorInflater.loadAnimator(this,R.animator.value_anim)
    valueAnimator.setTarget(tv_content)
    valueAnimator.start()

注:有点坑地方补间动画xml的文件夹名是anim,属性动画是animator。第二单纯这样setTarget(view)是没用的,它不知道改变什么属性。
所以用属性动画还是用java代码定义写方便
对象:ValueAnimator.ofObject()
作用:将初始值以对象的形式过渡到结束值,即通过操作对象实现动画效果
需我们自定义TypeEvaluator来告知系统如何进行从初始对象过渡到结束对象的逻辑
示例PointEvaluator.java

    // 实现TypeEvaluator接口
public class PointEvaluator implements TypeEvaluator {

    // 复写evaluate()
    // 在evaluate()里写入对象动画过渡的逻辑
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        
        // 将动画初始值startValue 和 动画结束值endValue 强制类型转换成Point对象
        Point startPoint = (Point) startValue;
        Point endPoint = (Point) endValue;
        
        // 根据fraction来计算当前动画的x和y的值
        float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
        float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
        
        // 将计算后的坐标封装到一个新的Point对象中并返回
        Point point = new Point(x, y);
        return point;
    }

}

估值器(TypeEvaluator)

作用:设置动画 如何从初始值 过渡到 结束值 的逻辑

ObjectAnimator类

是ValueAnimator的子类
是通过反射找到相应的set函数
原理:通过不断控制值的变化,再不断自动赋给对象的属性,从而实现动画效果
示例代码:

ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotationX",0,270,0);  
animator.setDuration(2000);  
animator.start();  

怎么知道哪些属性是能赋值改变形成动画的呢
1、要使用ObjectAnimator来构造对画,要操作的控件中,必须存在对应的属性的set方法
2、setter 方法的命名必须以骆驼拼写法命名,即set后每个单词首字母大写,其余字母小写,即类似于setPropertyName所对应的属性为propertyName
简单来说:先找View的set相应属性试试,有,就把属性开头大写改为小写即可
自定义ObjectAnimator属性

public class MyPointView extends View {

private PointRadius mPoint = new PointRadius(100);

public MyPointView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (mPoint != null) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(300, 300, mPoint.getRadius(), paint);
    }
}

void setPointRadius(int radius) {
    mPoint.setRadius(radius);
    invalidate();
}
}

关键setPointRadius方法
使用:

val objectAnimator = ObjectAnimator.ofInt(my_point_view,"pointRadius",0,300,200)
    objectAnimator.duration = 2000
    objectAnimator.start()

参考:https://www.jianshu.com/p/2412d00a0ce4