Skip to content

Commit d1de576

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Top-down onLayout events
Summary: This makes Android Paper/Classic renderer fire `onLayout` events top down, like in Fabric/new Architecture, and like iOS on Paper/classic 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. This is a behavior change, but to my knowledge was never hit during the Fabric migration, and any JS code written for both Android and iOS will need to support this model already. Changelog: [General][Changed] - Make layout events top-down on Android classic renderer Differential Revision: D49627996
1 parent babbc3e commit d1de576

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)