Android 动画之 View动画 和 帧动画

Android 动画可以分为三大类:

1>  View 动画 又称:补间 

2>  帧动画

3>  属性动画

==================【View 动画】=========================

有5种:

alpha                            渐变透明度动画效果
scale                            渐变尺寸伸缩动画效果
translate                        画面转换位置移动动画效果
rotate                           画面转移旋转动画效果
layoutAnimation           容器中的控件应用统一动画

首先,创建一个 目录 具体  步骤如下:

选:anim 目录

OK,看目录,创建一个 动画文件:set

命名为: my_animat

好了 ,我们在这个set  添加我们所需要的动画。

可以既是一个动画,也可以是多个动画的组合    

现在暂且先一个一个来,演示平移动画:

【translate】

看下动画目标View 的设置,用了一个TextView  注意:用了 宽高  是300px  像素,,,像素,,,像素

为什么用 px呢,会说到的  

<TextView
            android:id="@+id/tv_animator"
            android:layout_width="300px"
            android:layout_height="300px"
            android:background="@color/color_00a3f3"
            android:text="animator"
            android:textColor="@color/color_7e4513"
            android:gravity="center"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
    />

然后动画 translate_ani.xml      注意:fillAfter="true"  View 最后会停留在动画结束的地方

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:fillAfter="true">

    <!-- 平移  对应 TranslateAnimation
    fromXDelta,fromYDelta   起始时X,Y座标,如果是0,0,就从远处开启
    toXDelta, toYDelta     动画结束时X,Y的座标 这个坐标是相对于 原始位置(0,0)的
    看下 toXDelta  toYDelta  和TextView 宽高同是 300
    -->
    <translate
            android:fromXDelta="0"
            android:fromYDelta="0"
            android:toXDelta="300"
            android:toYDelta="300"
            android:duration="500">
    </translate>

</set>

看下原始的截图

看下Activity 中的layout:  activity_animator.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".animator.AnimatorActivity">


    <TextView
            android:id="@+id/tv_animator"
            android:layout_width="300px"
            android:layout_height="300px"
            android:background="@color/color_00a3f3"
            android:text="目标动画"
            android:textColor="@color/color_7e4513"
            android:gravity="center"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
    />


    <TextView
            android:id="@+id/tv_animator1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="@dimen/dp10"
            android:layout_marginTop="@dimen/dp100"
            android:text="平移动画"
            android:background="@color/color_999999"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_animator"/>

    <TextView
            android:id="@+id/tv_animator2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="@dimen/dp10"
            android:layout_marginTop="@dimen/dp10"
            android:text="透明动画"
            android:background="@color/color_999999"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_animator1"/>

    <TextView
            android:id="@+id/tv_animator3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="@dimen/dp10"
            android:layout_marginTop="@dimen/dp10"
            android:text="旋转动画"
            android:background="@color/color_999999"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_animator2"/>

    <TextView
            android:id="@+id/tv_animator4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="@dimen/dp10"
            android:layout_marginTop="@dimen/dp10"
            android:text="缩放动画"
            android:background="@color/color_999999"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_animator3"/>
</android.support.constraint.ConstraintLayout>

Activity的代码:

package com.leo.dicaprio.myutilapp.animator

import android.content.Context
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.animation.AnimationUtils
import android.widget.Toast
import com.leo.dicaprio.myutilapp.R
import kotlinx.android.synthetic.main.activity_animator.*
import org.jetbrains.anko.startActivity

class AnimatorActivity : AppCompatActivity() {

    companion object {
        fun launch(context: Context) {
            context.startActivity<AnimatorActivity>()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_animator)
        tv_animator.setOnClickListener { Toast.makeText(this, "被单击了", Toast.LENGTH_LONG).show() }
        tv_animator1.setOnClickListener { startAni1() }
        tv_animator2.setOnClickListener { startAni2() }
        tv_animator3.setOnClickListener { startAni3() }
        tv_animator4.setOnClickListener { startAni4() }
    }


    /**
     *      平移动画
     * */
    private fun startAni1() {
        //先加载 平移动画
        val animation = AnimationUtils.loadAnimation(this, R.anim.translate_ani)
        tv_animator.startAnimation(animation)
    }

    

}

点击一下,动画过度效果就不生产 gif了,直接上最后结果

可以看到最后的结果。这时候 手机开了开发者模式的边界布局的。

分析下:

目标的 View 宽高是 300*300 像素,现在 动画 的 toXDelta和toYDelta都是300,表示向X Y 轴正方向移动 300像素。

为什么这么肯定 toXDelta 的单位是 像素呢?  从 截图看,可以理解为:目标 View 基于源点(左上角坐标)在XY方向 移动的距离,刚好是目标的宽高。。。

