Add backward compatible support for onLayout event in Fabric

Reviewed By: achen1

Differential Revision: D8231722

fbshipit-source-id: 3d0641a7813e742ca81b98576f9ffc30ee597f30
This commit is contained in:
David Vacca 2018-06-01 17:47:40 -07:00 committed by Facebook Github Bot
parent 6c989fe7c6
commit 6aea98441a
8 changed files with 90 additions and 10 deletions

View File

@ -37,7 +37,9 @@ import com.facebook.react.modules.core.PermissionListener;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.react.testing.idledetection.ReactBridgeIdleSignaler;
import com.facebook.react.testing.idledetection.ReactIdleDetectionUtil;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.UIImplementationProvider;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.uimanager.ViewManagerRegistry;
import java.util.Arrays;
@ -265,8 +267,10 @@ public class ReactAppTestActivity extends FragmentActivity
public FabricUIManager get() {
List<ViewManager> viewManagers =
mReactInstanceManager.getOrCreateViewManagers(reactApplicationContext);
EventDispatcher eventDispatcher =
reactApplicationContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
FabricUIManager fabricUIManager =
new FabricUIManager(reactApplicationContext, new ViewManagerRegistry(viewManagers), jsContext);
new FabricUIManager(reactApplicationContext, new ViewManagerRegistry(viewManagers), jsContext, eventDispatcher);
new FabricJSCBinding().installFabric(jsContext, fabricUIManager);
return fabricUIManager;
}

View File

@ -25,6 +25,8 @@ import com.facebook.react.testing.idledetection.IdleWaiter;
public abstract class ReactInstrumentationTest extends
ActivityInstrumentationTestCase2<ReactAppTestActivity> implements IdleWaiter {
protected StringRecordingModule mRecordingModule;
public ReactInstrumentationTest() {
super(ReactAppTestActivity.class);
}
@ -36,6 +38,7 @@ public abstract class ReactInstrumentationTest extends
Intent intent = new Intent();
intent.putExtra(ReactAppTestActivity.EXTRA_IS_FABRIC_TEST, isFabricTest());
setActivityIntent(intent);
mRecordingModule = new StringRecordingModule();
final ReactAppTestActivity activity = getActivity();
activity.loadBundle(
createReactInstanceSpecForTest(),
@ -95,7 +98,7 @@ public abstract class ReactInstrumentationTest extends
* Override this method to provide extra native modules to be loaded before the app starts
*/
protected ReactInstanceSpecForTest createReactInstanceSpecForTest() {
return new ReactInstanceSpecForTest();
return new ReactInstanceSpecForTest().addNativeModule(mRecordingModule);
}
/**

View File

@ -10,6 +10,7 @@ package com.facebook.react.fabric;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.UNSPECIFIED;
import static com.facebook.react.uimanager.common.UIManagerType.FABRIC;
import android.util.Log;
import android.view.View;
@ -24,9 +25,11 @@ import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.fabric.events.FabricEventEmitter;
import com.facebook.react.modules.i18nmanager.I18nUtil;
import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.NativeViewHierarchyManager;
import com.facebook.react.uimanager.OnLayoutEvent;
import com.facebook.react.uimanager.ReactRootViewTagGenerator;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.ReactShadowNodeImpl;
@ -37,6 +40,7 @@ import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.uimanager.ViewManagerRegistry;
import com.facebook.react.uimanager.common.MeasureSpecProvider;
import com.facebook.react.uimanager.common.SizeMonitoringFrameLayout;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.yoga.YogaDirection;
import java.util.ArrayList;
import java.util.LinkedList;
@ -61,13 +65,15 @@ public class FabricUIManager implements UIManager, JSHandler {
private final JavaScriptContextHolder mJSContext;
private volatile int mCurrentBatch = 0;
private final FabricReconciler mFabricReconciler;
private final EventDispatcher mEventDispatcher;
private FabricBinding mBinding;
private long mEventHandlerPointer;
public FabricUIManager(
ReactApplicationContext reactContext,
ViewManagerRegistry viewManagerRegistry,
JavaScriptContextHolder jsContext) {
JavaScriptContextHolder jsContext,
EventDispatcher eventDispatcher) {
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(reactContext);
mReactApplicationContext = reactContext;
mViewManagerRegistry = viewManagerRegistry;
@ -76,7 +82,12 @@ public class FabricUIManager implements UIManager, JSHandler {
new UIViewOperationQueue(
reactContext, mNativeViewHierarchyManager, 0);
mFabricReconciler = new FabricReconciler(mUIViewOperationQueue);
mEventDispatcher = eventDispatcher;
mJSContext = jsContext;
FabricEventEmitter eventEmitter =
new FabricEventEmitter(reactContext, this);
eventDispatcher.registerEventEmitter(FABRIC, eventEmitter);
}
public void setBinding(FabricBinding binding) {
@ -355,7 +366,18 @@ public class FabricUIManager implements UIManager, JSHandler {
if (mRootShadowNodeRegistry.getNode(tag) == null) {
boolean frameDidChange =
node.dispatchUpdates(absoluteX, absoluteY, mUIViewOperationQueue, null);
// Notify JS about layout event if requested
// and if the position or dimensions actually changed
// (consistent with iOS and Android Default implementation).
if (frameDidChange && node.shouldNotifyOnLayout()) {
mUIViewOperationQueue.enqueueOnLayoutEvent(tag,
node.getScreenX(),
node.getScreenY(),
node.getScreenWidth(),
node.getScreenHeight());
}
}
// Set the reference to the OriginalReactShadowNode to NULL, as the tree is already committed
// and we do not need to hold references to the previous tree anymore
node.setOriginalReactShadowNode(null);

View File

@ -26,6 +26,7 @@ import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.fabric.FabricUIManager;
import com.facebook.react.fabric.Scheduler;
import com.facebook.react.fabric.Work;
import com.facebook.react.uimanager.IllegalViewOperationException;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import java.io.Closeable;
import java.io.IOException;
@ -48,9 +49,13 @@ public class FabricEventEmitter implements RCTEventEmitter, Closeable {
}
@Override
public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap params) {
long eventTarget = mFabricUIManager.createEventTarget(targetTag);
mScheduler.scheduleWork(new FabricUIManagerWork(eventTarget, eventName, params));
public void receiveEvent(int reactTag, String eventName, @Nullable WritableMap params) {
try {
long eventTarget = mFabricUIManager.createEventTarget(reactTag);
mScheduler.scheduleWork(new FabricUIManagerWork(eventTarget, eventName, params));
} catch (IllegalViewOperationException e) {
Log.e(TAG, "Unable to emmit event for tag " + reactTag, e);
}
}
@Override

View File

@ -224,11 +224,11 @@ public class NativeViewHierarchyManager {
public long getInstanceHandle(int reactTag) {
View view = mTagsToViews.get(reactTag);
if (view == null) {
throw new IllegalArgumentException("Unable to find view for tag: " + reactTag);
throw new IllegalViewOperationException("Unable to find view for tag: " + reactTag);
}
Long instanceHandle = (Long) view.getTag(R.id.view_tag_instance_handle);
if (instanceHandle == null) {
throw new IllegalArgumentException("Unable to find instanceHandle for tag: " + reactTag);
throw new IllegalViewOperationException("Unable to find instanceHandle for tag: " + reactTag);
}
return instanceHandle;
}

View File

@ -96,6 +96,38 @@ public class UIViewOperationQueue {
}
}
private final class EmitOnLayoutEventOperation extends ViewOperation {
private final int mScreenX;
private final int mScreenY;
private final int mScreenWidth;
private final int mScreenHeight;
public EmitOnLayoutEventOperation(
int tag,
int screenX,
int screenY,
int screenWidth,
int screenHeight) {
super(tag);
mScreenX = screenX;
mScreenY = screenY;
mScreenWidth = screenWidth;
mScreenHeight = screenHeight;
}
@Override
public void execute() {
mReactApplicationContext.getNativeModule(UIManagerModule.class)
.getEventDispatcher()
.dispatchEvent(OnLayoutEvent.obtain(
mTag,
mScreenX,
mScreenY,
mScreenWidth,
mScreenHeight));
}
}
private final class UpdateInstanceHandleOperation extends ViewOperation {
@ -706,6 +738,16 @@ public class UIViewOperationQueue {
mOperations.add(new UpdatePropertiesOperation(reactTag, props));
}
public void enqueueOnLayoutEvent(
int tag,
int screenX,
int screenY,
int screenWidth,
int screenHeight) {
mOperations.add(new EmitOnLayoutEventOperation(tag, screenX, screenY, screenWidth, screenHeight));
}
public void enqueueUpdateLayout(
int parentTag,
int reactTag,

View File

@ -11,6 +11,7 @@ import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactTestHelper;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.NativeViewHierarchyManager;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.ReactShadowNodeImpl;
@ -46,7 +47,8 @@ public class FabricReconcilerTest {
List<ViewManager> viewManagers = new ArrayList<>();
ViewManagerRegistry viewManagerRegistry = new ViewManagerRegistry(viewManagers);
JavaScriptContextHolder jsContext = mock(JavaScriptContextHolder.class);
mFabricUIManager = new FabricUIManager(reactContext, viewManagerRegistry, jsContext);
EventDispatcher eventDispatcher = mock(EventDispatcher.class);
mFabricUIManager = new FabricUIManager(reactContext, viewManagerRegistry, jsContext, eventDispatcher);
mMockUIViewOperationQueue = new MockUIViewOperationQueue(reactContext);
mFabricReconciler = new FabricReconciler(mMockUIViewOperationQueue);
}

View File

@ -13,6 +13,7 @@ import com.facebook.react.bridge.JavaScriptContextHolder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactTestHelper;
import com.facebook.react.bridge.ReadableNativeMap;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.ReactShadowNodeImpl;
import com.facebook.react.uimanager.Spacing;
@ -55,7 +56,8 @@ public class FabricUIManagerTest {
new ReactViewManager(), new ReactTextViewManager(), new ReactRawTextManager());
ViewManagerRegistry viewManagerRegistry = new ViewManagerRegistry(viewManagers);
JavaScriptContextHolder jsContext = mock(JavaScriptContextHolder.class);
mFabricUIManager = new FabricUIManager(reactContext, viewManagerRegistry, jsContext);
EventDispatcher eventDispatcher = mock(EventDispatcher.class);
mFabricUIManager = new FabricUIManager(reactContext, viewManagerRegistry, jsContext, eventDispatcher);
}
@Test