Last active
December 9, 2019 11:31
-
-
Save jossiwolf/f38532cae54bb979cb032e94be35231d to your computer and use it in GitHub Desktop.
Enhanced MotionLayout capabilities such as being able to add multiple listeners and disable touch and await animations using coroutines. Adapted from https://github.com/chrisbanes/tivi/blob/master/common-ui/src/main/java/app/tivi/ui/widget/TiviMotionLayout.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class EnhancedMotionLayout @JvmOverloads constructor( | |
context: Context, | |
attrs: AttributeSet? = null, | |
defStyleAttr: Int = 0 | |
) : MotionLayout(context, attrs, defStyleAttr) { | |
private val listeners = CopyOnWriteArrayList<TransitionListener>() | |
fun addTransitionListener(listener: TransitionListener) { | |
listeners.addIfAbsent(listener) | |
} | |
fun removeTransitionListener(listener: TransitionListener) { | |
listeners.remove(listener) | |
} | |
init { | |
super.setTransitionListener(object : TransitionListener { | |
override fun onTransitionTrigger(motionLayout: MotionLayout, triggerId: Int, positive: Boolean, progress: Float) { | |
listeners.forEach { | |
it.onTransitionTrigger(motionLayout, triggerId, positive, progress) | |
} | |
} | |
override fun onTransitionStarted(motionLayout: MotionLayout, startId: Int, endId: Int) { | |
listeners.forEach { | |
it.onTransitionStarted(motionLayout, startId, endId) | |
} | |
} | |
override fun onTransitionChange(motionLayout: MotionLayout, startId: Int, endId: Int, progress: Float) { | |
listeners.forEach { | |
it.onTransitionChange(motionLayout, startId, endId, progress) | |
} | |
} | |
override fun onTransitionCompleted(motionLayout: MotionLayout, currentId: Int) { | |
listeners.forEach { | |
it.onTransitionCompleted(motionLayout, currentId) | |
} | |
} | |
}) | |
} | |
@Deprecated("Use addTransitionListener instead") | |
override fun setTransitionListener(listener: TransitionListener) { | |
throw IllegalArgumentException("Use addTransitionListener instead") | |
} | |
/** | |
* Whether this MotionLayout should react to nested scrolls and touch events | |
*/ | |
var motionEnabled = true | |
override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int): Boolean { | |
return motionEnabled && super.onStartNestedScroll(child, target, axes, type) | |
} | |
override fun onInterceptTouchEvent(event: MotionEvent): Boolean { | |
if (event.action == MotionEvent.ACTION_DOWN && !motionEnabled) { | |
return false | |
} | |
return super.onInterceptTouchEvent(event) | |
} | |
override fun onTouchEvent(event: MotionEvent): Boolean { | |
if (event.action == MotionEvent.ACTION_DOWN && !motionEnabled) { | |
return false | |
} | |
return super.onTouchEvent(event) | |
} | |
} | |
/** | |
* Wait for the transition to complete so that the given [transitionId] is fully displayed. | |
*/ | |
suspend fun EnhancedMotionLayout.transitionTo(@IdRes transitionId: Int) { | |
// If we're already at the specified state, return now | |
if (currentState == transitionId) return | |
suspendCancellableCoroutine<Unit> { cont -> | |
val listener = object : TransitionAdapter() { | |
override fun onTransitionCompleted(motionLayout: MotionLayout, currentId: Int) { | |
if (currentId == transitionId) { | |
removeTransitionListener(this) | |
cont.resume(Unit) | |
} | |
} | |
} | |
// If the coroutine is cancelled, remove the listener | |
cont.invokeOnCancellation { | |
removeTransitionListener(listener) | |
// TODO maybe reverse the current transition? | |
} | |
// And finally add the listener | |
addTransitionListener(listener) | |
transitionToState(transitionId) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment