日韩性视频-久久久蜜桃-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不能解决的...的全部內容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: 九九久久精品 | 日韩欧美一区在线观看 | 欧美a一级片 | 五月婷婷影院 | 很黄的性视频 | 日本成人免费观看 | www.我爱av | 丝袜美腿亚洲综合 | 日韩欧美无 | 男女www视频 | 日本大尺度吃奶做爰久久久绯色 | 毛片毛片毛片毛片毛片毛片毛片毛片 | 欧美性爱精品一区 | 国产乱一区二区三区 | 一级做a免费视频 | 精品视频在线免费看 | 少妇3p视频| 国产 日韩 欧美 综合 | 久久久久噜噜噜亚洲熟女综合 | 91av在线网站 | 久久男人av | 性欧美在线视频 | 三上悠亚一区二区 | 五月综合色 | 天堂中文在线8 | jizz日本视频 | 色小姐av | 窝窝午夜视频 | 欧美乱日 | 亚洲国产精品第一页 | 国产在线观看免费网站 | 亚洲无人禁区 | 麻豆专区| 一出一进一爽一粗一大视频 | 成人调教视频 | 天天天天躁天天爱天天碰2018 | 亚洲 欧美 变态 另类 综合 | 中文字幕福利视频 | 久久咪咪| 有机z中国电影免费观看 | 欧美成人精品 | 九九亚洲| 777奇米视频 | 二区三区在线视频 | 日本高清中文字幕 | 亚洲AV无码片久久精品 | 一区视频在线播放 | 理论片国产 | 精品爆乳一区二区三区无码av | 精品久久久无码中文字幕 | 不卡视频免费在线观看 | 国产女人叫床高潮大片免费 | 九九午夜 | 日本xxxxxⅹxxxx69 | 制服丝袜一区二区三区 | www.亚洲高清| 黑人巨大精品一区二区在线 | 一区二区视频免费 | 天天干天天操心 | 激情小说中文字幕 | 韩国国产在线 | 二区视频在线 | 国产美女作爱全过程免费视频 | 国产欧美在线观看视频 | 五月天激情小说 | 精品一区二区久久久久久按摩 | 放荡的少妇2欧美版 | 欧美成人三级视频 | 在线视频福利 | 亚洲综合影院 | 国产黄视频网站 | 日本少妇一区二区三区 | 国产精品igao视频 | 久久精品色欲国产AV一区二区 | 亚洲av无码乱码在线观看富二代 | 4438国产精品一区二区 | 日韩成人综合 | 日韩女优网站 | 激情拍拍拍 | 国产第2页 | 四虎av影院 | 中文字幕视频观看 | 欧美在线不卡 | 少妇无内裤下蹲露大唇视频 | 黄色欧美一级片 | 福利电影一区 | 国产一区二区在线免费观看视频 | 婷婷丁香激情 | 欧美做受高潮1 | 亚洲视频在线观看网址 | 日韩精品麻豆 | 91免费看.| 99热免费观看 | 被黑人啪到哭的番号922在线 | 少妇精品无码一区二区三区 | 波多野结衣一区二区三区免费视频 | 久久久在线观看 | 丝袜制服影音先锋 | 制服丝袜国产精品 |