diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-03-16 22:49:51 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2023-03-16 22:49:51 +0000 |
commit | 497e3d8cb03a99657c4f51c6b9f7317f57b5f15a (patch) | |
tree | 69e9be0dcd410934ff158809d5173a73bdce1b97 | |
parent | 8b6d82fa81cf938a709c55cc97db95c16f0d5715 (diff) | |
parent | 9a1247639be7542ead7263f92de3a54a064817fb (diff) | |
download | support-497e3d8cb03a99657c4f51c6b9f7317f57b5f15a.tar.gz |
Merge "Merge cherrypicks of ['android-review.googlesource.com/2472949', 'android-review.googlesource.com/2494919'] into androidx-compose-integration-release." into androidx-compose-integration-release
4 files changed, 132 insertions, 2 deletions
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt index 71f542b250b..925bd713b52 100644 --- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt +++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt @@ -18,6 +18,7 @@ package androidx.compose.ui.layout import android.annotation.SuppressLint import android.os.Build +import android.view.View import android.widget.FrameLayout import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints @@ -52,6 +53,7 @@ import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asAndroidBitmap +import androidx.compose.ui.layout.RootMeasurePolicy.measure import androidx.compose.ui.platform.AndroidOwnerExtraAssertionsRule import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalDensity @@ -71,6 +73,7 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.zIndex import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest @@ -2351,6 +2354,40 @@ class SubcomposeLayoutTest { .assertExists() } + // Regression test of b/271156218 + @Test + fun deactivatingDeeplyNestedAndroidViewDoesNotCauseRemeasure() { + var showContent by mutableStateOf(true) + val state = SubcomposeLayoutState(SubcomposeSlotReusePolicy(1)) + rule.setContent { + SubcomposeLayout( + state = state, + modifier = Modifier.fillMaxSize() + ) { constraints -> + val content = if (showContent) { + subcompose(0) { + Box { + AndroidView(::View, Modifier.fillMaxSize().testTag("AndroidView")) + } + } + } else emptyList() + + val placeables = measure(content, constraints) + layout(100, 100) { + placeables.placeChildren() + } + } + } + + rule.onNodeWithTag("AndroidView").assertExists() + + rule.runOnIdle { showContent = false } + rule.onNodeWithTag("AndroidView").assertIsNotDisplayed() + + rule.runOnIdle { showContent = true } + rule.onNodeWithTag("AndroidView").assertExists() + } + private fun composeItems( state: SubcomposeLayoutState, items: MutableState<List<Int>> diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt index 2084e711f38..0ba55a45e78 100644 --- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt +++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/modifier/ModifierNodeReuseAndDeactivationTest.kt @@ -37,7 +37,10 @@ import androidx.compose.ui.node.LayoutModifierNode import androidx.compose.ui.node.ModifierNodeElement import androidx.compose.ui.node.ObserverNode import androidx.compose.ui.node.observeReads +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.unit.Constraints import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest @@ -499,6 +502,47 @@ class ModifierNodeReuseAndDeactivationTest { assertThat(receivedValue).isEqualTo(1) } } + + @Test + fun placingChildWithReusedUnchangedModifier() { + // regression test for b/271430143 + var active by mutableStateOf(true) + var modifier by mutableStateOf(StatelessLayoutElement1.then(StatelessLayoutElement2)) + var childX by mutableStateOf(0) + + rule.setContent { + ReusableContentHost(active) { + ReusableContent(0) { + Layout(content = { + Layout( + modifier = modifier.testTag("child"), + measurePolicy = MeasurePolicy + ) + }) { measurables, constraints -> + val placeable = measurables.first().measure(constraints) + layout(placeable.width, placeable.height) { + childX.toString() + placeable.place(childX, 0) + } + } + } + } + } + + rule.runOnIdle { + active = false + } + + rule.runOnIdle { + active = true + modifier = StatelessLayoutElement1 + // force relayout parent + childX = 10 + } + + rule.onNodeWithTag("child") + .assertLeftPositionInRootIsEqualTo(with(rule.density) { 10.toDp() }) + } } @Composable @@ -657,3 +701,41 @@ private data class ObserverModifierElement( } } } + +private object StatelessLayoutElement1 : ModifierNodeElement<StatelessLayoutModifier1>() { + override fun create() = StatelessLayoutModifier1() + override fun update(node: StatelessLayoutModifier1) = node + override fun hashCode(): Int = 241 + override fun equals(other: Any?) = other === this +} + +private class StatelessLayoutModifier1 : Modifier.Node(), LayoutModifierNode { + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints + ): MeasureResult { + val placeable = measurable.measure(constraints) + return layout(placeable.width, placeable.height) { + placeable.place(0, 0) + } + } +} + +private object StatelessLayoutElement2 : ModifierNodeElement<StatelessLayoutModifier2>() { + override fun create() = StatelessLayoutModifier2() + override fun update(node: StatelessLayoutModifier2) = node + override fun hashCode(): Int = 242 + override fun equals(other: Any?) = other === this +} + +private class StatelessLayoutModifier2 : Modifier.Node(), LayoutModifierNode { + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints + ): MeasureResult { + val placeable = measurable.measure(constraints) + return layout(placeable.width, placeable.height) { + placeable.place(0, 0) + } + } +}
\ No newline at end of file diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt index 258b7171ea5..bd6bf08f5f6 100644 --- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt +++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/viewinterop/AndroidViewHolder.android.kt @@ -204,7 +204,7 @@ internal open class AndroidViewHolder( override fun onDeactivate() { reset() - removeView(view) + removeAllViewsInLayout() } override fun onRelease() { @@ -212,6 +212,13 @@ internal open class AndroidViewHolder( } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + if (view?.parent !== this) { + setMeasuredDimension( + MeasureSpec.getSize(widthMeasureSpec), + MeasureSpec.getSize(heightMeasureSpec) + ) + return + } view?.measure(widthMeasureSpec, heightMeasureSpec) setMeasuredDimension(view?.measuredWidth ?: 0, view?.measuredHeight ?: 0) lastWidthMeasureSpec = widthMeasureSpec @@ -339,6 +346,10 @@ internal open class AndroidViewHolder( measurables: List<Measurable>, constraints: Constraints ): MeasureResult { + if (childCount == 0) { + return layout(constraints.minWidth, constraints.minHeight) {} + } + if (constraints.minWidth != 0) { getChildAt(0).minimumWidth = constraints.minWidth } diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt index a01e089bd03..e6eeda92dc1 100644 --- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt +++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/NodeChain.kt @@ -241,7 +241,7 @@ internal class NodeChain(val layoutNode: LayoutNode) { var node: Modifier.Node? = tail.parent while (node != null) { if (node.isKind(Nodes.Layout) && node is LayoutModifierNode) { - val next = if (node.isAttached) { + val next = if (node.coordinator != null) { val c = node.coordinator as LayoutModifierNodeCoordinator val prevNode = c.layoutModifierNode c.layoutModifierNode = node |