Updated on 2026-08-14
This commit is contained in:
parent
72e282956c
commit
b327109ca8
3 changed files with 175 additions and 201 deletions
|
|
@ -32,14 +32,14 @@ fun createProgressListener(duration: Long, listener: ProgressListener?): Animato
|
|||
return valueAnimator
|
||||
}
|
||||
|
||||
fun View.unfoldAnimation(animDuration: Long, properties: AnimationProperties): AnimatorSet {
|
||||
val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, properties.yTranslationEnd)
|
||||
fun View.unfoldAnimation(animDuration: Long, properties: LeapViewProperties): AnimatorSet {
|
||||
val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
|
||||
translate.duration = animDuration
|
||||
return AnimatorSet().apply { playTogether(translate) }
|
||||
}
|
||||
|
||||
fun View.foldAnimation(animDuration: Long, properties: AnimationProperties): AnimatorSet {
|
||||
val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, properties.yTranslationEnd)
|
||||
fun View.foldAnimation(animDuration: Long, properties: LeapViewProperties): AnimatorSet {
|
||||
val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
|
||||
translate.duration = animDuration
|
||||
|
||||
return AnimatorSet().apply {
|
||||
|
|
@ -47,17 +47,17 @@ fun View.foldAnimation(animDuration: Long, properties: AnimationProperties): Ani
|
|||
}
|
||||
}
|
||||
|
||||
fun View.leapAnimation(animDuration: Long, properties: AnimationProperties, overLift: Float): AnimatorSet {
|
||||
fun View.leapAnimation(animDuration: Long, properties: LeapViewProperties, overLift: Float): AnimatorSet {
|
||||
val halfDuration = animDuration / 2
|
||||
|
||||
val upTo = (height.toFloat() + properties.yTranslationStart + overLift) * -1
|
||||
val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, upTo)
|
||||
val upTo = (height.toFloat() + overLift) * -1
|
||||
val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo)
|
||||
translateUp.duration = halfDuration
|
||||
translateUp.interpolator = LinearInterpolator()
|
||||
|
||||
val scaleDuration = halfDuration - halfDuration / 5
|
||||
val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scaleStart, properties.scaleEnd)
|
||||
val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scaleStart, properties.scaleEnd)
|
||||
val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale)
|
||||
val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale)
|
||||
val scale = AnimatorSet().apply {
|
||||
startDelay = scaleDuration
|
||||
duration = scaleDuration
|
||||
|
|
@ -65,37 +65,28 @@ fun View.leapAnimation(animDuration: Long, properties: AnimationProperties, over
|
|||
playTogether(scaleX, scaleY)
|
||||
}
|
||||
|
||||
val elevation = ValueAnimator.ofFloat(properties.elevationStart, properties.elevationEnd)
|
||||
elevation.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: properties.elevationStart }
|
||||
elevation.startDelay = halfDuration
|
||||
elevation.duration = 100
|
||||
|
||||
val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo, properties.yTranslationEnd)
|
||||
val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd, halfDuration, 100)
|
||||
val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
|
||||
translateDown.startDelay = halfDuration
|
||||
translateDown.duration = halfDuration
|
||||
translateDown.interpolator = LinearInterpolator()
|
||||
|
||||
return AnimatorSet().apply {
|
||||
playTogether(
|
||||
translateUp,
|
||||
scale,
|
||||
elevation,
|
||||
translateDown
|
||||
)
|
||||
playTogether(translateUp, scale, elevation, translateDown)
|
||||
}
|
||||
}
|
||||
|
||||
fun View.leapBackAnimation(animDuration: Long, properties: AnimationProperties, calculator: PropertyCalculator): AnimatorSet {
|
||||
fun View.leapBackAnimation(animDuration: Long, properties: LeapViewProperties, calculator: PropertyCalculator): AnimatorSet {
|
||||
val halfDuration = animDuration / 2
|
||||
|
||||
val upTo = ((height).toFloat() - calculator.yTranslationFactor) * -1
|
||||
val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, upTo)
|
||||
val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo)
|
||||
translateUp.duration = halfDuration
|
||||
translateUp.interpolator = LinearInterpolator()
|
||||
|
||||
val scaleDuration = halfDuration - halfDuration / 5
|
||||
val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scaleStart, properties.scaleEnd)
|
||||
val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scaleStart, properties.scaleEnd)
|
||||
val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale)
|
||||
val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale)
|
||||
val scale = AnimatorSet().apply {
|
||||
startDelay = scaleDuration
|
||||
duration = scaleDuration
|
||||
|
|
@ -103,34 +94,24 @@ fun View.leapBackAnimation(animDuration: Long, properties: AnimationProperties,
|
|||
playTogether(scaleX, scaleY)
|
||||
}
|
||||
|
||||
val elevation = ValueAnimator.ofFloat(properties.elevationStart, properties.elevationEnd)
|
||||
elevation.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: properties.elevationStart }
|
||||
elevation.startDelay = halfDuration
|
||||
elevation.duration = 100
|
||||
|
||||
val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo, properties.yTranslationEnd)
|
||||
val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd, halfDuration, 100)
|
||||
val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
|
||||
translateDown.startDelay = halfDuration
|
||||
translateDown.duration = halfDuration
|
||||
|
||||
return AnimatorSet().apply {
|
||||
playTogether(
|
||||
translateUp,
|
||||
scale,
|
||||
elevation,
|
||||
translateDown,
|
||||
)
|
||||
playTogether(translateUp, scale, elevation, translateDown)
|
||||
}
|
||||
}
|
||||
|
||||
fun View.pullUpAnimation(leapDuration: Long, properties: AnimationProperties): AnimatorSet {
|
||||
fun View.pullUpAnimation(leapDuration: Long, properties: LeapViewProperties): AnimatorSet {
|
||||
val pullDuration = leapDuration / 2
|
||||
val delayDuration = leapDuration / 2
|
||||
|
||||
val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, properties.yTranslationEnd)
|
||||
val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scaleStart, properties.scaleEnd)
|
||||
val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scaleStart, properties.scaleEnd)
|
||||
val elevation = ValueAnimator.ofFloat(properties.elevationStart, properties.elevationEnd)
|
||||
elevation.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: properties.elevationStart }
|
||||
val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
|
||||
val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale)
|
||||
val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale)
|
||||
val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd)
|
||||
|
||||
return AnimatorSet().apply {
|
||||
startDelay = delayDuration
|
||||
|
|
@ -140,15 +121,14 @@ fun View.pullUpAnimation(leapDuration: Long, properties: AnimationProperties): A
|
|||
}
|
||||
}
|
||||
|
||||
fun View.pullDownAnimation(leapDuration: Long, properties: AnimationProperties): AnimatorSet {
|
||||
fun View.pullDownAnimation(leapDuration: Long, properties: LeapViewProperties): AnimatorSet {
|
||||
val pullDuration = leapDuration / 2
|
||||
val delayDuration = leapDuration / 4
|
||||
|
||||
val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslationStart, properties.yTranslationEnd)
|
||||
val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scaleStart, properties.scaleEnd)
|
||||
val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scaleStart, properties.scaleEnd)
|
||||
val elevation = ValueAnimator.ofFloat(properties.elevationStart, properties.elevationEnd)
|
||||
elevation.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: properties.elevationStart }
|
||||
val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation)
|
||||
val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale)
|
||||
val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale)
|
||||
val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd)
|
||||
|
||||
return AnimatorSet().apply {
|
||||
startDelay = delayDuration
|
||||
|
|
@ -156,4 +136,12 @@ fun View.pullDownAnimation(leapDuration: Long, properties: AnimationProperties):
|
|||
interpolator = DecelerateInterpolator()
|
||||
playTogether(translate, scaleX, scaleY, elevation)
|
||||
}
|
||||
}
|
||||
|
||||
fun View.elevationAnimator(start: Float, end: Float, startDelay: Long? = null, duration: Long? = null): Animator {
|
||||
val elevationAnimator = ValueAnimator.ofFloat(start, end)
|
||||
elevationAnimator.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: start }
|
||||
startDelay?.let { elevationAnimator.startDelay = it }
|
||||
duration?.let { elevationAnimator.duration = it }
|
||||
return elevationAnimator
|
||||
}
|
||||
|
|
@ -12,35 +12,33 @@ class LeapView(
|
|||
private val maximalPosition: Int,
|
||||
private val calculator: PropertyCalculator,
|
||||
) {
|
||||
var currentProperties: AnimationProperties
|
||||
var state: LeapViewState
|
||||
private set
|
||||
|
||||
var currentPosition: Int = position
|
||||
private set
|
||||
var previousPosition: Int = position
|
||||
private set
|
||||
val initialState: LeapViewState
|
||||
|
||||
init {
|
||||
val initialProperties = createInitialProperty(position)
|
||||
currentProperties = AnimationProperties.from(initialProperties, initialProperties)
|
||||
initView(initialProperties)
|
||||
state = LeapViewState(index, position, position, initialProperties)
|
||||
initialState = state.copy()
|
||||
}
|
||||
|
||||
private fun initView(properties: Properties) {
|
||||
view.elevation = properties.elevation
|
||||
view.translationY = properties.yTranslation
|
||||
view.scaleX = properties.scale
|
||||
view.scaleY = properties.scale
|
||||
// initialProperties.hasForeground
|
||||
fun initView() {
|
||||
applyState(initialState)
|
||||
}
|
||||
|
||||
fun leap(): LeapFrogAnimation {
|
||||
previousPosition = currentPosition
|
||||
currentPosition = when (previousPosition) {
|
||||
val previousPosition = state.currentPosition
|
||||
val currentPosition = when (previousPosition) {
|
||||
0 -> maximalPosition
|
||||
else -> currentPosition - 1
|
||||
else -> state.currentPosition - 1
|
||||
}
|
||||
updateProperties(createLeapAnimationProperty(previousPosition, currentPosition))
|
||||
|
||||
state = state.copy(
|
||||
currentPosition = currentPosition,
|
||||
previousPosition = previousPosition,
|
||||
properties = createLeapAnimationProperty(previousPosition, currentPosition)
|
||||
)
|
||||
|
||||
return when {
|
||||
previousPosition == 0 && currentPosition == maximalPosition -> LeapFrogAnimation.LEAP
|
||||
|
|
@ -49,12 +47,16 @@ class LeapView(
|
|||
}
|
||||
|
||||
fun leapBack(): LeapFrogAnimation {
|
||||
previousPosition = currentPosition
|
||||
currentPosition = when (previousPosition) {
|
||||
val previousPosition = state.currentPosition
|
||||
val currentPosition = when (previousPosition) {
|
||||
maximalPosition -> 0
|
||||
else -> currentPosition + 1
|
||||
else -> state.currentPosition + 1
|
||||
}
|
||||
updateProperties(createLeapAnimationProperty(previousPosition, currentPosition))
|
||||
state = state.copy(
|
||||
currentPosition = currentPosition,
|
||||
previousPosition = previousPosition,
|
||||
properties = createLeapAnimationProperty(previousPosition, currentPosition)
|
||||
)
|
||||
|
||||
return when {
|
||||
previousPosition == maximalPosition && currentPosition == 0 -> LeapFrogAnimation.LEAP
|
||||
|
|
@ -63,66 +65,60 @@ class LeapView(
|
|||
}
|
||||
|
||||
fun fold() {
|
||||
updateProperties(currentProperties.toFold())
|
||||
state = state.copy(properties = state.properties.toFold()
|
||||
)
|
||||
}
|
||||
|
||||
fun unfold() {
|
||||
updateProperties(currentProperties.toUnfold(calculator))
|
||||
}
|
||||
|
||||
fun getState(): LeapViewState {
|
||||
return LeapViewState(index, currentPosition, previousPosition, currentProperties)
|
||||
state = state.copy(properties = state.properties.toUnfold(calculator))
|
||||
}
|
||||
|
||||
fun applyState(state: LeapViewState) {
|
||||
previousPosition = state.previousPosition
|
||||
currentPosition = state.currentPosition
|
||||
updateProperties(state.properties)
|
||||
initView(state.properties.endProperties())
|
||||
this.state = state
|
||||
setProperties(this.state.properties)
|
||||
}
|
||||
|
||||
private fun updateProperties(properties: AnimationProperties) {
|
||||
currentProperties = properties
|
||||
private fun setProperties(properties: LeapViewProperties) {
|
||||
view.elevation = properties.elevationEnd
|
||||
view.translationY = properties.yTranslation
|
||||
view.scaleX = properties.scale
|
||||
view.scaleY = properties.scale
|
||||
// initialProperties.hasForeground
|
||||
}
|
||||
|
||||
private fun createInitialProperty(endPosition: Int): Properties {
|
||||
return Properties(
|
||||
endPosition,
|
||||
calculator.scale(endPosition),
|
||||
calculator.elevation(endPosition, maximalPosition + 1),
|
||||
calculator.yTranslation(0),
|
||||
calculator.hasForeground(endPosition),
|
||||
private fun createInitialProperty(endPosition: Int): LeapViewProperties {
|
||||
return LeapViewProperties(
|
||||
endPosition,
|
||||
endPosition,
|
||||
calculator.scale(endPosition),
|
||||
calculator.elevation(endPosition, maximalPosition + 1),
|
||||
calculator.elevation(endPosition, maximalPosition + 1),
|
||||
calculator.yTranslation(0),
|
||||
calculator.hasForeground(endPosition),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createLeapAnimationProperty(startPosition: Int, endPosition: Int): AnimationProperties {
|
||||
return AnimationProperties(
|
||||
startPosition,
|
||||
endPosition,
|
||||
calculator.scale(startPosition),
|
||||
calculator.scale(endPosition),
|
||||
calculator.elevation(startPosition, maximalPosition + 1),
|
||||
calculator.elevation(endPosition, maximalPosition + 1),
|
||||
calculator.yTranslation(startPosition),
|
||||
calculator.yTranslation(endPosition),
|
||||
calculator.hasForeground(startPosition),
|
||||
calculator.hasForeground(endPosition),
|
||||
private fun createLeapAnimationProperty(startPosition: Int, endPosition: Int): LeapViewProperties {
|
||||
return LeapViewProperties(
|
||||
startPosition,
|
||||
endPosition,
|
||||
calculator.scale(endPosition),
|
||||
calculator.elevation(startPosition, maximalPosition + 1),
|
||||
calculator.elevation(endPosition, maximalPosition + 1),
|
||||
calculator.yTranslation(endPosition),
|
||||
calculator.hasForeground(endPosition),
|
||||
)
|
||||
}
|
||||
|
||||
private fun AnimationProperties.toUnfold(calculator: PropertyCalculator): AnimationProperties {
|
||||
val start = this.yTranslationEnd
|
||||
val end = calculator.yTranslation(this.positionEnd)
|
||||
return this.copy(yTranslationStart = start, yTranslationEnd = end)
|
||||
private fun LeapViewProperties.toUnfold(calculator: PropertyCalculator): LeapViewProperties {
|
||||
return this.copy(yTranslation = calculator.yTranslation(this.positionEnd))
|
||||
}
|
||||
|
||||
private fun AnimationProperties.toFold(): AnimationProperties {
|
||||
val start = this.yTranslationEnd
|
||||
val end = 0f
|
||||
return this.copy(yTranslationStart = start, yTranslationEnd = end)
|
||||
private fun LeapViewProperties.toFold(): LeapViewProperties {
|
||||
return this.copy(yTranslation = 0f)
|
||||
}
|
||||
|
||||
override fun toString(): String = "index: $index, position: $currentPosition, code: ${view.hashCode()}"
|
||||
override fun toString(): String = "index: $index, position: ${state.currentPosition}, code: ${view.hashCode()}"
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -130,62 +126,19 @@ data class LeapViewState(
|
|||
val index: Int,
|
||||
val currentPosition: Int,
|
||||
val previousPosition: Int,
|
||||
val properties: AnimationProperties
|
||||
val properties: LeapViewProperties
|
||||
)
|
||||
|
||||
enum class LeapFrogAnimation {
|
||||
LEAP, PULL
|
||||
}
|
||||
|
||||
data class Properties(
|
||||
val position: Int,
|
||||
val scale: Float,
|
||||
val elevation: Float,
|
||||
val yTranslation: Float,
|
||||
val hasForeground: Boolean,
|
||||
)
|
||||
|
||||
data class AnimationProperties(
|
||||
data class LeapViewProperties(
|
||||
val positionStart: Int,
|
||||
val positionEnd: Int,
|
||||
val scaleStart: Float,
|
||||
val scaleEnd: Float,
|
||||
val scale: Float,
|
||||
val elevationStart: Float,
|
||||
val elevationEnd: Float,
|
||||
val yTranslationStart: Float,
|
||||
val yTranslationEnd: Float,
|
||||
val hasForegroundStart: Boolean,
|
||||
val hasForegroundEnd: Boolean,
|
||||
) {
|
||||
|
||||
fun endProperties(): Properties {
|
||||
return Properties(
|
||||
positionEnd,
|
||||
scaleEnd,
|
||||
elevationEnd,
|
||||
yTranslationEnd,
|
||||
hasForegroundEnd,
|
||||
)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return """
|
||||
scaleStart: $scaleStart, scaleEnd: $scaleEnd,
|
||||
elevationStart: $elevationStart, elevationEnd: $elevationEnd,
|
||||
yTranslationStart: $yTranslationStart, yTranslationEnd: $yTranslationEnd,
|
||||
hasForegroundStart : $hasForegroundStart, hasForegroundEnd: $hasForegroundEnd,
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun from(start: Properties, end: Properties): AnimationProperties {
|
||||
return AnimationProperties(
|
||||
start.position, end.position,
|
||||
start.scale, end.scale,
|
||||
start.elevation, end.elevation,
|
||||
start.yTranslation, end.yTranslation,
|
||||
start.hasForeground, end.hasForeground
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
val yTranslation: Float,
|
||||
val hasForeground: Boolean,
|
||||
)
|
||||
|
|
@ -2,9 +2,9 @@ package com.tangem.tap.common.leapfrogWidget
|
|||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorSet
|
||||
import android.view.View
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.view.children
|
||||
import com.tangem.tangem_sdk_new.extensions.dpToPx
|
||||
|
||||
|
|
@ -25,60 +25,87 @@ class LeapfrogWidget(
|
|||
private var leapProgress: Int = 100
|
||||
private var leapBackProgress: Int = 100
|
||||
|
||||
private var isFolded: Boolean = true
|
||||
var isFolded: Boolean = true
|
||||
private set
|
||||
|
||||
init {
|
||||
calculator.setTranslationConverter { parentContainer.dpToPx(it) }
|
||||
init()
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
if (!viewIsFullFledged()) return
|
||||
|
||||
val maxPosition = parentContainer.childCount - 1
|
||||
parentContainer.children.forEachIndexed { index, view ->
|
||||
val initialPosition = maxPosition - index
|
||||
leapViews.add(LeapView(view, index, initialPosition, maxPosition, calculator))
|
||||
if (viewIsFullFledged()) {
|
||||
val maxPosition = parentContainer.childCount - 1
|
||||
parentContainer.children.forEachIndexed { index, view ->
|
||||
val initialPosition = maxPosition - index
|
||||
leapViews.add(LeapView(view, index, initialPosition, maxPosition, calculator))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fold(listener: ProgressListener? = null) {
|
||||
if (!canFoldUnfold() || isFolded) return
|
||||
|
||||
createFoldUnfoldAnimation(true, foldAnimationDuration, listener)
|
||||
/**
|
||||
* Apply initial properties to the views
|
||||
*/
|
||||
fun initViews() {
|
||||
isFolded = true
|
||||
leapViews.forEach { it.initView() }
|
||||
}
|
||||
|
||||
fun unfold(listener: ProgressListener? = null) {
|
||||
if (!canFoldUnfold() || !isFolded) return
|
||||
|
||||
createFoldUnfoldAnimation(false, foldAnimationDuration, listener)
|
||||
fun fold(onEnd: () -> Unit = {}) {
|
||||
val animator = foldAnimator()
|
||||
if (animator == null) {
|
||||
onEnd()
|
||||
} else {
|
||||
animator.doOnEnd { onEnd() }
|
||||
animator.start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFoldUnfoldAnimation(isFoldAnimation: Boolean, duration: Long, listener: ProgressListener?) {
|
||||
fun unfold(onEnd: () -> Unit = {}) {
|
||||
val animator = unfoldAnimator()
|
||||
if (animator == null) {
|
||||
onEnd()
|
||||
} else {
|
||||
animator.doOnEnd { onEnd() }
|
||||
animator.start()
|
||||
}
|
||||
}
|
||||
|
||||
fun foldAnimator(): Animator? {
|
||||
if (!canFoldUnfold() || isFolded) return null
|
||||
|
||||
return createFoldUnfoldAnimator(true, foldAnimationDuration)
|
||||
}
|
||||
|
||||
fun unfoldAnimator(): Animator? {
|
||||
if (!canFoldUnfold() || !isFolded) return null
|
||||
|
||||
return createFoldUnfoldAnimator(false, foldAnimationDuration)
|
||||
}
|
||||
|
||||
private fun createFoldUnfoldAnimator(isFoldAnimation: Boolean, duration: Long): Animator {
|
||||
val animatorsList = mutableListOf<Animator>()
|
||||
leapViews.forEach {
|
||||
if (isFoldAnimation) {
|
||||
it.fold()
|
||||
animatorsList.add(it.view.foldAnimation(duration, it.currentProperties))
|
||||
animatorsList.add(it.view.foldAnimation(duration, it.state.properties))
|
||||
} else {
|
||||
it.unfold()
|
||||
animatorsList.add(it.view.unfoldAnimation(duration, it.currentProperties))
|
||||
animatorsList.add(it.view.unfoldAnimation(duration, it.state.properties))
|
||||
}
|
||||
}
|
||||
animatorsList.add(createProgressListener(duration) {
|
||||
foldUnfoldProgress = it
|
||||
if (it == 100) isFolded = isFoldAnimation
|
||||
})
|
||||
animatorsList.add(createProgressListener(duration, listener))
|
||||
|
||||
val animator = AnimatorSet()
|
||||
animator.interpolator = AccelerateDecelerateInterpolator()
|
||||
animator.playTogether(animatorsList.toList())
|
||||
animator.start()
|
||||
return animator
|
||||
}
|
||||
|
||||
fun leap(listener: ProgressListener? = null) {
|
||||
if (!canLeap()) return
|
||||
fun leap(onEnd: () -> Unit = {}) {
|
||||
if (!canLeap()) {
|
||||
onEnd()
|
||||
return
|
||||
}
|
||||
|
||||
val duration = leapAnimationDuration
|
||||
val animatorsList = mutableListOf<Animator>()
|
||||
|
|
@ -86,48 +113,54 @@ class LeapfrogWidget(
|
|||
when (it.leap()) {
|
||||
LeapFrogAnimation.LEAP -> {
|
||||
val overLift = calculator.overLift(leapViews.size)
|
||||
animatorsList.add(it.view.leapAnimation(duration, it.currentProperties, overLift))
|
||||
animatorsList.add(it.view.leapAnimation(duration, it.state.properties, overLift))
|
||||
}
|
||||
LeapFrogAnimation.PULL -> {
|
||||
animatorsList.add(it.view.pullUpAnimation(duration, it.currentProperties))
|
||||
animatorsList.add(it.view.pullUpAnimation(duration, it.state.properties))
|
||||
}
|
||||
}
|
||||
}
|
||||
animatorsList.add(createProgressListener(duration) { leapProgress = it })
|
||||
animatorsList.add(createProgressListener(duration, listener))
|
||||
|
||||
val animator = AnimatorSet()
|
||||
animator.playTogether(animatorsList.toList())
|
||||
animator.doOnEnd { onEnd() }
|
||||
animator.start()
|
||||
}
|
||||
|
||||
fun leapBack(listener: ProgressListener? = null) {
|
||||
if (!canLeapBack()) return
|
||||
fun leapBack(onEnd: () -> Unit = {}) {
|
||||
if (!canLeapBack()) {
|
||||
onEnd()
|
||||
return
|
||||
}
|
||||
|
||||
val duration = leapBackAnimationDuration
|
||||
val animatorsList = mutableListOf<Animator>()
|
||||
leapViews.forEach {
|
||||
when (it.leapBack()) {
|
||||
LeapFrogAnimation.LEAP -> {
|
||||
animatorsList.add(it.view.leapBackAnimation(duration, it.currentProperties, calculator))
|
||||
animatorsList.add(it.view.leapBackAnimation(duration, it.state.properties, calculator))
|
||||
}
|
||||
LeapFrogAnimation.PULL -> {
|
||||
animatorsList.add(it.view.pullDownAnimation(duration, it.currentProperties))
|
||||
animatorsList.add(it.view.pullDownAnimation(duration, it.state.properties))
|
||||
}
|
||||
}
|
||||
}
|
||||
animatorsList.add(createProgressListener(duration) { leapBackProgress = it })
|
||||
animatorsList.add(createProgressListener(duration, listener))
|
||||
|
||||
val animator = AnimatorSet()
|
||||
animator.playTogether(animatorsList.toList())
|
||||
animator.doOnEnd { onEnd() }
|
||||
animator.start()
|
||||
}
|
||||
|
||||
fun getViewByPosition(position: Int): View? {
|
||||
return leapViews.firstOrNull { it.currentPosition == position }?.view
|
||||
}
|
||||
fun getViewsCount(): Int = parentContainer.childCount
|
||||
|
||||
fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(isFolded, leapViews.map { it.getState() })
|
||||
fun getViewPositionByIndex(index: Int): Int = leapViews.first { it.index == index }.state.currentPosition
|
||||
|
||||
fun getViewByPosition(position: Int): LeapView = leapViews.first { it.state.currentPosition == position }
|
||||
|
||||
fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(isFolded, leapViews.map { it.state })
|
||||
|
||||
fun applyState(state: LeapfrogWidgetState) {
|
||||
isFolded = state.isFolded
|
||||
|
|
@ -179,11 +212,11 @@ data class LeapfrogWidgetState(
|
|||
|
||||
class PropertyCalculator(
|
||||
val elevationFactor: Float = 1f,
|
||||
val decreaseScaleFactor: Float = 0.1f,
|
||||
val yTranslationFactor: Float = 20f,
|
||||
val decreaseScaleFactor: Float = 0.2f,
|
||||
val yTranslationFactor: Float = -40f,
|
||||
val decreaseOverLiftingFactor: Float = 0.85f,
|
||||
) {
|
||||
// 1st view it is view on the top of stack, but it is last in parent.children
|
||||
// 0 view it is view on the top of stack, but it is last in parent.children
|
||||
|
||||
private var translationValueConverter: ((Float) -> Float)? = null
|
||||
fun setTranslationConverter(converter: (Float) -> Float) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue