Android 属性动画常见效果收集

  • Post author:
  • Post category:其他


转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/128948795

本文出自

【赵彦军的博客】

属性动画往期博客


Android属性动画 AnimatorSet



Android属性动画 ObjectAnimator



1、呼吸动画

在这里插入图片描述

//呼吸动画
fun View.breathAnim(): AnimatorSet {
    val anim1 = ObjectAnimator.ofFloat(this, "scaleX", 1.0f, 0.8f)
    val anim2 = ObjectAnimator.ofFloat(this, "scaleY", 1.0f, 0.8f)
    val sets = AnimatorSet()
    sets.playTogether(anim1, anim2)
    sets.interpolator = LinearInterpolator()
    sets.duration = 800
    anim1.repeatMode = ValueAnimator.REVERSE
    anim2.repeatMode = ValueAnimator.REVERSE
    anim1.repeatCount = ValueAnimator.INFINITE
    anim2.repeatCount = ValueAnimator.INFINITE
    sets.start()
    return sets
}

使用:

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    private var breathAnim: AnimatorSet? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        //呼吸动画
        breathAnim = binding.breath.breathAnim()
    }

    override fun onDestroy() {
        super.onDestroy()
        breathAnim?.cancel()
    }
}



2、摇晃动画

在这里插入图片描述

//摇晃动画
fun View.shake(): AnimatorSet {
    val animatorX = ObjectAnimator.ofFloat(this, "scaleX", 1f, 1.08f, 1f)
    val animatorY = ObjectAnimator.ofFloat(this, "scaleY", 1f, 1.08f, 1f)
    val rotation = ObjectAnimator.ofFloat(this, "rotation", 0f, 15f, 0f, -15f, 0f, 12f, 0f, -12f, 0f, 9f, 0f, -9f, 0f, 6f, 0f, -6f, 0f, 3f, 0f, -3f, 0f)
    val animatorSet = AnimatorSet()
    animatorSet.playTogether(animatorX, animatorY, rotation)
    animatorSet.duration = 600
    animatorSet.interpolator = LinearInterpolator()
    animatorSet.startDelay = 1200
    var cancel = false
    animatorSet.addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator) {
            super.onAnimationEnd(animation)
            if (!cancel) {
                animatorSet.start()
            }
        }

        override fun onAnimationCancel(animation: Animator?) {
            super.onAnimationCancel(animation)
            cancel = true
        }
    })
    animatorSet.start()
    return animatorSet
}

使用:

package com.example.myapplication

import android.animation.AnimatorSet
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.myapplication.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private var shakeAnim: AnimatorSet? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        //抖动动画
        shakeAnim = binding.shake.shake()
    }

    override fun onDestroy() {
        super.onDestroy()
        shakeAnim?.cancel()
    }
}



3、两个view位移

在这里插入图片描述

布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="true"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/view1"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_marginHorizontal="20dp"
        android:layout_marginTop="20dp"
        android:background="#f00"
        android:gravity="center"
        android:textColor="@color/white"
        android:translationZ="10dp"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/view2"
        android:layout_width="100dp"
        android:layout_height="60dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

动画核心思路

在这里插入图片描述

1、分别计算view1、view2 中心点的 x , y 坐标,计算方式使用

view.getLocationInWindow(location)


2、计算view1 到 view2 的 x、y 轴的距离

3、最后加上缩放,透明度动画即可完美实现


4、最后一点也是最重要的,view1 在平移过程中会被 view2 遮挡,需要在布局中抬高 view1 的层级,使用

android:translationZ="10dp"

