aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt167
-rw-r--r--docs/Migration.md3
-rw-r--r--sample/src/main/res/xml/kau_changelog.xml2
3 files changed, 120 insertions, 52 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
diff --git a/docs/Migration.md b/docs/Migration.md
index 721cd0d..c9e39b8 100644
--- a/docs/Migration.md
+++ b/docs/Migration.md
@@ -8,6 +8,9 @@ Along with the update to support Android Studio 3.1, a lot of changes have occur
* Resource ids can be negatives due to the bit overflow. Instead, `INVALID_ID` has been introduced to signify an unset or invalid id.
Methods such as `Context.string(id, fallback)` now check against `INVALID_ID` through equality rather than using an inequality to address this.
+* Kotterknife has been deprecated in favour of `kotlin-android-extensions`. See [official docs](https://kotlinlang.org/docs/tutorials/android-plugin.html#view-binding).
+* `ProgressAnimator` has been completely rewritten to be an extension of `ValueAnimator`.
+This for the most part is not a breaking change, apart from the fact that creating an animator will not start it immediately.
# v3.6.0
diff --git a/sample/src/main/res/xml/kau_changelog.xml b/sample/src/main/res/xml/kau_changelog.xml
index a5d0ea9..e5ddad5 100644
--- a/sample/src/main/res/xml/kau_changelog.xml
+++ b/sample/src/main/res/xml/kau_changelog.xml
@@ -12,8 +12,6 @@
<item text=":adapter: Add more IAdapter functions to help retrieve selections" />
<item text=":core: Add deprecation warning to bindView for fragment based extensions; use bindViewResettable instead" />
<item text=":kpref-activity: Fix seekbar increment" />
- <item text="" />
- <item text="" />
<version title="v3.7.1" />
<item text="Update appcompat to 27.1.0" />