[go: nahoru, domu]

blob: 64c31802c530bf6a01222c73e277491fbab017a6 [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.ui.widgets.implicitanimation
import androidx.ui.animation.Animation
import androidx.ui.animation.AnimationController
import androidx.ui.animation.Tween
import androidx.ui.animation.animations.CurvedAnimation
import androidx.ui.widgets.tickerprovider.SingleTickerProviderStateMixin
/**
* Signature for a [Tween] factory.
*
* This is the type of one of the arguments of [TweenVisitor], the signature
* used by [AnimatedWidgetBaseState.forEachTween].
*/
typealias TweenConstructor<T> = (targetValue: T) -> Tween<T>
/** Signature for callbacks passed to [AnimatedWidgetBaseState.forEachTween]. */
interface TweenVisitor {
operator fun <T> invoke(
tween: Tween<T>?,
targetValue: T?,
constructor: TweenConstructor<T>
): Tween<T>?
}
/**
* A base class for widgets with implicit animations.
*
* Subclasses must implement the [forEachTween] method to help
* [AnimatedWidgetBaseState] iterate through the subclasses' widget's fields
* and animate them.
*/
abstract class AnimatedWidgetBaseState<T : ImplicitlyAnimatedWidget>(
widget: T
) : SingleTickerProviderStateMixin<T>(widget) {
private var controller: AnimationController? = null
/** The animation driving this widget's implicit animations. */
var animation: Animation<Float>? = null
private set
override fun initState() {
super.initState()
controller = AnimationController(
duration = widget.duration,
debugLabel = widget.toStringShort(),
vsync = this
).apply {
addListener(handleAnimationChanged)
}
updateCurve()
constructTweens()
}
override fun didUpdateWidget(oldWidget: T) {
super.didUpdateWidget(oldWidget)
if (widget.curve != oldWidget.curve) {
updateCurve()
}
val controller = controller!!
controller.duration = widget.duration
if (constructTweens()) {
forEachTween(object : TweenVisitor {
override operator fun <T> invoke(
tween: Tween<T>?,
targetValue: T?,
constructor: TweenConstructor<T>
): Tween<T>? {
updateTween(tween, targetValue)
return tween
}
})
controller.apply {
value = 0.0f
forward()
}
}
}
private fun updateCurve() {
animation = CurvedAnimation(parent = controller!!, curve = widget.curve)
}
override fun dispose() {
controller!!.dispose()
super.dispose()
}
private val handleAnimationChanged = {
setState {}
}
internal fun <T> shouldAnimateTween(tween: Tween<T>, targetValue: T): Boolean {
return targetValue != (tween.end ?: tween.begin)
}
internal fun <T> updateTween(tween: Tween<T>?, targetValue: T?) {
if (tween == null)
return
tween.apply {
begin = tween.evaluate(animation!!)
end = targetValue
}
}
private fun constructTweens(): Boolean {
var shouldStartAnimation = false
forEachTween(object : TweenVisitor {
override fun <T> invoke(
tween: Tween<T>?,
targetValue: T?,
constructor: TweenConstructor<T>
): Tween<T>? {
var tween = tween
if (targetValue != null) {
tween = tween ?: constructor(targetValue)
if (shouldAnimateTween(tween, targetValue)) {
shouldStartAnimation = true
}
} else {
tween = null
}
return tween
}
})
return shouldStartAnimation
}
/**
* Subclasses must implement this function by running through the following
* steps for each animatable facet in the class:
*
* 1. Call the visitor callback with three arguments, the first argument
* being the current value of the Tween<T> object that represents the
* tween (initially null), the second argument, of type T, being the value
* on the Widget that represents the current target value of the
* tween, and the third being a callback that takes a value T (which will
* be the second argument to the visitor callback), and that returns an
* Tween<T> object for the tween, configured with the given value
* as the begin value.
*
* 2. Take the value returned from the callback, and store it. This is the
* value to use as the current value the next time that the forEachTween()
* method is called.
*/
abstract fun forEachTween(visitor: TweenVisitor)
}