From 039914e0029fac994d289cb254010e61ff79275c Mon Sep 17 00:00:00 2001 From: Simon Featherstone Date: Wed, 20 Apr 2022 15:00:43 +0100 Subject: [PATCH] Added PureComposeRibView --- .../badoo/ribs/compose/PureComposeRibView.kt | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 libraries/rib-compose/src/main/java/com/badoo/ribs/compose/PureComposeRibView.kt diff --git a/libraries/rib-compose/src/main/java/com/badoo/ribs/compose/PureComposeRibView.kt b/libraries/rib-compose/src/main/java/com/badoo/ribs/compose/PureComposeRibView.kt new file mode 100644 index 000000000..0f8f56a24 --- /dev/null +++ b/libraries/rib-compose/src/main/java/com/badoo/ribs/compose/PureComposeRibView.kt @@ -0,0 +1,70 @@ +package com.badoo.ribs.compose + +import android.content.Context +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.compose.runtime.MutableState +import androidx.compose.ui.viewinterop.AndroidView +import com.badoo.ribs.android.AndroidRibViewHost +import com.badoo.ribs.core.Node +import com.badoo.ribs.core.view.RibView + +/** + * Prevents wrapping of Compose children in [FrameLayout] which break Compose context + * (for instance breaking Modifier.nestedScroll). Can cause problems with transitions. + * + * @param context Android context used to create the required views + */ +abstract class PureComposeRibView( + override val context: Context +) : RibView { + + abstract val composable: ComposeView + + override val androidView: ViewGroup by lazy { + androidx.compose.ui.platform.ComposeView(context).apply { + setContent(composable) + } + } + + private var lastChildAttached: MutableMap> = mutableMapOf() + + abstract fun getParentStateForSubtree(subtreeOf: Node<*>): MutableState + + override fun attachChild(child: Node<*>, subtreeOf: Node<*>) { + val target: MutableState = getParentStateForSubtree(subtreeOf) + lastChildAttached[target] = child + + when (val childView = child.onCreateView(this)) { + is PureComposeRibView -> { + child.onAttachToView() + target.value = childView.composable + } + + is ComposeRibView -> { + child.onAttachToView() + target.value = childView.composable + } + + else -> { + val innerContainer = FrameLayout(context) + AndroidRibViewHost(innerContainer).attachChild(child) + target.value = { AndroidView(factory = { innerContainer }) } + } + } + } + + override fun detachChild(child: Node<*>, subtreeOf: Node<*>) { + child.onDetachFromView() + val target: MutableState = getParentStateForSubtree(subtreeOf) + + // Only detach the same child, or we would remove something unintended. + // If there was already another child attached to the same target, then the MutableState + // of lastChildAttached[target] was overwritten, and the related ComposeView + // was already removed from the composition as a result, and no further action is needed. + if (child == lastChildAttached[target]) { + target.value = null + lastChildAttached.remove(target) + } + } +}