aboutsummaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorAllanWang <me@allanwang.ca>2018-09-09 13:06:49 -0400
committerAllanWang <me@allanwang.ca>2018-09-09 13:06:49 -0400
commita83dd51f545dda905194ea84caed6e2906b0f06f (patch)
treead28aad1aef43c164216f7a877e74936ee71de32 /core/src
parentd540934915da26ab2cec4c897e973be35e0bfe24 (diff)
downloadkau-a83dd51f545dda905194ea84caed6e2906b0f06f.tar.gz
kau-a83dd51f545dda905194ea84caed6e2906b0f06f.tar.bz2
kau-a83dd51f545dda905194ea84caed6e2906b0f06f.zip
Add initial progress animator change
Diffstat (limited to 'core/src')
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt167
1 files changed, 117 insertions, 50 deletions
diff --git a/core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt b/core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt
index e37e59f..39e8657 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt
@@ -3,7 +3,7 @@ package ca.allanwang.kau.ui
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
-import android.view.animation.Interpolator
+import ca.allanwang.kau.kotlin.kauRemoveIf
/**
* Created by Allan Wang on 2017-11-10.
@@ -13,77 +13,144 @@ import android.view.animation.Interpolator
* This differs in that everything can be done with simple listeners, which will be bundled
* and added to the backing [ValueAnimator]
*/
-class ProgressAnimator private constructor(private vararg val values: Float) {
+class ProgressAnimator private constructor() : ValueAnimator() {
companion object {
- inline fun ofFloat(crossinline builder: ProgressAnimator.() -> Unit) = ofFloat(0f, 1f) { builder() }
- fun ofFloat(vararg values: Float, builder: ProgressAnimator.() -> Unit) = ProgressAnimator(*values).apply {
- builder()
- build()
+ fun ofFloat(builder: ProgressAnimator.() -> Unit): ProgressAnimator = ProgressAnimator().apply {
+ setFloatValues(0f, 1f)
+ addUpdateListener { apply(it.animatedValue as Float) }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
+ isCancelled = false
+ startActions.runAll()
+ }
+
+ override fun onAnimationCancel(animation: Animator?) {
+ isCancelled = true
+ cancelActions.runAll()
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ endActions.runAll()
+ isCancelled = false
+ }
+ })
+ apply(builder)
+ }
+
+ /**
+ * Gets output of a linear function starting at [start] when [progress] is 0 and [end] when [progress] is 1 at point [progress].
+ */
+ fun progress(start: Float, end: Float, progress: Float): Float = start + (end - start) * progress
+
+ fun progress(start: Float, end: Float, progress: Float, min: Float, max: Float): Float = when {
+ min == max -> throw IllegalArgumentException("Progress range cannot be 0 (min == max == $min")
+ progress <= min -> start
+ progress >= max -> end
+ else -> {
+ val trueProgress = (progress - min) / (max - min)
+ start + (end - start) * trueProgress
+ }
}
+
}
- private val animators: MutableList<(Float) -> Unit> = mutableListOf()
- private val startActions: MutableList<() -> Unit> = mutableListOf()
- private val endActions: MutableList<() -> Unit> = mutableListOf()
+ private val animators: MutableList<ProgressDisposableAction> = mutableListOf()
+ private val startActions: MutableList<ProgressDisposableRunnable> = mutableListOf()
+ private val cancelActions: MutableList<ProgressDisposableRunnable> = mutableListOf()
+ private val endActions: MutableList<ProgressDisposableRunnable> = mutableListOf()
+ var isCancelled: Boolean = false
+ private set
- var duration: Long = -1L
- var interpolator: Interpolator? = null
/**
- * Add more changes to the [ValueAnimator] before running
+ * Converts an action to a disposable action
*/
- var extraConfigs: ValueAnimator.() -> Unit = {}
+ private fun ProgressAction.asDisposable(): ProgressDisposableAction = { this(it); false }
+
+ private fun ProgressRunnable.asDisposable(): ProgressDisposableRunnable = { this(); false }
/**
- * Range animator. Multiples the range by the current float progress before emission
+ * If [condition] applies, run the animator.
+ * @return [condition]
*/
- fun withAnimator(from: Float, to: Float, animator: (Float) -> Unit) = animators.add {
- val range = to - from
- animator(range * it + from)
+ private fun ProgressAction.runIf(condition: Boolean, progress: Float): Boolean {
+ if (condition) this(progress)
+ return condition
}
- /**
- * Standard animator. Emits progress value as is
- */
- fun withAnimator(animator: (Float) -> Unit) = animators.add(animator)
+ private fun MutableList<ProgressDisposableRunnable>.runAll() = kauRemoveIf { it() }
- /**
- * Start action to be called once when the animator first begins
- */
- fun withStartAction(action: () -> Unit) = startActions.add(action)
+ internal fun apply(progress: Float) {
+ animators.kauRemoveIf { it(progress) }
+ }
+
+ fun withAnimator(action: ProgressAction) =
+ withDisposableAnimator(action.asDisposable())
/**
- * End action to be called once when the animator ends
+ * Range animator. Multiples the range by the current float progress before emission
*/
- fun withEndAction(action: () -> Unit) = endActions.add(action)
-
- fun build() {
- ValueAnimator.ofFloat(*values).apply {
- if (this@ProgressAnimator.duration > 0L)
- duration = this@ProgressAnimator.duration
- if (this@ProgressAnimator.interpolator != null)
- interpolator = this@ProgressAnimator.interpolator
- addUpdateListener {
- val progress = it.animatedValue as Float
- animators.forEach { it(progress) }
+ fun withAnimator(from: Float, to: Float, action: ProgressAction) =
+ withDisposableAnimator(from, to, action.asDisposable())
+
+ fun withDisposableAnimator(action: ProgressDisposableAction) = animators.add(action)
+
+ fun withDisposableAnimator(from: Float, to: Float, action: ProgressDisposableAction) {
+ if (to != from) {
+ animators.add {
+ action(progress(from, to, it))
}
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationStart(animation: Animator?) {
- startActions.forEach { it() }
- }
+ }
+ }
- override fun onAnimationEnd(animation: Animator?) {
- endActions.forEach { it() }
+ fun withRangeAnimator(min: Float, max: Float, start: Float, end: Float, progress: Float, action: ProgressAction) {
+ if (min >= max) {
+ throw IllegalArgumentException("Range animator must have min < max; currently min=$min, max=$max")
+ }
+ withDisposableAnimator {
+ when {
+ it > max -> true
+ it < min -> false
+ else -> {
+ action(progress(start, end, progress, min, max))
+ false
+ }
}
+ }
+ }
- override fun onAnimationCancel(animation: Animator?) {
- endActions.forEach { it() }
- }
- })
- extraConfigs()
- start()
+ fun withPointAnimator(point: Float, action: ProgressAction) {
+ animators.add {
+ action.runIf(it >= point, it)
}
}
-} \ No newline at end of file
+
+ fun withDelayedStartAction(skipCount: Int, action: ProgressAction) {
+ var count = 0
+ animators.add {
+ action.runIf(count++ >= skipCount, it)
+ }
+ }
+
+ /**
+ * Start action to be called once when the animator first begins
+ */
+ fun withStartAction(action: ProgressRunnable) = withDisposableStartAction(action.asDisposable())
+
+ fun withDisposableStartAction(action: ProgressDisposableRunnable) = startActions.add(action)
+
+ fun withCancelAction(action:ProgressRunnable) = withDisposableCancelAction(action.asDisposable())
+
+ fun withDisposableCancelAction(action: ProgressDisposableRunnable) = cancelActions.add(action)
+
+ fun withEndAction(action: ProgressRunnable) = withDisposableEndAction(action.asDisposable())
+
+ fun withDisposableEndAction(action: ProgressDisposableRunnable) = endActions.add(action)
+}
+
+private typealias ProgressAction = (Float) -> Unit
+private typealias ProgressDisposableAction = (Float) -> Boolean
+private typealias ProgressRunnable = () -> Unit
+private typealias ProgressDisposableRunnable = () -> Boolean \ No newline at end of file