转载请标明出处: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 版权协议,转载请附上原文出处链接和本声明。