Android动画
ViewAnimation
- 属性
- 插值器
Interpolator 是个接口,系统已经实现了几种插值器,在使用的时候,以LinearInterpolator为例
在Xml中:
android:interpolator="@android:anim/linear_interpolator"
在代码中(Kotlin):
interpolator = LinearInterpolator()
- 插值器
-
部分属性中不同的值类型
Java xml description Animation.ABSOLUTE 数值 10、50等数值,表示据坐标原点的距离 Animation.RELATIVE_TO_SELF 数值% xml:50%, java:0.5, 再传入类型参数Animation.RELATIVE_TO_SELF,表示据坐标原点的距离为当前控件当前方向上50%的长度 Animation.RELATIVE_TO_PARENT 数值%p xml:50%p, java:0.5, 再传入类型参数Animation.RELATIVE_TO_PARENT,表示据坐标原点的距离为父控件当前方向上50%的长度
-
Common: 共有的属性
name description duration 动画的执行时间 , ms fillAfter 保持动画执行结束后的状态 , boolean fillBefore 恢复到动画执行前的状态 , boolean interpolator 插值器,动画执行的过程中的速率变化, 比如先快后慢、匀速等 repeatMode 动画再次执行的模式: reverse, 逆向执行;restart,从头开始 repeatCount 动画执行的次数: infinite(-1)表示无限循环,要在具体的Animation中设置才有效 -
Alpha
name description toAlpha 动画结束时的透明度, 0.0~1.0 fromAlpha 动画开始时的透明度, 0.0~1.0
-
Rotate
name description pivotX 作为旋转中心的X的值,值有三种类型 pivotY 作为旋转中心的Y的值,值有三种类型 fromDegrees 动画开始时相较于原始位置所处的角度 toDegrees 动画结束时相较于原始位置所处的角度, 值为负数时逆时针旋转, 值为正数时顺时针旋转 -
Scale
name description pivotX 作为动画起始点的X的值,值有三种类型 pivotY 作为动画起始点Y的值,值有三种类型 fromXScale 动画开始时X方向相较于原始大小的缩放比例 toXScale 动画结束时X方向相较于原始大小的缩放比例 fromYScale 动画开始时Y方向相较于原始大小的缩放比例 toYScale 动画结束时Y方向相较于原始大小的缩放比例 -
Translate
name description fromXDelta 动画开始时相较于原始位置X平移的距离,值有三种类型 toXDelta 动画结束时相较于原始位置X平移的距离,值有三种类型 fromYDelta 动画开始时相较于原始位置Y平移的距离,值有三种类型 toYDelta 动画结束时相较于原始位置X平移的距离,值有三种类型 -
Set
name description shareInterpolator Set动画集合中的动画是否共享插值器, boolean
-
应用
-
LayoutAnimation
name description delay 动画延迟播放时间: 用百分号表示基于Item动画的duration,0%,表示动画同步执行; 100% ,表示前一个动画执行完后才执行下一个 animationOrder 子View动画播放顺序:normal,item的顺序;reverse,item的倒序;random,随机; animation 动画 interpolator 插值器 layout_animation.xml <?xml version="1.0" encoding="utf-8"?> <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" android:animation="@anim/layout_set_anim" android:delay="15%" android:animationOrder="normal"> </layoutAnimation> layout_set_anim.xml <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" > <translate android:toXDelta="0%" android:toYDelta="0%" android:fromXDelta="0%" android:fromYDelta="120%"/> <alpha android:toAlpha="1.0" android:fromAlpha="0.0"/> <!-- <scale android:pivotX="50%" android:pivotY="50%" android:fromXScale="0.2" android:fromYScale="0.2" android:toXScale="1.0" android:toYScale="1.0"/>--> </set>
-
Activity之间的切换
- 必须在Activity 的 startActivity 系列方法或finish之后调用
Activity.overridePendingTransition(enterAnim, exitAnim)
- 对所有的Activity都有效
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="android:windowAnimationStyle">@style/activityAnimStyle</item> </style> <style name="activityAnimStyle" parent="@android:style/Animation.Activity"> <item name="android:activityOpenEnterAnimation">@anim/act_next_in</item> <item name="android:activityOpenExitAnimation">@anim/act_next_current_out</item> <item name="android:activityCloseEnterAnimation">@anim/act_last_in</item> <item name="android:activityCloseExitAnimation">@anim/act_last_current_out</item> </style>
- 必须在Activity 的 startActivity 系列方法或finish之后调用
Fragment之间的切换
enterAnim: 指Fragment进入视图时的动画,exitAnim:退出视图时的动画
FragmentActivity.getSupportFragmentManager() .beginTransaction() {.setCustomAnimations(enterAnim,exitExit) | .setCustomAnimations(enterAnim,exitAnim,popEnterAnim,popExitAnim)} { .add().show() | .replace()} .commit()
-
DrawableAnimation(FrameAnimation)
//开始动画
(testV.drawable as? AnimationDrawable)?.start()
-
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.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:layoutAnimation="@anim/layout_animation" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.appcompat.widget.Toolbar android:id="@+id/titleToolBar" android:background="@color/colorPrimary" android:layout_width="match_parent" android:minHeight="?android:attr/actionBarSize" app:layout_constraintTop_toTopOf="parent" android:layout_height="wrap_content"/> <com.google.android.material.tabs.TabLayout android:layout_width="match_parent" app:layout_constraintTop_toBottomOf="@+id/ titleToolBar" app:layout_constraintLeft_toLeftOf="parent" android:id="@+id/topTab" app:tabMode="scrollable" android:layout_height="?android:actionBarSize"/> <ImageView android:id="@+id/testV" app:layout_constraintTop_toBottomOf="@+id/topTab" android:layout_width="100dp" android:layout_height="100dp" android:background="#689F38" android:src="@drawable/drawable_animation" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintLeft_toLeftOf="parent" /> <ToggleButton android:id="@+id/toggleB" android:textOff="use XML" android:textOn="use JAVA" app:layout_constraintBottom_toBottomOf="parent" android:layout_width="match_parent" android:layout_height="wrap_content"/> </androidx.constraintlayout.widget.ConstraintLayout>
-
AnimationDrawable
<?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/img_0" android:duration="150"/> <item android:drawable="@drawable/img_1" android:duration="150"/> <item android:drawable="@drawable/img_2" android:duration="150"/> <item android:drawable="@drawable/img_3" android:duration="150"/> <item android:drawable="@drawable/img_4" android:duration="150"/> <item android:drawable="@drawable/img_5" android:duration="150"/> <item android:drawable="@drawable/img_6" android:duration="150"/> </animation-list>
PropertyAnimation
- 属性动画不仅仅局限与View,它的作用在于改变目标对象的属性,目标对象不仅是View
- View动画只是改变了View的视觉效果,并没有改变View的属性;属性动画则改变了属性。
如一个用了View动画位移到新位置的按钮,它的点击响应位置还是在原来的位置,本身的属性并没有改变。
属性动画的点击响应位置则会在新位置(点击响应的位置坐标与组件的位置坐标不是同一个)。
-
ValueAnimator
- 普通用法(ofInt(), ofFloat()...)
- animator_int.xml
<?xml version="1.0" encoding="utf-8"?> <animator xmlns:android="http://schemas.android.com/apk/res/android" android:repeatMode="restart" android:repeatCount="infinite" android:duration="3000" android:interpolator="@android:anim/linear_interpolator" android:valueType="colorType" android:valueFrom="@color/colorFrom" android:valueTo="@color/colorTo" />
- code
private val valueAnimator: ValueAnimator get() = if (useCode) ValueAnimator.ofInt(Color.parseColor("#008577"), Color.parseColor("#D81B60")).apply { repeatCount = ValueAnimator.INFINITE repeatMode = ValueAnimator.RESTART duration = 3000 interpolator = AccelerateDecelerateInterpolator() addUpdateListener { //在回调中改变目标对象相应的属性值,达到动画效果 target.background = ColorDrawable(it.animatedValue as Int) } } else (AnimatorInflater.loadAnimator(this, R.animator.animator_int) as ValueAnimator).apply { addUpdateListener { target.background = ColorDrawable(it.animatedValue as Int) } } //开始动画 valueAnimator.start() //移除更新监听器来结束动画 valueAnimator.removeUpdateListener(listener)
- 自定义(ofObject())
//自定义的Evaluator class ScaleShapeEvaluator : TypeEvaluator<ScaleShape> { override fun evaluate(fraction: Float, startValue: ScaleShape, endValue: ScaleShape): ScaleShape { val x = startValue.x + fraction * (endValue.x - startValue.x) val y = startValue.y + fraction * (endValue.y - startValue.y) return ScaleShape(x, y) } } ValueAnimator.ofObject(ScaleShapeEvaluator(), ScaleShape(0.5F, 0.2F), ScaleShape(2.0F, 3.0F)).apply { repeatCount = ValueAnimator.INFINITE repeatMode = ValueAnimator.RESTART duration = 2000 interpolator = AccelerateDecelerateInterpolator() addUpdateListener(listener) start() }
- ObjectAnimator
- 普通用法
-
animator_object_rotate.xml
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:repeatMode="restart" android:valueFrom="0" android:valueTo="360" android:interpolator="@android:anim/linear_interpolator" android:propertyName="rotationX" android:duration="1000" android:repeatCount="infinite" android:valueType="floatType" />
val animator = ObjectAnimator.ofFloat(target, "rotationY", 0f, -360F).apply { repeatCount = ObjectAnimator.INFINITE repeatMode = ObjectAnimator.RESTART duration = 2000 interpolator = LinearInterpolator() } or val animator = AnimatorInflater.loadAnimator(this, R.animator.animator_object_rotate) .apply { setTarget(target) } animator.start() //设置目标为null 来取消动画 animator.setTarget(null)
- 自定义
对于一些没有的属性,可以通过装饰器模式来包装一下
//ShapeWrapper
class ShapeWrapper(private val target: View) {
fun setShape(shape: ScaleShape) {
target.layoutParams.apply {
width = shape.x.toInt()
height = shape.y.toInt()
}
target.requestLayout()
}
fun getShape(): ScaleShape {
return ScaleShape(target.width.toFloat(), target.height.toFloat())
}
}
//ScaleShape
data class ScaleShape(var x: Float, var y: Float)
//Evaluator
class ScaleShapeEvaluator : TypeEvaluator<ScaleShape> {
override fun evaluate(fraction: Float, startValue: ScaleShape, endValue: ScaleShape): ScaleShape {
val x = startValue.x + fraction * (endValue.x - startValue.x)
val y = startValue.y + fraction * (endValue.y - startValue.y)
return ScaleShape(x, y)
}
}
//使用
val animator = ObjectAnimator.ofObject(
ShapeWrapper(target),
"shape",
ScaleShapeEvaluator(),
ScaleShape(100F, 100F),
ScaleShape(500F, 500F)
).apply {
repeatCount = ObjectAnimator.INFINITE
repeatMode = ObjectAnimator.RESTART
duration = 2000
interpolator = LinearInterpolator()
}