by the way ! 这个时候你点蓝色区域,是不会弹Taost,只有点击原始的位置,才会弹Toast。所以View动画并没有改变

View的属性。所以目标动画View是有点击事件的话,用View动画实现不了功能的。。。这个注意下。

好了,基本上可以理解完了。其实 translate 标签,是对应  TranslateAnimation() 

可以用它,代码改成:

    /**
     *      平移动画
     * */
    private fun startAni1() {
        //先加载 平移动画
//        val animation = AnimationUtils.loadAnimation(this, R.anim.translate_ani)
//        tv_animator.startAnimation(animation)

        //单位是像素
        val translateAnimation = TranslateAnimation(0F, 300F,0F , 300F)
        translateAnimation.fillAfter = true//停留在结束地方
        translateAnimation.duration = 500//时间
        tv_animator.startAnimation(translateAnimation)
    }

同样的效果!!!

另外,通过这个TranslateAnimation 还可以监听 动画的执行状态,比如:开始,结束,重复......

比如:

    /**
     *      平移动画
     * */
    private fun startAni1() {
        //先加载 平移动画
//        val animation = AnimationUtils.loadAnimation(this, R.anim.translate_ani)
//        tv_animator.startAnimation(animation)

        val translateAnimation = TranslateAnimation(0F, 300F, 0F, 300F)
        translateAnimation.setAnimationListener(object : Animation.AnimationListener {
            override fun onAnimationRepeat(animation: Animation?) {
                //动画重复
            }

            override fun onAnimationEnd(animation: Animation?) {
                //动画结束
            }

            override fun onAnimationStart(animation: Animation?) {
                //动画开始
            }

        })
        translateAnimation.fillAfter = false//停留在结束地方
        translateAnimation.duration = 500//时间
        tv_animator.startAnimation(translateAnimation)
    }

【alpha】

透明比较简单。。。。上alpha_ani.xml 代码:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:fillAfter="true">

    <!--透明
        fromAlpha:开始时透明度 0.0- 1.0   0.0全透明  1.0不透明
        toAlpha: 结束时透明度 0.0- 1.0
        duration:动画持续时间 单位 毫秒
        fillAfter:动画结束后是否停留在结束位置,true停留
        -->
    <alpha android:fromAlpha="1"
           android:toAlpha="0"
           android:duration="10000"
    >
    </alpha>

</set>

Activity 引用

    /**
     *      透明动画
     * */
    private fun startAni2() {
        //先加载 透明动画
        val animation = AnimationUtils.loadAnimation(this, R.anim.alpha_ani)
        tv_animator.startAnimation(animation)

        /*
        *       或者
        * */
        val alphaAnimation = AlphaAnimation(1.0F, 0F)
        alphaAnimation.fillAfter = true
        alphaAnimation.duration = 5000
        tv_animator.startAnimation(alphaAnimation)
    }

【Rotate】

旋转,一般四个参数

fromDegrees    开始时的角度
toDegrees      结束时角度 ,正代表顺时针
pivotX         旋转中心轴点 X坐标 不指定 默认是View 左上角 X坐标   单位像素
pivotY         旋转中心轴点 Y坐标 不指定 默认是View 左上角 Y坐标   单位像素

不说废话,上   目标View,哈哈,同样是一个TextView,同样是 300*300 px

    <TextView
            android:id="@+id/tv_animator"
            android:layout_width="300px"
            android:layout_height="300px"
            android:background="@color/color_00a3f3"
            android:text="目标动画"
            android:textColor="@color/color_7e4513"
            android:gravity="center"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
    />

上动画文件:anim /rotate_ani.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:fillAfter="true">

    <!--
        fromDegrees  	开始时的角度
        toDegrees    	结束时角度 ,正代表顺时针
        pivotX  	    旋转中心轴点 X坐标 不指定 默认是View 左上角 X坐标
        pivotY 	        旋转中心轴点 Y坐标 不指定 默认是View 左上角 Y坐标
        -->
    <rotate android:fromDegrees="0"
            android:toDegrees="180"
            android:pivotY="150"
            android:pivotX="150"
            android:duration="5000">
    </rotate>

</set>

注意!!!   pivotX 和 pivotY  是150,刚好是TextView宽高的一半。

Activity调用:

    /**
     *      旋转动画
     * */
    private fun startAni3() {
        //先加载 旋转动画
        val animation = AnimationUtils.loadAnimation(this, R.anim.rotate_ani)
        tv_animator.startAnimation(animation)

        /*
        *       或者
        * */
        val rotateAnimation = RotateAnimation(0F, 180F, 150F, 150F)
        rotateAnimation.fillAfter = true
        rotateAnimation.duration = 5000
        tv_animator.startAnimation(rotateAnimation)
    }

