android 动画动态消失,Android属性动画——没有什么动画是一个AnimSet不能解决的...
沒有什么動畫是一個AnimSet不能解決的,如果有那就再來一個AnimSet。項目是Kotlin寫的也不復雜,不懂Kotlin剛好可以學學。
系統動畫那些坑
現在應該沒人使用View動畫了吧,還再使用怕是學的假Android了,所以這邊講的是屬性動畫。
先說說ValueAnimator
不提供動畫方向判斷方法,這點一直很困惑,查看源碼發現有一個很明顯的字段mReversing,跟蹤下方法發現shouldPlayBackward()方法,興高采烈的去調用時才發現是私有方法😓。無奈之下只能用反射調用,結果在5.0系統突然崩了,去查5.0源碼發現居然是不同字段mPlayingBackwards,再去查5.1的源碼發現居然兩個都存在,就不能專一點嗎,8.0更新又不能獲取了解決方法待定,感覺不能再玩反射太不靠譜了。
revese() 方向是個問題,字面上理解是反轉的意思,不就是到著播放嘛,但是當你倒著播放時再掉reverse()又給正向播放了,然后還不告訴你方向不帶這么玩的啊😢。
播放時間,還是不給方向判斷的坑😢。
再說說AnimatorSet
該怎么說遠看像越野車近看才發現是拖拉機,能存在并被使用簡直是個奇跡。
reverse()是隱藏方法也就是說不能用了,忍了看在能播放那么多動畫的面子上。
播放存在問題,當一個動畫沒結束再次start()會發現前面播放過的動畫居然不播放了,這還怎么玩啊。
看似播放方式多樣但并沒有什么卵用,with,before,after包含了多種播放方式,但是實際使用時基本都是一個動畫沒結束就開始下一個動畫,這中理想的動畫播放方式根本用不到。
實現效果
動畫要求:總動畫時間3s,紅塊直接開始時間3s,綠塊1s后開始時間2s,藍塊2s后開始時間1s,動畫執行過程中可以隨時來回切換,可以暫停、繼續、結束和取消,可以想象下使用系統提供的方式要怎么實現。
Kapture 2017-07-16 at 13.35.24.gif
ValueAnim
看看怎么填ValueAnimator的坑,獲取播放方向問題,通過反射獲取播放方向,利用Kotlin擴展方法的特性,對ValueAnimator進行擴展,但是mReversing的值只有再動畫播放時才有效果,動畫結束就被初始化為false了,結果還得在結束前把方向保存下來。Kotlin并不能真正給添加個參數到某個類,只能繼承ValueAnimator進行擴展了。其次播放控制問題,為了保留原來的方法和避免reverse()存在的問題,添加了幾個方法animStart()正向播放,animReverse()反向播放,animTrigger()切換方向(類似reverse()作用)。代碼很簡單并注釋了以后就用它來替代ValueAnimator了,本來想也改下ObjectAnimator發現是final無法繼承,看在沒什么大問題的份上就放過它了。
package cn.wittyneko.anim
import android.animation.*
/**
* Created by wittyneko on 2017/7/7.
*/
open class ValueAnim : ValueAnimator(), AnimListener {
companion object {
internal val argbEvaluator = ArgbEvaluator()
fun ofInt(vararg values: Int): ValueAnim {
val anim = ValueAnim()
anim.setIntValues(*values)
return anim
}
fun ofArgb(values: IntArray): ValueAnim {
val anim = ValueAnim()
anim.setIntValues(*values)
anim.setEvaluator(argbEvaluator)
return anim
}
fun ofFloat(vararg values: Float): ValueAnim {
val anim = ValueAnim()
anim.setFloatValues(*values)
return anim
}
fun ofPropertyValuesHolder(vararg values: PropertyValuesHolder): ValueAnim {
val anim = ValueAnim()
anim.setValues(*values)
return anim
}
fun ofObject(evaluator: TypeEvaluator, vararg values: Any): ValueAnim {
val anim = ValueAnim()
anim.setObjectValues(*values)
anim.setEvaluator(evaluator)
return anim
}
}
private var _isAnimReverse: Boolean = true
var listener: AnimListener? = null
var isAnimEnd: Boolean = false
protected set
var isAnimCancel: Boolean = false
protected set
//是否反向
var isAnimReverse: Boolean
get() {
if (isRunning) {
return isReversing
} else {
return _isAnimReverse
}
}
internal set(value) {
_isAnimReverse = value
}
//動畫播放時間
val animCurrentPlayTime: Long
get() {
if (isRunning && isAnimReverse) {
return duration - currentPlayTime
} else {
return currentPlayTime
}
}
init {
addListener(this)
addUpdateListener(this)
}
/**
* 正向播放
*/
open fun animStart() {
when {
isRunning && isAnimReverse -> {
reverse()
}
!isRunning -> {
start()
}
}
}
/**
* 反向播放
*/
open fun animReverse() {
when {
isRunning && !isAnimReverse -> {
reverse()
}
!isRunning -> {
reverse()
}
}
}
/**
* 切換播放方向
*/
open fun animTrigger() {
if (isAnimReverse) {
animStart()
} else {
animReverse()
}
}
override fun start() {
isAnimCancel = false
isAnimEnd = false
super.start()
}
override fun reverse() {
isAnimCancel = false
isAnimEnd = false
super.reverse()
}
override fun end() {
isAnimCancel = false
isAnimEnd = true
super.end()
}
override fun cancel() {
isAnimCancel = true
isAnimEnd = false
super.cancel()
}
override fun onAnimationUpdate(animation: ValueAnimator?) {
listener?.onAnimationUpdate(animation)
}
override fun onAnimationStart(animation: Animator?) {
listener?.onAnimationStart(animation)
}
override fun onAnimationEnd(animation: Animator?) {
if ((isStarted || isRunning) && animation is ValueAnimator) {
_isAnimReverse = animation.isReversing
}
listener?.onAnimationEnd(animation)
}
override fun onAnimationCancel(animation: Animator?) {
listener?.onAnimationCancel(animation)
}
override fun onAnimationRepeat(animation: Animator?) {
listener?.onAnimationRepeat(animation)
}
}
interface AnimListener : ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener
// 動畫播放時方向 api22+
val ValueAnimator.isReversing: Boolean
get() {
try {
var rfield = ValueAnimator::class.java.getDeclaredField("mReversing")
rfield.isAccessible = true
return rfield.get(this) as? Boolean ?: false
} catch (e: Throwable) {
return isPlayingBackwards
}
}
// 動畫播放時方向 api21-
val ValueAnimator.isPlayingBackwards: Boolean
get() {
try {
var rfield = ValueAnimator::class.java.getDeclaredField("mPlayingBackwards")
rfield.isAccessible = true
return rfield.get(this) as? Boolean ?: false
} catch (e: Throwable) {
return false
}
}
AnimSet
這才是本篇的重點,首先跟AnimatorSet沒有半毛關系,AnimatorSet是個final類其次再它基礎上修改,還不如重造一個容易。所以AnimSet當然是再擁有優良血統的ValueAnim上擴展出來的啦。為了避免AnimatorSet的坑AnimSet設計得很簡單,如果想要AnimatorSet的的before和after的效果也可以很方便的擴展,為了偷懶不對是為了簡單易懂,就不實現了畢竟沒什么用。子動畫播放時間只跟動畫集合有關,通俗的講假設動畫集合播放1秒后開始播放第一個動畫2秒后開始第二個動畫,這樣只要一個子動畫相對集合的延遲時間就足夠實現復雜動畫了。任何復雜動畫都能簡單的實現,剩下的就是其它的優化了,比如子動畫的播放方向,動畫集合嵌套問題的處理了。代碼重點在于addChildAnim()添加子動畫,animChildPlayTime()計算子動畫播放時間,onAnimationUpdate刷新子動畫。
package cn.wittyneko.anim
import android.animation.*
import android.view.animation.LinearInterpolator
/**
* Created by wittyneko on 2017/7/6.
*/
open class AnimSet : ValueAnim() {
companion object {
fun ofDef(): AnimSet {
return ofFloat(0f, 1f)
}
fun ofInt(vararg values: Int): AnimSet {
val anim = AnimSet()
anim.setIntValues(*values)
return anim
}
fun ofArgb(values: IntArray): AnimSet {
val anim = AnimSet()
anim.setIntValues(*values)
anim.setEvaluator(argbEvaluator)
return anim
}
fun ofFloat(vararg values: Float): AnimSet {
val anim = AnimSet()
anim.setFloatValues(*values)
return anim
}
fun ofPropertyValuesHolder(vararg values: PropertyValuesHolder): AnimSet {
val anim = AnimSet()
anim.setValues(*values)
return anim
}
fun ofObject(evaluator: TypeEvaluator, vararg values: Any): AnimSet {
val anim = AnimSet()
anim.setObjectValues(*values)
anim.setEvaluator(evaluator)
return anim
}
}
var childAnimSet: HashSet = hashSetOf()
init {
interpolator = LinearInterpolator()
}
/**
* 計算子動畫播放時間
* @param delayed 子動畫延遲時間
* @param duration 子動畫時長
*
* @return 子動畫當前播放時間
*/
fun animChildPlayTime(delayed: Long, duration: Long): Long {
var childPlayTime = animCurrentPlayTime - delayed
when {
childPlayTime < 0 -> {
childPlayTime = 0
}
childPlayTime > duration -> {
childPlayTime = duration
}
}
return childPlayTime
}
/**
* 添加子動畫
* @param childAnim 子動畫
* @param delayed 子動畫延遲時間
* @param tag 子動畫tag標簽
*/
fun addChildAnim(childAnim: ValueAnimator, delayed: Long = 0, tag: String = AnimWrapper.EMPTY_TAG): AnimSet {
addChildAnim(AnimWrapper(childAnim, delayed, tag))
return this
}
/**
* 添加子動畫
* @param child 子動畫包裝類
*
* @throws e duration grate than parent
*/
fun addChildAnim(child: AnimWrapper): AnimSet {
if (child.delayed + child.anim.duration > this.duration)
throw Exception("duration greater than parent")
childAnimSet.add(child)
return this
}
override fun onAnimationUpdate(animation: ValueAnimator?) {
super.onAnimationUpdate(animation)
childAnimSet.forEach {
//刷新子動畫
val anim = it.anim
anim.currentPlayTime = animChildPlayTime(it.delayed, anim.duration)
if(anim is ValueAnim) {
anim.isAnimReverse = isAnimReverse
}
}
}
override fun onAnimationStart(animation: Animator?) {
super.onAnimationStart(animation)
childAnimSet.forEach {
val anim = it.anim
anim.listeners?.forEach {
it.onAnimationStart(anim)
}
}
}
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
childAnimSet.forEach {
val anim = it.anim
if (isAnimEnd) {
if (isAnimReverse)
anim.currentPlayTime = 0
else
anim.currentPlayTime = anim.duration
}
anim.listeners?.forEach {
it.onAnimationEnd(anim)
}
}
}
override fun onAnimationCancel(animation: Animator?) {
super.onAnimationCancel(animation)
childAnimSet.forEach {
val anim = it.anim
anim.listeners?.forEach {
it.onAnimationCancel(anim)
}
}
}
override fun onAnimationRepeat(animation: Animator?) {
super.onAnimationRepeat(animation)
childAnimSet.forEach {
val anim = it.anim
anim.listeners?.forEach {
it.onAnimationRepeat(anim)
}
}
}
/**
* 子動畫包裝類
*/
class AnimWrapper(
var anim: ValueAnimator,
var delayed: Long = 0,
var tag: String = AnimWrapper.EMPTY_TAG) {
companion object {
val EMPTY_TAG = ""
}
}
}
使用方法
見證奇跡的時刻,神獸保佑🙏代碼無Bug。看看如何實現上面的動畫要求。應該沒什么需要解釋的方案A只用一個AnimSet,方案B采用AnimSet嵌套AnimSet。
val msec = 1000L
val animTime = ValueAnim.ofFloat(0f, 1f)
animTime.interpolator = LinearInterpolator()
animTime.duration = msec * 3
animTime.addUpdateListener {
time.text = "time: ${animTime.animCurrentPlayTime}"
}
val objAnimRed = ObjectAnimator.ofFloat(red, "translationX", 0f, 300f)
objAnimRed.interpolator = LinearInterpolator()
objAnimRed.duration = msec * 3
val objAnimGreen = ObjectAnimator.ofFloat(green, "translationX", 0f, 300f)
objAnimGreen.interpolator = LinearInterpolator()
objAnimGreen.duration = msec * 2
val objAnimBlue = ObjectAnimator.ofFloat(blue, "translationX", 0f, 300f)
objAnimBlue.interpolator = LinearInterpolator()
objAnimBlue.duration = msec * 1
animSet = AnimSet.ofDef()
animSet.duration = msec * 3;
//Plan A
// animSet.addChildAnim(animTime)
// .addChildAnim(objAnimRed)
// .addChildAnim(objAnimGreen, msec * 1)
// .addChildAnim(objAnimBlue, msec * 2)
//Plan B
val childSet = AnimSet.ofDef()
childSet.duration = msec * 2
childSet.addChildAnim(objAnimGreen)
.addChildAnim(objAnimBlue, msec * 1)
animSet.addChildAnim(animTime)
.addChildAnim(objAnimRed)
.addChildAnim(childSet, msec * 1)
trigger.onClick {
animSet.animTrigger()
}
start.onClick {
animSet.animStart()
}
reverse.onClick {
animSet.animReverse()
}
pause.onClick {
animSet.pause()
}
resume.onClick {
animSet.resume()
}
end.onClick {
animSet.end()
}
cancel.onClick {
animSet.cancel()
}
總結
以上是生活随笔為你收集整理的android 动画动态消失,Android属性动画——没有什么动画是一个AnimSet不能解决的...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言的运算符表格,C语言教案(运算符和
- 下一篇: android 按钮吐司,Android