Android 动画总结

Android 动画总结

  • 逐帧动画
  • 补间动画
  • 属性动画

Android开发中,动画特效是经常遇到的,接下来对这块知识点做一下总结。

一.逐帧动画

逐帧(Frame)动画,就是动画过程的每张静态的图片都收集起来,然后由Android来控制依次展示这些静态图片,再利用人眼的“视觉残留”原理,给用户呈现动画的错觉。(逐帧动画的原理和放电影的原理一致)

  1. 定义逐帧动画
    在< animation-list… />元素中使用< item…/ >子元素定义动画的全部帧即可
<?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_frame0" android:duration="60"/>
    <item android:drawable="@drawable/ic_frame1" android:duration="60"/>
    <item android:drawable="@drawable/ic_frame2" android:duration="60"/>
    <item android:drawable="@drawable/ic_frame3" android:duration="60"/>
    <!--下面忽略多个item-->
</animation-list>

上面的xml文件定义了一个逐帧动画资源,android:oneshot为false指的可以循环播放动画

  1. 使用逐帧动画
class MainAnimActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main_anim)
        val framImageView = findViewById<ImageView>(R.id.fram_imge)
        val playAniBtn = findViewById<Button>(R.id.play_btn)
        val stopAniBtn = findViewById<Button>(R.id.stop_btn)
        val anim = framImageView.background

        playAniBtn.setOnClickListener{v ->
            if (anim is AnimationDrawable) {
                anim.start()
            }
        }

        stopAniBtn.setOnClickListener {v ->  
            if (anim is AnimationDrawable) {
                anim.stop()
            }
        }
    }
}

AnimationDrawble(An object used to create frame-by-frame animations)代表的动画默认不播放,提供如下方法来开始、结束动画
start() 开始播放动画
end() 结束动画

  1. 代码创建逐帧动画
val animationDrawable = AnimationDrawable()
    animationDrawable.addFrame(resources.getDrawable(R.drawable.ic_frame0), 60)
    animationDrawable.addFrame(resources.getDrawable(R.drawable.ic_frame1), 60)
    animationDrawable.addFrame(resources.getDrawable(R.drawable.ic_frame2), 60)
    animationDrawable.addFrame(resources.getDrawable(R.drawable.ic_frame3), 60)
    animationDrawable.isOneShot = false
    
	framImageView.background = animationDrawable
    playAniBtn.setOnClickListener{v ->
        animationDrawable.start()
    }
    stopAniBtn.setOnClickListener {v ->
        animationDrawable.stop()
    }

二.补间动画

补间动画,就是指开发者只需指定动画开始、动画结束等“关键帧”,而动画变化的“中间帧”由系统计算并补齐。

Android使用Animation代表抽象的动画类
包含如下几个子类:AlphaAnimation(透明度动画)、ScaleAnimation(缩放动画)、TranslateAnimation(位移动画)、RotationAnimation(旋转动画)

AnimationSet : 组合多个补间动画时使用

  • 使用补间动画

res/anim/tween_anim.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/linear_interpolator">
    <!--缩放 -->
    <scale
        android:duration="3000"
        android:fillAfter="true"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="0.1"
        android:toYScale="0.1" />
    <!--透明度-->
    <alpha
        android:duration="3000"
        android:fromAlpha="1"
        android:toAlpha="0.05" />
    <!-- 旋转 -->
    <rotate
        android:duration="3000"
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="180" />
    <!--平移-->
    <translate
        android:duration="3000"
        android:fromXDelta="100"
        android:fromYDelta="100"
        android:toXDelta="200"
        android:toYDelta="100" />
</set>
//通过AnimationUtils得到代表动画的Animation之后,就可以调用view的startAnimation(Animation anim)方法开始对该view执行动画了
val loadAnimation = AnimationUtils.loadAnimation(this, R.anim.tween_anim)
        playAniBtn.setOnClickListener{v ->
            framImageView.startAnimation(loadAnimation)
        }
  • 代码创建补间动画
//透明度动画
val alphaAnimation = AlphaAnimation(1f, 0f)
alphaAnimation.duration = 3000

//旋转动画
/*
*  创建一个旋转动画对象
*  入参列表含义如下:
*  1.fromDegrees:从哪个角度开始旋转
*  2.toDegrees:旋转到哪个角度结束
*  3.pivotXType:旋转所围绕的圆心的x轴坐标的类型,有ABSOLUT绝对坐标、RELATIVE_TO_SELF相对于自身坐标、RELATIVE_TO_PARENT相对于父控件的坐标
*  4.pivotXValue:旋转所围绕的圆心的x轴坐标,0.5f表明是以自身这个控件的一半长度为x轴
*  5.pivotYType:y轴坐标的类型
*  6.pivotYValue:y轴坐标
*/
val rotateAnimation = RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f,
        Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.duration = 3000

//缩放动画
/*
*  创建一个缩放效果的动画
*  入参列表含义如下:
*  fromX:x轴的初始值
*  toX:x轴缩放后的值
*  fromY:y轴的初始值
*  toY:y轴缩放后的值
*  pivotXType:x轴坐标的类型,有ABSOLUT绝对坐标、RELATIVE_TO_SELF相对于自身坐标、RELATIVE_TO_PARENT相对于父控件的坐标
*  pivotXValue:x轴的值,0.5f表明是以自身这个控件的一半长度为x轴
*  pivotYType:y轴坐标的类型
*  pivotYValue:轴的值,0.5f表明是以自身这个控件的一半长度为y轴
*/
val scaleAnimation = ScaleAnimation(1f, 0.8f, 1f, 0.8f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
scaleAnimation.duration = 3000

//平移动画
/*
 *  创建一个移动动画效果
 *  入参的含义如下:
 *  fromXType:移动前的x轴坐标的类型
 *  fromXValue:移动前的x轴的坐标
 *  toXType:移动后的x轴的坐标的类型
 *  toXValue:移动后的x轴的坐标
 *  fromYType:移动前的y轴的坐标的类型
 *  fromYValue:移动前的y轴的坐标
 *  toYType:移动后的y轴的坐标的类型
 *  toYValue:移动后的y轴的坐标
 */
val translateAnimation = TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0.2f,
        Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0.2f)
translateAnimation.duration = 3000

playAniBtn.setOnClickListener { v ->
    val animationSet = AnimationSet(true)
    animationSet.addAnimation(alphaAnimation)
    animationSet.addAnimation(rotateAnimation)
    animationSet.addAnimation(scaleAnimation)
    animationSet.addAnimation(translateAnimation)
    //播放组合补间动画
    framImageView.startAnimation(animationSet)
    //播放单一补间动画
    //framImageView.startAnimation(alphaAnimation)
}
  • 补间动画只是去改变view的展示效果,不会真正改变view的属性
val translateAnimation = TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 1.5f, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f)
        translateAnimation.fillAfter = true
        
        //延迟2秒执行平移动画 
        //对比平移前点击按钮和平移后点击按钮的现象
        mHandler.postDelayed(Runnable { mTransBtn?.startAnimation(translateAnimation) }, 2000)
        
		mTransBtn?.setOnClickListener {
            Toast.makeText(this@MainActivity, getString(R.string.click_trans_btn), Toast.LENGTH_SHORT).show() 
        }

平移后的按钮无法触发我们注册的点击事件,而此时平移前按钮的位置是可以触发该点击事件的,因为实际上这个按钮还是停留在平移前的位置,只不过补间动画将这个按钮绘制到了平移后的位置

注:属性动画对view属性进行动画,解决了该问题

三.属性动画

属性动画,可以定义任何属性的变化,从某种角度来看,属性动画是增强版的补间动画
1.补间动画只能定义在透明度、旋转、缩放、位移,这4个方面的变化,但属性动画可以定义任何属性的变化
2.补间动画只能对ui组件执行动画,但属性动画几乎可以对任何对象执行动画(不管是否显示在屏幕上)

Animator 提供了创建属性动画的基类,基本不会直接使用该类,通常只用于被继承并重写它的相关方法

