Skip to content

Instantly share code, notes, and snippets.

@kaeawc
Created January 17, 2020 16:35
Show Gist options
  • Save kaeawc/9e226ef5096a8f798d8a0566879f0d01 to your computer and use it in GitHub Desktop.
Save kaeawc/9e226ef5096a8f798d8a0566879f0d01 to your computer and use it in GitHub Desktop.
MotionLayout extension functions to help aid transitions and debugging
import android.content.Context
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.motion.widget.MotionScene
import timber.log.Timber
fun MotionLayout.after(completion: () -> Unit) {
Timber.v("Started listening, currently at ${currentState.resName(context)}")
this.setTransitionListener(object: MotionLayout.TransitionListener {
override fun onTransitionTrigger(layout: MotionLayout?, startId: Int, something: Boolean, progress: Float) {}
override fun onTransitionStarted(layout: MotionLayout?, startId: Int, endId: Int) {
layout?.context?.let { context ->
Timber.d("Started from ${startId.resName(context)} -> ${layout.currentState.resName(context)} -> to ${endId.resName(context)}")
}
}
override fun onTransitionChange(layout: MotionLayout?, startId: Int, endId: Int, progress: Float) {
layout?.context?.let { context ->
Timber.v("Changing from ${startId.resName(context)} -> ${layout.currentState.resName(context)} -> to ${endId.resName(context)}")
}
}
override fun onTransitionCompleted(layout: MotionLayout?, currentId: Int) {
layout?.context?.let { context ->
Timber.d("Completed at ${currentId.resName(context)}")
}
completion()
}
override fun allowsTransition(transition: MotionScene.Transition?): Boolean = true
})
}
fun MotionLayout.stopListening() {
this.setTransitionListener(object: MotionLayout.TransitionListener {
override fun onTransitionTrigger(layout: MotionLayout?, startId: Int, something: Boolean, progress: Float) {}
override fun onTransitionStarted(layout: MotionLayout?, startId: Int, endId: Int) {}
override fun onTransitionChange(layout: MotionLayout?, startId: Int, endId: Int, progress: Float) {}
override fun onTransitionCompleted(layout: MotionLayout?, currentId: Int) {}
override fun allowsTransition(transition: MotionScene.Transition?): Boolean = true
})
}
fun MotionLayout.setActionListeners(vararg listeners: MotionLayoutActionListener) {
stopListening()
var currentStartScene = 0
this.setTransitionListener(object: MotionLayout.TransitionListener {
override fun onTransitionTrigger(layout: MotionLayout?, startId: Int, something: Boolean, progress: Float) {}
override fun onTransitionStarted(layout: MotionLayout?, startId: Int, endId: Int) {
currentStartScene = startId
}
override fun onTransitionChange(layout: MotionLayout?, startId: Int, endId: Int, progress: Float) {}
override fun onTransitionCompleted(layout: MotionLayout?, currentId: Int) {
listeners.forEach { it.performActionIfCorrectTransition(currentStartScene, currentId) }
}
override fun allowsTransition(transition: MotionScene.Transition?): Boolean = true
})
}
class MotionLayoutActionListener(
private val startScene: Int,
private val endScene: Int,
val action: () -> Unit
) {
fun performActionIfCorrectTransition(start: Int, end: Int) {
if (startScene == start && endScene == end) action()
}
}
fun Int.resName(context: Context?): String {
return if (this == -1) {
"moving"
} else try {
context?.resources?.getResourceEntryName(this) ?: "unknown"
} catch (ex: Throwable) {
"unknown"
}
}
fun MotionLayout.transitionTo(currentState: Int, nextState: Int) {
Timber.v("transitionTo ${currentState.resName(context)} -> ${nextState.resName(context)}, currently at ${currentState.resName(context)}")
setTransition(currentState, nextState)
progress = 0f
transitionToEnd()
}
fun MotionLayout.goTo(next: Int, immediately: Boolean = false, completion: () -> Unit = {}) {
val currentMotion = currentState
val start = startState
val end = endState
stopListening()
after(completion)
when (currentMotion) {
-1 -> when (end) {
-1 -> {
stopListening()
completion()
}
next -> transitionToEnd()
else -> transitionTo(end, next)
}
start -> when (end) {
next -> transitionToEnd()
else -> transitionTo(start, next)
}
end -> when (end) {
next -> {
stopListening()
completion()
}
else -> transitionTo(end, next)
}
}
if (immediately) {
progress = 1f
}
}
fun MotionLayout.goTo(next: Int, delay: Long, immediately: Boolean = false, completion: () -> Unit = {}) {
stopListening()
if (needsTransition(next)) {
postDelayed({
goTo(next, immediately, completion)
}, delay)
} else {
goTo(next, immediately, completion)
}
}
fun MotionLayout.needsTransition(destination: Int): Boolean {
val end = endState
return when (currentState) {
-1 -> end != destination
startState -> true
end -> end != destination
else -> false
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment