From f0535152abdde27dfa0365b7d517096e6ea8d6b8 Mon Sep 17 00:00:00 2001 From: Ahmed El-Helw Date: Wed, 17 Feb 2016 19:08:19 -0800 Subject: [PATCH] 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 --- .../com/facebook/react/flat/AndroidView.java | 97 ++++--------------- .../react/flat/FlatUIImplementation.java | 2 +- .../react/flat/NativeViewWrapper.java | 97 +++++++++++++++++++ .../com/facebook/react/flat/RCTTextInput.java | 46 ++++----- .../com/facebook/react/flat/StateBuilder.java | 6 +- 5 files changed, 142 insertions(+), 106 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/flat/NativeViewWrapper.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/AndroidView.java b/ReactAndroid/src/main/java/com/facebook/react/flat/AndroidView.java index a5e6e9ee3..a53b2a26f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/AndroidView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/AndroidView.java @@ -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(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java index 62182d283..784ac065c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java @@ -116,7 +116,7 @@ public class FlatUIImplementation extends UIImplementation { } ViewManager viewManager = resolveViewManager(className); - return new AndroidView(viewManager); + return new NativeViewWrapper(viewManager); } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/NativeViewWrapper.java b/ReactAndroid/src/main/java/com/facebook/react/flat/NativeViewWrapper.java new file mode 100644 index 000000000..4d87dd9e5 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/NativeViewWrapper.java @@ -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(); + } + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInput.java b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInput.java index c4f942f5d..7f69f4350 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInput.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/RCTTextInput.java @@ -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; + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java b/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java index a6682cd9a..0bf16468f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/StateBuilder.java @@ -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,