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 package org.oxycblt.auxio.image
import android.animation.ValueAnimator import android.animation.Animator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.graphics.Canvas 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.Genre
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.MaterialFader
import org.oxycblt.auxio.ui.UISettings import org.oxycblt.auxio.ui.UISettings
import org.oxycblt.auxio.util.getAttrColorCompat import org.oxycblt.auxio.util.getAttrColorCompat
import org.oxycblt.auxio.util.getColorCompat import org.oxycblt.auxio.util.getColorCompat
import org.oxycblt.auxio.util.getDimenPixels import org.oxycblt.auxio.util.getDimenPixels
import org.oxycblt.auxio.util.getDrawableCompat 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 * 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 selectionBadge: ImageView?
private val iconSize: Int? 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 indicatorMatrix = Matrix()
private val indicatorMatrixSrc = RectF() private val indicatorMatrixSrc = RectF()
private val indicatorMatrixDst = RectF() private val indicatorMatrixDst = RectF()
@ -294,43 +295,10 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr
} }
private fun invalidateSelectionIndicatorAlpha(selectionBadge: ImageView) { private fun invalidateSelectionIndicatorAlpha(selectionBadge: ImageView) {
// Set up a target transition for the selection indicator. fadeAnimator?.cancel()
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 = fadeAnimator =
ValueAnimator.ofFloat(selectionBadge.alpha, targetAlpha).apply { (if (isActivated) fader.fadeIn(selectionBadge) else fader.fadeOut(selectionBadge))
duration = targetDuration .also { it.start() }
addUpdateListener { selectionBadge.alpha = it.animatedValue as Float }
start()
}
} }
/** /**

View file

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