Make AndroidView an interface

Summary:
The current AndroidView stipulates that the backing shadow node can't
be a FlatShadowNode. In some cases, however, we want to apply some of the same
logic (ex not adding NodeRegions, etc) to other ViewManagers that have a
FlatShadowNode backing (and that don't necessarily create a FlatViewGroup).
This commit renames AndroidView to NativeViewWrapper, and re-introduces
AndroidView as an interface, so that logic for padding, NodeRegions, etc can
be shared.

Differential Revision: D2942387
This commit is contained in:
Ahmed El-Helw 2016-02-17 19:08:19 -08:00
parent b461c70b76
commit f0535152ab
5 changed files with 142 additions and 106 deletions

View File

@ -9,86 +9,29 @@
package com.facebook.react.flat;
import com.facebook.csslayout.CSSNode;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.csslayout.Spacing;
/* package */ final class AndroidView extends FlatShadowNode {
interface AndroidView {
final ViewManager mViewManager;
private final ReactShadowNode mReactShadowNode;
private final boolean mNeedsCustomLayoutForChildren;
private boolean mPaddingChanged = false;
/**
* Whether or not custom layout is needed for the children
* @return a boolean representing whether custom layout is needed
*/
boolean needsCustomLayoutForChildren();
/* package */ AndroidView(ViewManager viewManager) {
mViewManager = viewManager;
ReactShadowNode reactShadowNode = viewManager.createShadowNodeInstance();
if (reactShadowNode instanceof CSSNode.MeasureFunction) {
mReactShadowNode = reactShadowNode;
setMeasureFunction((CSSNode.MeasureFunction) reactShadowNode);
} else {
mReactShadowNode = null;
}
/**
* Did the padding change
* @return a boolean representing whether the padding changed
*/
boolean isPaddingChanged();
if (viewManager instanceof ViewGroupManager) {
ViewGroupManager viewGroupManager = (ViewGroupManager) viewManager;
mNeedsCustomLayoutForChildren = viewGroupManager.needsCustomLayoutForChildren();
} else {
mNeedsCustomLayoutForChildren = false;
}
/**
* Reset the padding changed internal state
*/
void resetPaddingChanged();
forceMountToView();
}
/* package */ boolean needsCustomLayoutForChildren() {
return mNeedsCustomLayoutForChildren;
}
/* package */ boolean isPaddingChanged() {
return mPaddingChanged;
}
/* package */ void resetPaddingChanged() {
mPaddingChanged = false;
}
@Override
public void setBackgroundColor(int backgroundColor) {
// suppress, this is handled by a ViewManager
}
@Override
public void setThemedContext(ThemedReactContext themedContext) {
super.setThemedContext(themedContext);
if (mReactShadowNode != null) {
mReactShadowNode.setThemedContext(themedContext);
}
}
@Override
/* package*/ void handleUpdateProperties(ReactStylesDiffMap styles) {
if (mReactShadowNode != null) {
mReactShadowNode.updateProperties(styles);
}
}
@Override
public void addChildAt(CSSNode child, int i) {
super.addChildAt(child, i);
if (child instanceof FlatShadowNode) {
((FlatShadowNode) child).forceMountToView();
}
}
@Override
public void setPadding(int spacingType, float padding) {
if (getPadding().set(spacingType, padding)) {
mPaddingChanged = true;
dirty();
}
}
/**
* Get this node's padding, as defined by style + default padding.
*/
Spacing getPadding();
}

View File

@ -116,7 +116,7 @@ public class FlatUIImplementation extends UIImplementation {
}
ViewManager viewManager = resolveViewManager(className);
return new AndroidView(viewManager);
return new NativeViewWrapper(viewManager);
}
@Override

View File

@ -0,0 +1,97 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.flat;
import javax.annotation.Nullable;
import com.facebook.csslayout.CSSNode;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.ViewManager;
/* package */ final class NativeViewWrapper extends FlatShadowNode implements AndroidView {
@Nullable private final ReactShadowNode mReactShadowNode;
private final boolean mNeedsCustomLayoutForChildren;
private boolean mPaddingChanged = false;
/* package */ NativeViewWrapper(ViewManager viewManager) {
ReactShadowNode reactShadowNode = viewManager.createShadowNodeInstance();
if (reactShadowNode instanceof CSSNode.MeasureFunction) {
mReactShadowNode = reactShadowNode;
setMeasureFunction((CSSNode.MeasureFunction) reactShadowNode);
} else {
mReactShadowNode = null;
}
if (viewManager instanceof ViewGroupManager) {
ViewGroupManager viewGroupManager = (ViewGroupManager) viewManager;
mNeedsCustomLayoutForChildren = viewGroupManager.needsCustomLayoutForChildren();
} else {
mNeedsCustomLayoutForChildren = false;
}
forceMountToView();
}
@Override
public boolean needsCustomLayoutForChildren() {
return mNeedsCustomLayoutForChildren;
}
@Override
public boolean isPaddingChanged() {
return mPaddingChanged;
}
@Override
public void resetPaddingChanged() {
mPaddingChanged = false;
}
@Override
public void setBackgroundColor(int backgroundColor) {
// suppress, this is handled by a ViewManager
}
@Override
public void setThemedContext(ThemedReactContext themedContext) {
super.setThemedContext(themedContext);
if (mReactShadowNode != null) {
mReactShadowNode.setThemedContext(themedContext);
}
}
@Override
/* package*/ void handleUpdateProperties(ReactStylesDiffMap styles) {
if (mReactShadowNode != null) {
mReactShadowNode.updateProperties(styles);
}
}
@Override
public void addChildAt(CSSNode child, int i) {
super.addChildAt(child, i);
if (child instanceof FlatShadowNode) {
((FlatShadowNode) child).forceMountToView();
}
}
@Override
public void setPadding(int spacingType, float padding) {
if (getPadding().set(spacingType, padding)) {
mPaddingChanged = true;
dirty();
}
}
}

View File

@ -9,20 +9,17 @@
package com.facebook.react.flat;
import javax.annotation.Nullable;
import android.text.SpannableStringBuilder;
import com.facebook.csslayout.Spacing;
import com.facebook.react.uimanager.UIViewOperationQueue;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.text.ReactTextUpdate;
import static com.facebook.react.views.text.ReactTextShadowNode.UNSET;
public class RCTTextInput extends RCTVirtualText {
public class RCTTextInput extends RCTVirtualText implements AndroidView {
private int mJsEventCount = UNSET;
private @Nullable float[] mComputedPadding;
private boolean mPaddingChanged = false;
public RCTTextInput() {
forceMountToView();
@ -46,15 +43,11 @@ public class RCTTextInput extends RCTVirtualText {
@Override
public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
super.onCollectExtraUpdates(uiViewOperationQueue);
if (mComputedPadding != null) {
uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), mComputedPadding);
mComputedPadding = null;
if (mJsEventCount != UNSET) {
ReactTextUpdate reactTextUpdate =
new ReactTextUpdate(getText(), mJsEventCount, false);
uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), reactTextUpdate);
}
ReactTextUpdate reactTextUpdate =
new ReactTextUpdate(getText(), mJsEventCount, false);
uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), reactTextUpdate);
}
@ReactProp(name = "mostRecentEventCount")
@ -64,18 +57,20 @@ public class RCTTextInput extends RCTVirtualText {
@Override
public void setPadding(int spacingType, float padding) {
super.setPadding(spacingType, padding);
mComputedPadding = spacingToFloatArray(getPadding());
markUpdated();
if (getPadding().set(spacingType, padding)) {
mPaddingChanged = true;
dirty();
}
}
private static float[] spacingToFloatArray(Spacing spacing) {
return new float[] {
spacing.get(Spacing.LEFT),
spacing.get(Spacing.TOP),
spacing.get(Spacing.RIGHT),
spacing.get(Spacing.BOTTOM),
};
@Override
public boolean isPaddingChanged() {
return mPaddingChanged;
}
@Override
public void resetPaddingChanged() {
mPaddingChanged = false;
}
/**
@ -87,4 +82,9 @@ public class RCTTextInput extends RCTVirtualText {
applySpans(sb);
return sb;
}
@Override
public boolean needsCustomLayoutForChildren() {
return false;
}
}

View File

@ -240,11 +240,7 @@ import com.facebook.react.uimanager.events.EventDispatcher;
isAndroidView,
needsCustomLayoutForChildren);
// this is a temporary measure to skip adding node regions for RCTTextInput. This will be fixed
// in a patch soon which will convert AndroidView into an interface, thus allowing RCTTextInput
// to be treated as an AndroidView
boolean skipAddingNodeRegion = node instanceof RCTTextInput;
if (!isAndroidView && node.isVirtualAnchor() && !skipAddingNodeRegion) {
if (!isAndroidView && node.isVirtualAnchor()) {
// If RCTText is mounted to View, virtual children will not receive any touch events
// because they don't get added to nodeRegions, so nodeRegions will be empty and
// FlatViewGroup.reactTagForTouch() will always return RCTText's id. To fix the issue,