测试的结果,是在TextView是 300*300 px时,

1>   pivotX和pivotY都为150,旋转中心为 TextView的中心

1>   pivotX和pivotY都为0,    旋转中心为 TextView的左上角

所以,结论是:这个旋转圆点坐标,是基于目标View左上角(0,0)坐标的。

【Scale】

缩放。。。。。。

fromXScale,fromYScale   X Y 方向  缩放起始值
toXScale, toYScale     X Y 方向  缩放结束值
pivotX ,pivotY          X Y 方向  缩放中心值   中心点也是和上面的基准规则一样

代码都不贴了,都一样的·原理。。。。。

============【多个动画一起播放或者按顺序播放】=============

多个动画同时播放

my_toget__animat.xml代码:   多个动画(透明,旋转,缩放)一起
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <alpha android:fromAlpha="1"
           android:toAlpha="0"
           android:duration="5000">
    </alpha>

    <rotate android:fromDegrees="0"
            android:toDegrees="180"
            android:pivotY="150"
            android:pivotX="150"
            android:duration="5000">
    </rotate>

    <scale android:fromXScale="0"
           android:fromYScale="0"
           android:toXScale="1"
           android:toYScale="1"
           android:pivotX="150"
           android:pivotY="150"
           android:duration="5000">
    </scale>

</set>

view层的处理:运行结果是 (透明,旋转,缩放)同事进行

private fun animationSet(){
        //加载 
        val animation = AnimationUtils.loadAnimation(this, R.anim.my_toget__animat)
        tv_animator_view.startAnimation(animation)
    }

这个可以不用 xml类型,可以用代码动态,比如:

private fun animationSet(){
        val rotateAnimation = RotateAnimation(0F, 180F, 150F, 150F)
        val alphaAnimation = AlphaAnimation(1.0F, 0F)
        val scaleAnimation = ScaleAnimation(0F, 1F, 0F, 1F, 150F, 150F)

        //多个动画 放进 AnimationSet 
        val animationSet = AnimationSet(true)
        animationSet.addAnimation(rotateAnimation)
        animationSet.addAnimation(alphaAnimation)
        animationSet.addAnimation(scaleAnimation)
        animationSet.duration = 5000
        tv_animator_view.startAnimation(animationSet)
    }

动态代码设置 AnimationSet 集合,把需要的动画放进去,效果是一样的

多个动画按顺序播放

顺序播放的思路,在xml是用延时法  通过 startOffset 指定延迟执行动画

比如:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <alpha android:fromAlpha="0"
           android:toAlpha="1"
           android:duration="3000"
           android:fillAfter="true">
    </alpha>

    <!--第2个动画延迟3000毫秒-->
    <rotate
            android:startOffset="3000"
            android:fromDegrees="0"
            android:toDegrees="180"
            android:pivotY="150"
            android:pivotX="150"
            android:duration="3000"
            android:fillAfter="true">
    </rotate>
    
</set>

加载设置用法是一样的,就不贴了

使用代码设置  顺序,监听第一个动画完成,再进行第2个动画

private fun animationOrder(){
        val rotateAnimation = RotateAnimation(0F, 180F, 150F, 150F)
        rotateAnimation.fillAfter = true
        rotateAnimation.duration = 5000
        rotateAnimation.setAnimationListener(object :Animation.AnimationListener{
            override fun onAnimationRepeat(animation: Animation?) {
                //动画重复
            }

            override fun onAnimationEnd(animation: Animation?) {
                //动画结束
                val alphaAnimation = AlphaAnimation(1.0F, 0F)
                alphaAnimation.fillAfter = true
                alphaAnimation.duration = 5000
                tv_animator_view.startAnimation(alphaAnimation)
            }

            override fun onAnimationStart(animation: Animation?) {
                //动画开始

            }

        })
        tv_animator_view.startAnimation(rotateAnimation)
    }

 

=======【layoutAnimation】==========

作用于 ViewGroup,为一个ViewGroup指定一个动画,他的所有子元素,出厂时都会具有这个动画效果

比如在RecycleView指定时,每个item都会有

比如在ViewGroup指定时,每个子View都会有

首先,创建 layoutAnimation :  anim/layout_animation.xml

<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
                 android:delay="1"
                 android:animationOrder="random"
                 android:animation="@anim/alpha_ani">

    <!--
            delay: 每个子View时间间隔 如果有多个子view的话,如果设定的动画周期是 duration = 1000毫秒,
                    如果 delay=0.1,则下一个子View 会在100(duration * delay)毫秒执行动画

            animationOrder:子view经常顺序,normal是最前面的先进来,
                                            reverse是最后的先进来
                                            random 正看英文就知道。。。。哈哈

            顺序就是View.getChildView(index)  里面的这个index
    -->
