From 8f9f6d27cc256792327afc0fc32b5e54b898834c Mon Sep 17 00:00:00 2001 From: George Mount Date: Thu, 20 Jun 2019 08:59:16 -0700 Subject: Fix: adding elements didn't cause remeasure. Fixes: 135668890 When a new element was added through composition, the containing layout didn't remeasure and relayout that child. This CL asks the parent to remeasure and relayout when a child is added or removed. Test: new test Change-Id: Ida8f71f9f6a159be79767c04b96ffc1d2c42b3fe --- .../androidx/ui/core/test/AndroidLayoutDrawTest.kt | 50 +++++++++++++++++++++- ui/platform/api/1.0.0-alpha01.txt | 1 - ui/platform/api/current.txt | 1 - .../src/main/java/androidx/ui/core/AndroidOwner.kt | 25 +++++++---- .../main/java/androidx/ui/core/ComponentNodes.kt | 18 ++++++-- 5 files changed, 79 insertions(+), 16 deletions(-) diff --git a/ui/framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt b/ui/framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt index 52cf3dc761a..3d4ff9abfbe 100644 --- a/ui/framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt +++ b/ui/framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt @@ -665,6 +665,51 @@ class AndroidLayoutDrawTest { } } + // When a new child is added, the parent must be remeasured because we don't know + // if it affects the size and the child's measure() must be called as well. + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O) + @Test + fun testRelayoutOnNewChild() { + val drawChild = DoDraw() + + val outerColor = Color(0xFF000080.toInt()) + val innerColor = Color(0xFFFFFFFF.toInt()) + runOnUiThread { + activity.setContent { + CraneWrapper { + AtLeastSize(size = 30.ipx) { + Draw { canvas, parentSize -> + drawLatch.countDown() + val paint = Paint() + paint.color = outerColor + canvas.drawRect(parentSize.toRect(), paint) + } + if (drawChild.value) { + Padding(size = 20.ipx) { + AtLeastSize(size = 20.ipx) { + Draw { canvas, parentSize -> + drawLatch.countDown() + val paint = Paint() + paint.color = innerColor + canvas.drawRect(parentSize.toRect(), paint) + } + } + } + } + } + } + } + } + + // The padded area doesn't draw + validateSquareColors(outerColor = outerColor, innerColor = outerColor, size = 10) + + drawLatch = CountDownLatch(1) + runOnUiThread { drawChild.value = true } + + validateSquareColors(outerColor = outerColor, innerColor = innerColor, size = 20) + } + // We only need this because IR compiler doesn't like converting lambdas to Runnables private fun runOnUiThread(block: () -> Unit) { val runnable: Runnable = object : Runnable { @@ -1044,4 +1089,7 @@ class SquareModel( ) @Model -class OffsetModel(var offset: IntPx) \ No newline at end of file +class OffsetModel(var offset: IntPx) + +@Model +class DoDraw(var value: Boolean = false) diff --git a/ui/platform/api/1.0.0-alpha01.txt b/ui/platform/api/1.0.0-alpha01.txt index 579dd35cc91..24d3185a20c 100644 --- a/ui/platform/api/1.0.0-alpha01.txt +++ b/ui/platform/api/1.0.0-alpha01.txt @@ -21,7 +21,6 @@ package androidx.ui.core { method public void onSizeChange(androidx.ui.core.LayoutNode layoutNode); method public void onStartLayout(androidx.ui.core.LayoutNode layoutNode); method public void onStartMeasure(androidx.ui.core.LayoutNode layoutNode); - method public void requestRelayout(androidx.ui.core.LayoutNode layoutNode); method public void sendEvent(android.view.MotionEvent event); method public void setCommitUnsubscribe(kotlin.jvm.functions.Function0? p); method public void setConstraints(androidx.ui.core.Constraints p); diff --git a/ui/platform/api/current.txt b/ui/platform/api/current.txt index 579dd35cc91..24d3185a20c 100644 --- a/ui/platform/api/current.txt +++ b/ui/platform/api/current.txt @@ -21,7 +21,6 @@ package androidx.ui.core { method public void onSizeChange(androidx.ui.core.LayoutNode layoutNode); method public void onStartLayout(androidx.ui.core.LayoutNode layoutNode); method public void onStartMeasure(androidx.ui.core.LayoutNode layoutNode); - method public void requestRelayout(androidx.ui.core.LayoutNode layoutNode); method public void sendEvent(android.view.MotionEvent event); method public void setCommitUnsubscribe(kotlin.jvm.functions.Function0? p); method public void setConstraints(androidx.ui.core.Constraints p); diff --git a/ui/platform/src/main/java/androidx/ui/core/AndroidOwner.kt b/ui/platform/src/main/java/androidx/ui/core/AndroidOwner.kt index d042f491d72..1ffe8c11daf 100644 --- a/ui/platform/src/main/java/androidx/ui/core/AndroidOwner.kt +++ b/ui/platform/src/main/java/androidx/ui/core/AndroidOwner.kt @@ -198,11 +198,20 @@ class AndroidCraneView constructor(context: Context) override fun onRequestMeasure(layoutNode: LayoutNode) { // find root of layout request: + if (layoutNode.needsRemeasure) { + // don't need to do anything because it already needs to be remeasured + return + } layoutNode.needsRemeasure = true var layout = layoutNode - while (layout.parentLayoutNode != null && layout.affectsParentSize) { + while (layout.parentLayoutNode != null && layout.parentLayoutNode != root && + layout.affectsParentSize) { layout = layout.parentLayoutNode!! + if (layout.needsRemeasure) { + // don't need to do anything else since the parent already needs measuring + return + } layout.needsRemeasure = true } @@ -214,8 +223,8 @@ class AndroidCraneView constructor(context: Context) } } - fun requestRelayout(layoutNode: LayoutNode) { - if (layoutNode == root) { + private fun requestRelayout(layoutNode: LayoutNode) { + if (layoutNode == root || constraints.isZero) { requestLayout() } else if (relayoutNodes.isEmpty()) { Choreographer.getInstance().postFrameCallback { @@ -259,13 +268,11 @@ class AndroidCraneView constructor(context: Context) relayoutNodes.sortedBy { it.depth }.forEach { layoutNode -> if (layoutNode.needsRemeasure) { val parent = layoutNode.parentLayoutNode - if (parent != null) { + if (parent != null && parent.layout != null) { // This should call measure and layout on the child - val parentLayout = parent.layout - if (parentLayout != null) { - parent.needsRelayout = true - parentLayout.callLayout() - } + val parentLayout = parent.layout!! + parent.needsRelayout = true + parentLayout.callLayout() } else { layoutNode.layout?.callMeasure(layoutNode.constraints) layoutNode.layout?.callLayout() diff --git a/ui/platform/src/main/java/androidx/ui/core/ComponentNodes.kt b/ui/platform/src/main/java/androidx/ui/core/ComponentNodes.kt index 04e4607ba29..72ba120ab28 100644 --- a/ui/platform/src/main/java/androidx/ui/core/ComponentNodes.kt +++ b/ui/platform/src/main/java/androidx/ui/core/ComponentNodes.kt @@ -394,7 +394,7 @@ class LayoutNode : ComponentNode() { /** * `true` when the parent's size depends on this LayoutNode's size */ - var affectsParentSize: Boolean = false + var affectsParentSize: Boolean = true /** * `true` when called between [startMeasure] and [endMeasure] @@ -405,14 +405,14 @@ class LayoutNode : ComponentNode() { * `true` when the layout has been dirtied by [requestRemeasure]. `false` after * the measurement has been complete ([resize] has been called). */ - var needsRemeasure = true + var needsRemeasure = false internal set /** * `true` when the layout has been measured or dirtied because the layout * lambda accessed a model that has been dirtied. */ - var needsRelayout = true + var needsRelayout = false internal set override val parentLayoutNode: LayoutNode? @@ -421,6 +421,16 @@ class LayoutNode : ComponentNode() { override val containingLayoutNode: LayoutNode? get() = this + override fun attach(owner: Owner) { + super.attach(owner) + requestRemeasure() + } + + override fun detach() { + parentLayoutNode?.requestRemeasure() + super.detach() + } + fun moveTo(x: IntPx, y: IntPx) { visible = true if (x != this.x || y != this.y) { @@ -432,7 +442,6 @@ class LayoutNode : ComponentNode() { fun resize(width: IntPx, height: IntPx) { val parent = parentLayoutNode - needsRemeasure = false // we must have just finished measurement if (parent != null && parent.isInMeasure) { affectsParentSize = true } @@ -461,6 +470,7 @@ class LayoutNode : ComponentNode() { fun endMeasure() { owner?.onEndMeasure(this) isInMeasure = false + needsRemeasure = false needsRelayout = true } -- cgit v1.2.3