日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android 动画动态消失,Android属性动画——没有什么动画是一个AnimSet不能解决的...

發布時間:2024/4/11 Android 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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不能解决的...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。