Summary:
As of D3235050, Nodes supports the optimization of removing clipped
subviews from the hierarchy. However, because Nodes supports overflow:visible,
this could cause issues when DrawCommands overflow the bounds of their parent
container. This patch fixes this by not clipping any overflowing Nodes.

Reviewed By: astreet

Differential Revision: D3235072
This commit is contained in:
Ahmed El-Helw 2016-05-13 17:49:38 -07:00
Родитель 5f162ca119
Коммит 9d67989001
5 изменённых файлов: 83 добавлений и 8 удалений

Просмотреть файл

@ -64,10 +64,11 @@ import com.facebook.react.uimanager.ViewManagerRegistry;
int reactTag,
@Nullable DrawCommand[] drawCommands,
@Nullable AttachDetachListener[] listeners,
@Nullable NodeRegion[] nodeRegions) {
@Nullable NodeRegion[] nodeRegions,
boolean hasOverflowingElements) {
FlatViewGroup view = (FlatViewGroup) resolveView(reactTag);
if (drawCommands != null) {
view.mountDrawCommands(drawCommands);
view.mountDrawCommands(drawCommands, hasOverflowingElements);
}
if (listeners != null) {
view.mountAttachDetachListeners(listeners);

Просмотреть файл

@ -60,6 +60,11 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
private float mClipRight;
private float mClipBottom;
// Used to track whether any of the NodeRegions overflow this Node. This is used to determine
// whether or not we can detach this Node in the context of a container with
// setRemoveClippedSubviews enabled.
private boolean mOverflowsContainer;
// last OnLayoutEvent info, only used when shouldNotifyOnLayout() is true.
private int mLayoutX;
private int mLayoutY;
@ -120,6 +125,11 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
@ReactProp(name = "overflow")
public final void setOverflow(String overflow) {
mClipToBounds = "hidden".equals(overflow);
if (mClipToBounds) {
mOverflowsContainer = false;
} else {
updateOverflowsContainer();
}
invalidate();
}
@ -255,6 +265,51 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
/* package */ final void setNodeRegions(NodeRegion[] nodeRegion) {
mNodeRegions = nodeRegion;
updateOverflowsContainer();
}
/* package */ final void updateOverflowsContainer() {
boolean overflowsContainer = false;
int width = (int) (mNodeRegion.mRight - mNodeRegion.mLeft);
int height = (int) (mNodeRegion.mBottom - mNodeRegion.mTop);
if (!mClipToBounds && height > 0 && width > 0) {
for (NodeRegion region : mNodeRegions) {
if (region.mBottom - region.mTop > height || region.mRight - region.mLeft > width) {
overflowsContainer = true;
break;
}
}
}
// if we don't overflow, let's check if any of the immediate children overflow.
// this is "indirectly recursive," since this method is called when setNodeRegions is called,
// and the children call setNodeRegions before their parent. consequently, when a node deep
// inside the tree overflows, its immediate parent has mOverflowsContainer set to true, and,
// by extension, so do all of its ancestors, sufficing here to only check the immediate
// child's mOverflowsContainer value instead of recursively asking if each child overflows its
// container.
if (!overflowsContainer) {
int children = getChildCount();
for (int i = 0; i < children; i++) {
ReactShadowNode node = getChildAt(i);
if (node instanceof FlatShadowNode && ((FlatShadowNode) node).mOverflowsContainer) {
overflowsContainer = true;
break;
}
}
}
// if things changed, notify the parent(s) about said changes - while in many cases, this will
// be extra work (since we process this for the parents after the children), in some cases,
// we may have no new node regions in the parent, but have a new node region in the child, and,
// as a result, the parent may not get the correct value for overflows container.
if (mOverflowsContainer != overflowsContainer) {
mOverflowsContainer = overflowsContainer;
}
}
/* package */ final boolean getOverflowsContainer() {
return mOverflowsContainer;
}
/* package */ void updateNodeRegion(

Просмотреть файл

@ -44,16 +44,19 @@ import com.facebook.react.uimanager.UIViewOperationQueue;
private final @Nullable DrawCommand[] mDrawCommands;
private final @Nullable AttachDetachListener[] mAttachDetachListeners;
private final @Nullable NodeRegion[] mNodeRegions;
private final boolean mHasOverflowingElements;
private UpdateMountState(
int reactTag,
@Nullable DrawCommand[] drawCommands,
@Nullable AttachDetachListener[] listeners,
@Nullable NodeRegion[] nodeRegions) {
@Nullable NodeRegion[] nodeRegions,
boolean hasOverflowingElements) {
mReactTag = reactTag;
mDrawCommands = drawCommands;
mAttachDetachListeners = listeners;
mNodeRegions = nodeRegions;
mHasOverflowingElements = hasOverflowingElements;
}
@Override
@ -62,7 +65,8 @@ import com.facebook.react.uimanager.UIViewOperationQueue;
mReactTag,
mDrawCommands,
mAttachDetachListeners,
mNodeRegions);
mNodeRegions,
mHasOverflowingElements);
}
}
@ -234,8 +238,14 @@ import com.facebook.react.uimanager.UIViewOperationQueue;
int reactTag,
@Nullable DrawCommand[] drawCommands,
@Nullable AttachDetachListener[] listeners,
@Nullable NodeRegion[] nodeRegions) {
enqueueUIOperation(new UpdateMountState(reactTag, drawCommands, listeners, nodeRegions));
@Nullable NodeRegion[] nodeRegions,
boolean hasOverflowingElements) {
enqueueUIOperation(new UpdateMountState(
reactTag,
drawCommands,
listeners,
nodeRegions,
hasOverflowingElements));
}
public void enqueueUpdateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) {

Просмотреть файл

@ -100,6 +100,8 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
// lookups in o(1) instead of o(log n) - trade space for time
private final Map<Integer, DrawView> mDrawViewMap = new HashMap<>();
private final Map<Integer, FlatViewGroup> mClippedSubviews = new HashMap<>();
// whether or not this FlatViewGroup has elements that overflow its bounds
private boolean mHasOverflowingElements;
/* package */ FlatViewGroup(Context context) {
super(context);
@ -390,8 +392,9 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
++mDrawChildIndex;
}
/* package */ void mountDrawCommands(DrawCommand[] drawCommands) {
/* package */ void mountDrawCommands(DrawCommand[] drawCommands, boolean hasOverflowingElements) {
mDrawCommands = drawCommands;
mHasOverflowingElements = hasOverflowingElements;
if (mRemoveClippedSubviews) {
mDrawViewMap.clear();
for (DrawCommand drawCommand : mDrawCommands) {
@ -597,6 +600,7 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
Animation animation = flatChildView.getAnimation();
boolean isAnimating = animation != null && !animation.hasEnded();
if (!isAnimating &&
!flatChildView.mHasOverflowingElements &&
!clippingRect.intersects(
view.getLeft(),
view.getTop(),

Просмотреть файл

@ -325,6 +325,10 @@ import com.facebook.react.uimanager.events.EventDispatcher;
if (nodeRegions != null) {
shouldUpdateMountState = true;
node.setNodeRegions(nodeRegions);
} else if (descendantUpdated) {
// one of the descendant's value for overflows container may have changed, so
// we still need to update ours.
node.updateOverflowsContainer();
}
if (shouldUpdateMountState) {
@ -332,7 +336,8 @@ import com.facebook.react.uimanager.events.EventDispatcher;
node.getReactTag(),
drawCommands,
listeners,
nodeRegions);
nodeRegions,
node.getOverflowsContainer());
}
if (node.hasUnseenUpdates()) {