使用:

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.shake.setOnClickListener {
            startCloseAnimation(binding.view1, binding.view2)
        }
    }

    private fun startCloseAnimation(v1: View, v2: View) {
        val offset = getOffSet(v1, v2)
        val transX = offset[0]
        val transY = offset[1]

        val scaleX = ObjectAnimator.ofFloat(v1, "scaleX", 1f, 0f)
        val scaleY = ObjectAnimator.ofFloat(v1, "scaleY", 1f, 0f)
        val alpha = ObjectAnimator.ofFloat(v1, "alpha", 1f, 0.3f)
        val translationX = ObjectAnimator.ofFloat(v1, "translationX", transX.toFloat())
        val translationY = ObjectAnimator.ofFloat(v1, "translationY", transY.toFloat())

        val animatorSet = AnimatorSet()
        animatorSet.let {
            it.playTogether(scaleX, scaleY, translationX, translationY, alpha)
            it.duration = 800
            it.interpolator = AccelerateDecelerateInterpolator()
            it.addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    super.onAnimationEnd(animation)
                    v1.visibility = View.GONE
                }
            })
            it.start()
        }
    }

    //计算两个view,x/y坐标的相对距离
    private fun getOffSet(v1: View, v2: View): IntArray {
        //获取v1的坐标
        val location1 = IntArray(2)
        v1.getLocationInWindow(location1)
        //修正v1的位置为中心位置
        location1[0] = location1[0] + v1.width / 2
        location1[1] = location1[1] + v1.height / 2

        //获取v2的坐标
        val location2 = IntArray(2)
        v2.getLocationInWindow(location2)
        //修正v2的位置为中心位置
        location2[0] = location2[0] + v2.width / 2
        location2[1] = location2[1] + v2.height / 2

        val result = IntArray(2)
        result[0] = location2[0] - location1[0]
        result[1] = location2[1] - location1[1]
        return result
    }
}



4、两个view位移加循环

在这里插入图片描述

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            //这一句很关键,提高view1的层级。否则会被其他view遮挡
            binding.view1.z = 100f
        }
        binding.view2.setOnClickListener {
            startCloseAnimation(binding.view1, binding.view2)
        }
    }

    private fun startCloseAnimation(v1: View, v2: View) {
        val offset = getOffSet(v1, v2)
        val transX = offset[0]
        val transY = offset[1]

        val scaleX = ObjectAnimator.ofFloat(v1, "scaleX", 1f, 0.1f)
        val scaleY = ObjectAnimator.ofFloat(v1, "scaleY", 1f, 0.1f)
        val alpha = ObjectAnimator.ofFloat(v1, "alpha", 1f, 0.3f)
        val translationX = ObjectAnimator.ofFloat(v1, "translationX", transX.toFloat())
        val translationY = ObjectAnimator.ofFloat(v1, "translationY", transY.toFloat())

        scaleX.repeatCount = ObjectAnimator.INFINITE
        scaleX.repeatMode = ObjectAnimator.REVERSE

        scaleY.repeatCount = ObjectAnimator.INFINITE
        scaleY.repeatMode = ObjectAnimator.REVERSE

        alpha.repeatCount = ObjectAnimator.INFINITE
        alpha.repeatMode = ObjectAnimator.REVERSE

        translationX.repeatCount = ObjectAnimator.INFINITE
        translationX.repeatMode = ObjectAnimator.REVERSE

        translationY.repeatCount = ObjectAnimator.INFINITE
        translationY.repeatMode = ObjectAnimator.REVERSE

        val animatorSet = AnimatorSet()
        animatorSet.let {
            it.playTogether(scaleX, scaleY, translationX, translationY, alpha)
            it.duration = 1000
            it.interpolator = AccelerateDecelerateInterpolator()
            it.start()
        }
    }

    //计算两个view,x/y坐标的相对距离
    private fun getOffSet(v1: View, v2: View): IntArray {
        //获取v1的坐标
        val location1 = IntArray(2)
        v1.getLocationInWindow(location1)
        //修正v1的位置为中心位置
        location1[0] = location1[0] + v1.width / 2
        location1[1] = location1[1] + v1.height / 2

        //获取v2的坐标
        val location2 = IntArray(2)
        v2.getLocationInWindow(location2)
        //修正v2的位置为中心位置
        location2[0] = location2[0] + v2.width / 2
        location2[1] = location2[1] + v2.height / 2

        val result = IntArray(2)
        result[0] = location2[0] - location1[0]
        result[1] = location2[1] - location1[1]
        return result
    }
}



版权声明:本文为zhaoyanjun6原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。