Skip to content

Commit 21e2445

Browse files
NickGerlemanhuntie
authored andcommitted
Top-down onLayout events (#39644)
Summary: Pull Request resolved: #39644 This makes Android Paper/Classic renderer fire `onLayout` events top down, like in Fabric/new Architecture. This gives a much more sane model for using layout events to calculate bottom/right-edge insets. I was under the impression that Paper in general was bottom-up, but it turns out that is only true for Android and Windows (iOS seems totally deterministic). This is a behavior change, but to my knowledge was never hit during the Fabric migration, and any JS code already written for both Android and iOS cannot make assumptions here anyways. Changelog: [General][Changed] - Make layout events top-down on Android classic renderer Reviewed By: mdvacca Differential Revision: D49627996 fbshipit-source-id: 29964b421dd420681d45348c7db16f211a6c087f
1 parent 4a4fb12 commit 21e2445

File tree

3 files changed

+60
-31
lines changed

3 files changed

+60
-31
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,9 @@ public interface ReactShadowNode<T extends ReactShadowNode> {
123123
*/
124124
void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue);
125125

126-
/** @return true if layout (position or dimensions) changed, false otherwise. */
126+
/* package */ boolean dispatchUpdatesWillChangeLayout(float absoluteX, float absoluteY);
127127

128-
/* package */ boolean dispatchUpdates(
128+
/* package */ void dispatchUpdates(
129129
float absoluteX,
130130
float absoluteY,
131131
UIViewOperationQueue uiViewOperationQueue,

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,32 @@ public void onAfterUpdateTransaction() {
337337
@Override
338338
public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {}
339339

340-
/** @return true if layout (position or dimensions) changed, false otherwise. */
341340
@Override
342-
public boolean dispatchUpdates(
341+
public boolean dispatchUpdatesWillChangeLayout(float absoluteX, float absoluteY) {
342+
if (!hasNewLayout()) {
343+
return false;
344+
}
345+
346+
float layoutX = getLayoutX();
347+
float layoutY = getLayoutY();
348+
int newAbsoluteLeft = Math.round(absoluteX + layoutX);
349+
int newAbsoluteTop = Math.round(absoluteY + layoutY);
350+
int newAbsoluteRight = Math.round(absoluteX + layoutX + getLayoutWidth());
351+
int newAbsoluteBottom = Math.round(absoluteY + layoutY + getLayoutHeight());
352+
353+
int newScreenX = Math.round(layoutX);
354+
int newScreenY = Math.round(layoutY);
355+
int newScreenWidth = newAbsoluteRight - newAbsoluteLeft;
356+
int newScreenHeight = newAbsoluteBottom - newAbsoluteTop;
357+
358+
return newScreenX != mScreenX
359+
|| newScreenY != mScreenY
360+
|| newScreenWidth != mScreenWidth
361+
|| newScreenHeight != mScreenHeight;
362+
}
363+
364+
@Override
365+
public void dispatchUpdates(
343366
float absoluteX,
344367
float absoluteY,
345368
UIViewOperationQueue uiViewOperationQueue,
@@ -386,10 +409,6 @@ public boolean dispatchUpdates(
386409
getScreenHeight());
387410
}
388411
}
389-
390-
return layoutHasChanged;
391-
} else {
392-
return false;
393412
}
394413
}
395414

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
import com.facebook.systrace.SystraceMessage;
3030
import com.facebook.yoga.YogaConstants;
3131
import com.facebook.yoga.YogaDirection;
32+
import java.util.ArrayList;
3233
import java.util.Arrays;
34+
import java.util.List;
3335
import java.util.Map;
3436

3537
/**
@@ -665,7 +667,20 @@ protected void updateViewHierarchy() {
665667
.arg("rootTag", cssRoot.getReactTag())
666668
.flush();
667669
try {
668-
applyUpdatesRecursive(cssRoot, 0f, 0f);
670+
List<ReactShadowNode> onLayoutNodes = new ArrayList<>();
671+
applyUpdatesRecursive(cssRoot, 0f, 0f, onLayoutNodes);
672+
673+
for (ReactShadowNode node : onLayoutNodes) {
674+
mEventDispatcher.dispatchEvent(
675+
OnLayoutEvent.obtain(
676+
-1, /* surfaceId not used in classic renderer */
677+
node.getReactTag(),
678+
node.getScreenX(),
679+
node.getScreenY(),
680+
node.getScreenWidth(),
681+
node.getScreenHeight()));
682+
}
683+
669684
} finally {
670685
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
671686
}
@@ -951,39 +966,34 @@ protected void calculateRootLayout(ReactShadowNode cssRoot) {
951966
}
952967
}
953968

954-
protected void applyUpdatesRecursive(ReactShadowNode cssNode, float absoluteX, float absoluteY) {
969+
protected void applyUpdatesRecursive(
970+
ReactShadowNode cssNode,
971+
float absoluteX,
972+
float absoluteY,
973+
List<ReactShadowNode> onLayoutNodes) {
955974
if (!cssNode.hasUpdates()) {
956975
return;
957976
}
958977

978+
if (cssNode.dispatchUpdatesWillChangeLayout(absoluteX, absoluteY)
979+
&& cssNode.shouldNotifyOnLayout()
980+
&& !mShadowNodeRegistry.isRootNode(cssNode.getReactTag())) {
981+
onLayoutNodes.add(cssNode);
982+
}
983+
959984
Iterable<? extends ReactShadowNode> cssChildren = cssNode.calculateLayoutOnChildren();
960985
if (cssChildren != null) {
961986
for (ReactShadowNode cssChild : cssChildren) {
962987
applyUpdatesRecursive(
963-
cssChild, absoluteX + cssNode.getLayoutX(), absoluteY + cssNode.getLayoutY());
988+
cssChild,
989+
absoluteX + cssNode.getLayoutX(),
990+
absoluteY + cssNode.getLayoutY(),
991+
onLayoutNodes);
964992
}
965993
}
966994

967-
int tag = cssNode.getReactTag();
968-
if (!mShadowNodeRegistry.isRootNode(tag)) {
969-
boolean frameDidChange =
970-
cssNode.dispatchUpdates(
971-
absoluteX, absoluteY, mOperationsQueue, mNativeViewHierarchyOptimizer);
972-
973-
// Notify JS about layout event if requested
974-
// and if the position or dimensions actually changed
975-
// (consistent with iOS).
976-
if (frameDidChange && cssNode.shouldNotifyOnLayout()) {
977-
mEventDispatcher.dispatchEvent(
978-
OnLayoutEvent.obtain(
979-
-1, /* surfaceId not used in classic renderer */
980-
tag,
981-
cssNode.getScreenX(),
982-
cssNode.getScreenY(),
983-
cssNode.getScreenWidth(),
984-
cssNode.getScreenHeight()));
985-
}
986-
}
995+
cssNode.dispatchUpdates(absoluteX, absoluteY, mOperationsQueue, mNativeViewHierarchyOptimizer);
996+
987997
cssNode.markUpdateSeen();
988998
mNativeViewHierarchyOptimizer.onViewUpdatesCompleted(cssNode);
989999
}

0 commit comments

Comments
 (0)