mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-08 17:46:35 +08:00
Summary: Before this patch, we only collected virtual nodes in NodeRegions, because NodeRegions are only needed to implement ReactCompoundView.reactTargetForTouch() which is only interested in virtual nodes. In the next patch, FlatViewGroup will implement ReactCompoundViewGroup interface which requires knowledge of both virtual and non-virtual children. As a step towards that, we need to include non-virtual nodes in NodeRegions. This patch is implementing that. By itself, it should have not cause any changes in application behavior: we add non-virtual nodes to NodeRegions and mark them as non-virtual, then skip all non-virtual nodes in reactTagForTouch(). Reviewed By: ahmedre Differential Revision: D3018047
360 lines
10 KiB
Java
360 lines
10 KiB
Java
/**
|
|
* 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.infer.annotation.Assertions;
|
|
import com.facebook.react.uimanager.LayoutShadowNode;
|
|
import com.facebook.react.uimanager.OnLayoutEvent;
|
|
import com.facebook.react.uimanager.ReactShadowNode;
|
|
import com.facebook.react.uimanager.ReactStylesDiffMap;
|
|
import com.facebook.react.uimanager.ViewProps;
|
|
import com.facebook.react.uimanager.annotations.ReactProp;
|
|
|
|
/**
|
|
* FlatShadowNode is a base class for all shadow node used in FlatUIImplementation. It extends
|
|
* {@link LayoutShadowNode} by adding an ability to prepare DrawCommands off the UI thread.
|
|
*/
|
|
/* package */ class FlatShadowNode extends LayoutShadowNode {
|
|
|
|
/* package */ static final FlatShadowNode[] EMPTY_ARRAY = new FlatShadowNode[0];
|
|
|
|
private static final String PROP_DECOMPOSED_MATRIX = "decomposedMatrix";
|
|
private static final String PROP_OPACITY = "opacity";
|
|
private static final String PROP_RENDER_TO_HARDWARE_TEXTURE = "renderToHardwareTextureAndroid";
|
|
private static final String PROP_ACCESSIBILITY_LABEL = "accessibilityLabel";
|
|
private static final String PROP_ACCESSIBILITY_COMPONENT_TYPE = "accessibilityComponentType";
|
|
private static final String PROP_ACCESSIBILITY_LIVE_REGION = "accessibilityLiveRegion";
|
|
private static final String PROP_IMPORTANT_FOR_ACCESSIBILITY = "importantForAccessibility";
|
|
private static final String PROP_TEST_ID = "testID";
|
|
|
|
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
|
private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY;
|
|
private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY;
|
|
private FlatShadowNode[] mNativeChildren = FlatShadowNode.EMPTY_ARRAY;
|
|
private NodeRegion mNodeRegion = NodeRegion.EMPTY;
|
|
private int mNativeParentTag;
|
|
private int mViewLeft;
|
|
private int mViewTop;
|
|
private int mViewRight;
|
|
private int mViewBottom;
|
|
private boolean mBackingViewIsCreated;
|
|
private @Nullable DrawView mDrawView;
|
|
private @Nullable DrawBackgroundColor mDrawBackground;
|
|
private boolean mClipToBounds = false;
|
|
private boolean mIsUpdated = true;
|
|
private float mClipLeft;
|
|
private float mClipTop;
|
|
private float mClipRight;
|
|
private float mClipBottom;
|
|
|
|
// last OnLayoutEvent info, only used when shouldNotifyOnLayout() is true.
|
|
private int mLayoutX;
|
|
private int mLayoutY;
|
|
private int mLayoutWidth;
|
|
private int mLayoutHeight;
|
|
|
|
/* package */ void handleUpdateProperties(ReactStylesDiffMap styles) {
|
|
if (!mountsToView()) {
|
|
// Make sure we mount this FlatShadowNode to a View if any of these properties are present.
|
|
if (styles.hasKey(PROP_DECOMPOSED_MATRIX) ||
|
|
styles.hasKey(PROP_OPACITY) ||
|
|
styles.hasKey(PROP_RENDER_TO_HARDWARE_TEXTURE) ||
|
|
styles.hasKey(PROP_TEST_ID) ||
|
|
styles.hasKey(PROP_ACCESSIBILITY_LABEL) ||
|
|
styles.hasKey(PROP_ACCESSIBILITY_COMPONENT_TYPE) ||
|
|
styles.hasKey(PROP_ACCESSIBILITY_LIVE_REGION) ||
|
|
styles.hasKey(PROP_IMPORTANT_FOR_ACCESSIBILITY)) {
|
|
forceMountToView();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Collects DrawCommands produced by this FlatShadowNode.
|
|
*/
|
|
protected void collectState(
|
|
StateBuilder stateBuilder,
|
|
float left,
|
|
float top,
|
|
float right,
|
|
float bottom,
|
|
float clipLeft,
|
|
float clipTop,
|
|
float clipRight,
|
|
float clipBottom) {
|
|
if (mDrawBackground != null) {
|
|
mDrawBackground = (DrawBackgroundColor) mDrawBackground.updateBoundsAndFreeze(
|
|
left,
|
|
top,
|
|
right,
|
|
bottom,
|
|
clipLeft,
|
|
clipTop,
|
|
clipRight,
|
|
clipBottom);
|
|
stateBuilder.addDrawCommand(mDrawBackground);
|
|
}
|
|
}
|
|
|
|
@ReactProp(name = ViewProps.BACKGROUND_COLOR)
|
|
public void setBackgroundColor(int backgroundColor) {
|
|
mDrawBackground = (backgroundColor == 0) ? null : new DrawBackgroundColor(backgroundColor);
|
|
invalidate();
|
|
}
|
|
|
|
@ReactProp(name = "overflow")
|
|
public final void setOverflow(String overflow) {
|
|
mClipToBounds = "hidden".equals(overflow);
|
|
invalidate();
|
|
}
|
|
|
|
public final boolean clipToBounds() {
|
|
return mClipToBounds;
|
|
}
|
|
|
|
@Override
|
|
public final int getScreenX() {
|
|
return mViewLeft;
|
|
}
|
|
|
|
@Override
|
|
public final int getScreenY() {
|
|
return mViewTop;
|
|
}
|
|
|
|
@Override
|
|
public final int getScreenWidth() {
|
|
return mViewRight - mViewLeft;
|
|
}
|
|
|
|
@Override
|
|
public final int getScreenHeight() {
|
|
return mViewBottom - mViewTop;
|
|
}
|
|
|
|
/**
|
|
* Marks root node as updated to trigger a StateBuilder pass to collect DrawCommands for the node
|
|
* tree. Use it when FlatShadowNode is updated but doesn't require a layout pass (e.g. background
|
|
* color is changed).
|
|
*/
|
|
protected final void invalidate() {
|
|
FlatShadowNode node = this;
|
|
|
|
while (true) {
|
|
if (node.mountsToView()) {
|
|
if (node.mIsUpdated) {
|
|
// already updated
|
|
return;
|
|
}
|
|
|
|
node.mIsUpdated = true;
|
|
}
|
|
|
|
ReactShadowNode parent = node.getParent();
|
|
if (parent == null) {
|
|
// not attached to a hierarchy yet
|
|
return;
|
|
}
|
|
|
|
node = (FlatShadowNode) parent;
|
|
}
|
|
}
|
|
|
|
/* package */ final boolean isUpdated() {
|
|
return mIsUpdated;
|
|
}
|
|
|
|
/* package */ final void resetUpdated() {
|
|
mIsUpdated = false;
|
|
}
|
|
|
|
/* package */ final boolean clipBoundsChanged(
|
|
float clipLeft,
|
|
float clipTop,
|
|
float clipRight,
|
|
float clipBottom) {
|
|
return mClipLeft != clipLeft || mClipTop != clipTop ||
|
|
mClipRight != clipRight || mClipBottom != clipBottom;
|
|
}
|
|
|
|
/* package */ final void setClipBounds(
|
|
float clipLeft,
|
|
float clipTop,
|
|
float clipRight,
|
|
float clipBottom) {
|
|
mClipLeft = clipLeft;
|
|
mClipTop = clipTop;
|
|
mClipRight = clipRight;
|
|
mClipBottom = clipBottom;
|
|
}
|
|
|
|
/**
|
|
* Returns an array of DrawCommands to perform during the View's draw pass.
|
|
*/
|
|
/* package */ final DrawCommand[] getDrawCommands() {
|
|
return mDrawCommands;
|
|
}
|
|
|
|
/**
|
|
* Sets an array of DrawCommands to perform during the View's draw pass. StateBuilder uses old
|
|
* draw commands to compare to new draw commands and see if the View neds to be redrawn.
|
|
*/
|
|
/* package */ final void setDrawCommands(DrawCommand[] drawCommands) {
|
|
mDrawCommands = drawCommands;
|
|
}
|
|
|
|
/**
|
|
* Sets an array of AttachDetachListeners to call onAttach/onDetach when they are attached to or
|
|
* detached from a View that this shadow node maps to.
|
|
*/
|
|
/* package */ final void setAttachDetachListeners(AttachDetachListener[] listeners) {
|
|
mAttachDetachListeners = listeners;
|
|
}
|
|
|
|
/**
|
|
* Returns an array of AttachDetachListeners associated with this shadow node.
|
|
*/
|
|
/* package */ final AttachDetachListener[] getAttachDetachListeners() {
|
|
return mAttachDetachListeners;
|
|
}
|
|
|
|
/* package */ final FlatShadowNode[] getNativeChildren() {
|
|
return mNativeChildren;
|
|
}
|
|
|
|
/* package */ final void setNativeChildren(FlatShadowNode[] nativeChildren) {
|
|
mNativeChildren = nativeChildren;
|
|
}
|
|
|
|
/* package */ final int getNativeParentTag() {
|
|
return mNativeParentTag;
|
|
}
|
|
|
|
/* package */ final void setNativeParentTag(int nativeParentTag) {
|
|
mNativeParentTag = nativeParentTag;
|
|
}
|
|
|
|
/* package */ final NodeRegion[] getNodeRegions() {
|
|
return mNodeRegions;
|
|
}
|
|
|
|
/* package */ final void setNodeRegions(NodeRegion[] nodeRegion) {
|
|
mNodeRegions = nodeRegion;
|
|
}
|
|
|
|
/* package */ void updateNodeRegion(
|
|
float left,
|
|
float top,
|
|
float right,
|
|
float bottom,
|
|
boolean isVirtual) {
|
|
if (mNodeRegion.mLeft != left || mNodeRegion.mTop != top || mNodeRegion.mRight != right ||
|
|
mNodeRegion.mBottom != bottom || mNodeRegion.mIsVirtual != isVirtual) {
|
|
setNodeRegion(new NodeRegion(left, top, right, bottom, getReactTag(), isVirtual));
|
|
}
|
|
}
|
|
|
|
protected final void setNodeRegion(NodeRegion nodeRegion) {
|
|
mNodeRegion = nodeRegion;
|
|
}
|
|
|
|
/* package */ final NodeRegion getNodeRegion() {
|
|
return mNodeRegion;
|
|
}
|
|
|
|
/**
|
|
* Sets boundaries of the View that this node maps to relative to the parent left/top coordinate.
|
|
*/
|
|
/* package */ final void setViewBounds(int left, int top, int right, int bottom) {
|
|
mViewLeft = left;
|
|
mViewTop = top;
|
|
mViewRight = right;
|
|
mViewBottom = bottom;
|
|
}
|
|
|
|
/**
|
|
* Left position of the View this node maps to relative to the parent View.
|
|
*/
|
|
/* package */ final int getViewLeft() {
|
|
return mViewLeft;
|
|
}
|
|
|
|
/**
|
|
* Top position of the View this node maps to relative to the parent View.
|
|
*/
|
|
/* package */ final int getViewTop() {
|
|
return mViewTop;
|
|
}
|
|
|
|
/**
|
|
* Right position of the View this node maps to relative to the parent View.
|
|
*/
|
|
/* package */ final int getViewRight() {
|
|
return mViewRight;
|
|
}
|
|
|
|
/**
|
|
* Bottom position of the View this node maps to relative to the parent View.
|
|
*/
|
|
/* package */ final int getViewBottom() {
|
|
return mViewBottom;
|
|
}
|
|
|
|
/* package */ final void forceMountToView() {
|
|
if (isVirtual()) {
|
|
return;
|
|
}
|
|
|
|
if (mDrawView == null) {
|
|
mDrawView = DrawView.INSTANCE;
|
|
invalidate();
|
|
|
|
// reset NodeRegion to allow it getting garbage-collected
|
|
mNodeRegion = NodeRegion.EMPTY;
|
|
}
|
|
}
|
|
|
|
/* package */ final DrawView collectDrawView(float left, float top, float right, float bottom) {
|
|
if (!Assertions.assumeNotNull(mDrawView).clipBoundsMatch(left, top, right, bottom)) {
|
|
mDrawView = new DrawView(left, top, right, bottom);
|
|
}
|
|
|
|
return mDrawView;
|
|
}
|
|
|
|
@Nullable
|
|
/* package */ final OnLayoutEvent obtainLayoutEvent(int x, int y, int width, int height) {
|
|
if (mLayoutX == x && mLayoutY == y && mLayoutWidth == width && mLayoutHeight == height) {
|
|
return null;
|
|
}
|
|
|
|
mLayoutX = x;
|
|
mLayoutY = y;
|
|
mLayoutWidth = width;
|
|
mLayoutHeight = height;
|
|
|
|
return OnLayoutEvent.obtain(getReactTag(), x, y, width, height);
|
|
}
|
|
|
|
/* package */ final boolean mountsToView() {
|
|
return mDrawView != null;
|
|
}
|
|
|
|
/* package */ final boolean isBackingViewCreated() {
|
|
return mBackingViewIsCreated;
|
|
}
|
|
|
|
/* package */ final void signalBackingViewIsCreated() {
|
|
mBackingViewIsCreated = true;
|
|
}
|
|
}
|