ValueAnimator:主要负责计算各帧的属性值,更新对象的相关属性值时使用
ObjectAnimator:是ValueAnimator的子类,对指定对象的属性执行动画时使用
AnimatorSet:组合多个属性动画时使用

  • 使用ObjectAnimator属性动画
		//透明度属性动画
        mAlphaAnimator = ObjectAnimator.ofFloat(mMyLove, "alpha", 0.2f, 1f)
        //mAlphaAnimator?.start()  //使用alpha属性动画
        //缩放属性动画
        mScaleAnimatorX = ObjectAnimator.ofFloat(mMyLove, "scaleX", 1f, 1.1f)
        mScaleAnimatorY = ObjectAnimator.ofFloat(mMyLove, "scaleY", 1f, 1.1f)
        //组合属性动画
        mAnimatorSet = AnimatorSet()
        mAnimatorSet?.duration = 3000
        mAnimatorSet?.playTogether(mScaleAnimatorX, mScaleAnimatorY, mAlphaAnimator)
        mAnimatorSet?.start()  //使用组合属性动画
  • 使用ValueAnimator属性动画
//ValueAnimator属性动画
        mProgressAnimator = ValueAnimator.ofInt(0, 100)
        mProgressAnimator?.setDuration(3000)
        mProgressAnimator?.addUpdateListener(AnimatorUpdateListener { animation ->
            Log.i(TAG, "onAnimationUpdate :: " + animation.animatedValue)
            mProgressBar?.progress = (animation.animatedValue as Int)
        })
        mProgressAnimator?.addListener(object : Animator.AnimatorListener {
            override fun onAnimationStart(animation: Animator) {
                Toast.makeText(this@MainActivity, "动画开始", Toast.LENGTH_SHORT).show()
            }

            override fun onAnimationEnd(animation: Animator) {
                Toast.makeText(this@MainActivity, "动画结束", Toast.LENGTH_SHORT).show()
            }

            override fun onAnimationCancel(animation: Animator) {
                Toast.makeText(this@MainActivity, "动画取消", Toast.LENGTH_SHORT).show()
            }

            override fun onAnimationRepeat(animation: Animator) {
                Toast.makeText(this@MainActivity, "动画重复", Toast.LENGTH_SHORT).show()
            }
        })

		//色值ValueAnimator属性动画
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mColorAnimator = ValueAnimator.ofArgb(-0x1, -0x10000, -0xffff01, -0xff0100)
        }
        mColorAnimator?.duration = 3000
        mColorAnimator?.addUpdateListener { animation -> mStartBtn?.setBackgroundColor((animation.animatedValue as Int)) }
  • xml文件定义属性动画

res/animator/animator_set.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="3000"
        android:propertyName="alpha"
        android:valueFrom="0.2"
        android:valueTo="1.0"
        android:valueType="floatType">
    </objectAnimator>

    <objectAnimator
        android:duration="3000"
        android:propertyName="scaleX"
        android:valueFrom="1.0"
        android:valueTo="1.1"
        android:valueType="floatType">
    </objectAnimator>

    <objectAnimator
        android:duration="3000"
        android:propertyName="scaleY"
        android:valueFrom="1.0"
        android:valueTo="1.1"
        android:valueType="floatType">
    </objectAnimator>
</set>

res/animator/animator_alpha.xml

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:propertyName="alpha"
    android:valueFrom="0.2"
    android:valueTo="1.0"
    android:valueType="floatType">
</objectAnimator>

代码中使用在xml中定义的属性动画

//加载xml中属性动画
        val animatorset = AnimatorInflater.loadAnimator(this, R.animator.animator_set) as AnimatorSet
        val animator = AnimatorInflater.loadAnimator(this, R.animator.animator_alpha)

        //使用组合属性动画
        animatorset.setTarget(mMyLove)
        animatorset.start()

        //使用属性动画
        animator.setTarget(mMyLove)
        animator.start()

在使用过程中,可以选择合适的动画进行动画效果实现

传送门:https://github.com/xiaoai-summer/myKotlinDemo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值