image: new cover selection animation

This commit is contained in:
Alexander Capehart 2024-10-19 12:58:58 -06:00
parent 59fd4b5e18
commit 89110c2798
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
2 changed files with 50 additions and 51 deletions

View file

@ -18,7 +18,7 @@
package org.oxycblt.auxio.image
import android.animation.ValueAnimator
import android.animation.Animator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
@ -56,12 +56,12 @@ import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.MaterialFader
import org.oxycblt.auxio.ui.UISettings
import org.oxycblt.auxio.util.getAttrColorCompat
import org.oxycblt.auxio.util.getColorCompat
import org.oxycblt.auxio.util.getDimenPixels
import org.oxycblt.auxio.util.getDrawableCompat
import org.oxycblt.auxio.util.getInteger
/**
* Auxio's extension of [ImageView] that enables cover art loading and playing indicator and
@ -93,7 +93,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
private val selectionBadge: ImageView?
private val iconSize: Int?
private var fadeAnimator: ValueAnimator? = null
private val fader = MaterialFader.forSmallComponent(context)
private var fadeAnimator: Animator? = null
private val indicatorMatrix = Matrix()
private val indicatorMatrixSrc = RectF()
private val indicatorMatrixDst = RectF()
@ -294,43 +295,10 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
}
private fun invalidateSelectionIndicatorAlpha(selectionBadge: ImageView) {
// Set up a target transition for the selection indicator.
val targetAlpha: Float
val targetDuration: Long
if (isActivated) {
// View is "activated" (i.e marked as selected), so show the selection indicator.
targetAlpha = 1f
targetDuration = context.getInteger(R.integer.anim_fade_enter_duration).toLong()
} else {
// View is not "activated", hide the selection indicator.
targetAlpha = 0f
targetDuration = context.getInteger(R.integer.anim_fade_exit_duration).toLong()
}
if (selectionBadge.alpha == targetAlpha) {
// Nothing to do.
return
}
if (!isLaidOut) {
// Not laid out, initialize it without animation before drawing.
selectionBadge.alpha = targetAlpha
return
}
if (fadeAnimator != null) {
// Cancel any previous animation.
fadeAnimator?.cancel()
fadeAnimator = null
}
fadeAnimator?.cancel()
fadeAnimator =
ValueAnimator.ofFloat(selectionBadge.alpha, targetAlpha).apply {
duration = targetDuration
addUpdateListener { selectionBadge.alpha = it.animatedValue as Float }
start()
}
(if (isActivated) fader.fadeIn(selectionBadge) else fader.fadeOut(selectionBadge))
.also { it.start() }
}
/**

View file

@ -97,12 +97,21 @@ class MaterialCornerAnim(context: Context) {
}
}
class MaterialFader private constructor(context: Context, private val scale: Float) {
private val alphaOutConfig =
AnimConfig.of(context, AnimConfig.EMPHASIZED_ACCELERATE, AnimConfig.SHORT3)
private val scaleOutConfig =
AnimConfig.of(context, AnimConfig.EMPHASIZED_ACCELERATE, AnimConfig.MEDIUM1)
private val inConfig = AnimConfig.of(context, AnimConfig.EMPHASIZED, AnimConfig.LONG2)
class MaterialFader
private constructor(
context: Context,
private val scale: Float,
@AttrRes outInterpolator: Int,
alphaOutDuration: Pair<Int, Int>,
scaleOutDuration: Pair<Int, Int>,
inInterpolator: Int,
alphaInDuration: Pair<Int, Int>,
scaleInDuration: Pair<Int, Int>
) {
private val alphaOutConfig = AnimConfig.of(context, outInterpolator, alphaOutDuration)
private val scaleOutConfig = AnimConfig.of(context, outInterpolator, scaleOutDuration)
private val alphaInConfig = AnimConfig.of(context, inInterpolator, alphaInDuration)
private val scaleInConfig = AnimConfig.of(context, inInterpolator, scaleInDuration)
fun jumpToFadeOut(view: View) {
view.apply {
@ -128,7 +137,11 @@ class MaterialFader private constructor(context: Context, private val scale: Flo
return AnimatorSet()
}
val alphaAnimator = alphaOutConfig.genericFloat(view.alpha, 0f) { view.alpha = it }
val alphaAnimator =
alphaOutConfig.genericFloat(view.alpha, 0f) {
view.alpha = it
view.isInvisible = view.alpha == 0f
}
val scaleXAnimator = scaleOutConfig.genericFloat(view.scaleX, scale) { view.scaleX = it }
val scaleYAnimator = scaleOutConfig.genericFloat(view.scaleY, scale) { view.scaleY = it }
return AnimatorSet().apply { playTogether(alphaAnimator, scaleXAnimator, scaleYAnimator) }
@ -140,19 +153,37 @@ class MaterialFader private constructor(context: Context, private val scale: Flo
return AnimatorSet()
}
val alphaAnimator =
inConfig.genericFloat(view.alpha, 1f) {
alphaInConfig.genericFloat(view.alpha, 1f) {
view.alpha = it
view.isInvisible = view.alpha == 0f
}
val scaleXAnimator = inConfig.genericFloat(view.scaleX, 1.0f) { view.scaleX = it }
val scaleYAnimator = inConfig.genericFloat(view.scaleY, 1.0f) { view.scaleY = it }
val scaleXAnimator = scaleInConfig.genericFloat(view.scaleX, 1.0f) { view.scaleX = it }
val scaleYAnimator = scaleInConfig.genericFloat(view.scaleY, 1.0f) { view.scaleY = it }
return AnimatorSet().apply { playTogether(alphaAnimator, scaleXAnimator, scaleYAnimator) }
}
companion object {
fun forSmallComponent(context: Context) = MaterialFader(context, 0.4f)
fun forSmallComponent(context: Context) =
MaterialFader(
context,
0.6f,
AnimConfig.EMPHASIZED_ACCELERATE,
AnimConfig.SHORT1,
AnimConfig.SHORT3,
AnimConfig.EMPHASIZED_DECELERATE,
AnimConfig.SHORT1,
AnimConfig.MEDIUM3)
fun forLargeComponent(context: Context) = MaterialFader(context, 0.9f)
fun forLargeComponent(context: Context) =
MaterialFader(
context,
0.9f,
AnimConfig.EMPHASIZED_ACCELERATE,
AnimConfig.SHORT3,
AnimConfig.MEDIUM1,
AnimConfig.EMPHASIZED,
AnimConfig.LONG2,
AnimConfig.LONG2)
}
}