</layoutAnimation>

创建 layoutAnimation :  anim/ alpha.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:fillAfter="true">

    <!--透明
        fromAlpha:开始时透明度 0.0- 1.0   0.0全透明  1.0不透明
        toAlpha: 结束时透明度 0.0- 1.0
        duration:动画持续时间 单位 毫秒
        fillAfter:动画结束后是否停留在结束位置,true停留
        -->
    <alpha android:fromAlpha="0"
           android:toAlpha="1"
           android:duration="5000"
    >
    </alpha>

</set>

父布局 layout文件:

<android.support.constraint.ConstraintLayout
            android:id="@+id/tv_animator_contain"
            android:layout_width="0dp"
            android:layout_height="300px"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layoutAnimation="@anim/layout_animation"
            android:visibility="invisible"
    >

        <TextView
                android:id="@+id/tv_animator_view"
                android:layout_width="300px"
                android:layout_height="300px"
                android:background="@color/color_00a3f3"
                android:text="目标动画1"
                android:textColor="@color/color_7e4513"
                android:gravity="center"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toLeftOf="@id/tv_animator_view2"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintHorizontal_chainStyle="spread"/>


        <TextView
                android:id="@+id/tv_animator_view2"
                android:layout_width="300px"
                android:layout_height="300px"
                android:background="@color/color_00a3f3"
                android:text="目标动画1"
                android:textColor="@color/color_7e4513"
                android:gravity="center"
                app:layout_constraintLeft_toRightOf="@id/tv_animator_view"
                app:layout_constraintRight_toLeftOf="@id/tv_animator_view3"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintHorizontal_chainStyle="spread"/>

        <TextView
                android:id="@+id/tv_animator_view3"
                android:layout_width="300px"
                android:layout_height="300px"
                android:background="@color/color_00a3f3"
                android:text="目标动画1"
                android:textColor="@color/color_7e4513"
                android:gravity="center"
                app:layout_constraintLeft_toRightOf="@id/tv_animator_view2"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintHorizontal_chainStyle="spread"/>

    </android.support.constraint.ConstraintLayout>

注意!!   父布局的 visibility = invisible的,当父布局变成:visible后,三个子view 就会显示出来

当然,出现的形式是以动画的形式的。。。

在代码里调用:

private fun layoutAnimation() {
        tv_animator_contain.visibility = View.VISIBLE
    }

直接把父布局显示出来即可,所有子View的出现,都会有动画效果。。。。

你也可以在父布局  layout不设置,通过  LayoutAnimationController

直接上代码:

private fun layoutAnimation() {
        val loadAnimation = AnimationUtils.loadAnimation(this, R.anim.alpha_ani)
        val animationController = LayoutAnimationController(loadAnimation)
        animationController.delay = 0.1F
        animationController.order = LayoutAnimationController.ORDER_NORMAL
        tv_animator_contain.layoutAnimation = animationController
        tv_animator_contain.visibility = View.VISIBLE
    }

注意!!!!!!!

代码引用的是 alpha_ani.xml,直接引用动画文件,并不是引用  layoutAnimation 

==================【帧动画】=========================

首先说下,帧动画,是类似于视频播放那样,一秒显示26张图片,肉眼看到的效果就是视频了。

帧动画,会一下子把所有图片加载内存,然后每隔设定duration,就展示下一张图片。。。

不推荐!!!因为非常消耗内存!!!!!!

首先,创建帧动画,是在 drawable目录下的  。。。

然后选择  animation-list

然后 

在控件里使用:

    /**
     *      帧动画
     * */
    private fun startFrameAni() {
        //先设置
        tv_animator_view.setBackgroundResource(R.drawable.frame_animation)
        //再获取  强转成 AnimationDrawable
        val background = tv_animator_view.background as AnimationDrawable
        //开启动画
        background.start()
    }

再说一次,这个如果 图片过多的话,容易导致OOM

如果非得 使用帧动画,可以  把 需要的 drawable的ID全部放进一个List里面

然后自己写一个定时器(比如用Handler),隔多少秒,通过ID获取drawable  展示一个图片,这样避免一下子把所有图片加载到内存。。。。

好了。。。。。。这边就到这里

下篇写:属性动画

上面代码亲测没问题,,,,有问题请留言指正,,,谢谢!!!!

猜你喜欢

转载自blog.csdn.net/Leo_Liang_jie/article/details/90752748