Gather command and node region information off the UI thread.
Summary: This optimizes node region searches in clipping cases, and does position calculation for drawCommands off of the UI thread. Reviewed By: ahmedre Differential Revision: D3665301
This commit is contained in:
parent
ca79e6cf30
commit
192c99a4f6
|
@ -19,7 +19,7 @@ import android.graphics.Color;
|
|||
* The idea is to be able to reuse unmodified objects when we build up DrawCommands before we ship
|
||||
* them to UI thread, but we can only do that if DrawCommands are immutable.
|
||||
*/
|
||||
/* package */ abstract class AbstractDrawCommand implements DrawCommand, Cloneable {
|
||||
/* package */ abstract class AbstractDrawCommand extends DrawCommand implements Cloneable {
|
||||
|
||||
private float mLeft;
|
||||
private float mTop;
|
||||
|
|
|
@ -9,12 +9,18 @@
|
|||
|
||||
package com.facebook.react.flat;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
|
||||
|
@ -27,38 +33,133 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
*/
|
||||
/* package */ final class ClippingDrawCommandManager extends DrawCommandManager {
|
||||
private final FlatViewGroup mFlatViewGroup;
|
||||
DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||
private float[] mCommandMaxBottom = StateBuilder.EMPTY_FLOAT_ARRAY;
|
||||
private float[] mCommandMinTop = StateBuilder.EMPTY_FLOAT_ARRAY;
|
||||
|
||||
// lookups in o(1) instead of o(log n) - trade space for time
|
||||
private final Map<Integer, DrawView> mDrawViewMap = new HashMap<>();
|
||||
// When grandchildren are promoted, these can only be FlatViewGroups, but we need to handle the
|
||||
// case that we clip subviews and don't promote grandchildren.
|
||||
private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY;
|
||||
private float[] mRegionMaxBottom = StateBuilder.EMPTY_FLOAT_ARRAY;
|
||||
private float[] mRegionMinTop = StateBuilder.EMPTY_FLOAT_ARRAY;
|
||||
|
||||
// Onscreen bounds of draw command array.
|
||||
private int mStart;
|
||||
private int mStop;
|
||||
|
||||
// Mapping of ids to index position within the draw command array. O(log n) lookups should be
|
||||
// less in our case because of the large constant overhead and auto boxing of the map.
|
||||
private SparseIntArray mDrawViewIndexMap = StateBuilder.EMPTY_SPARSE_INT;
|
||||
// Map of views that are currently clipped.
|
||||
private final Map<Integer, View> mClippedSubviews = new HashMap<>();
|
||||
|
||||
private final Rect mClippingRect = new Rect();
|
||||
|
||||
// Used in updating the clipping rect, as sometimes we want to detach all views, which means we
|
||||
// need to temporarily store the views we are detaching and removing. These are always of size
|
||||
// 0, except when used in update clipping rect.
|
||||
private final SparseArray<View> mViewsToRemove = new SparseArray<>();
|
||||
private final ArrayList<View> mViewsToKeep = new ArrayList<>();
|
||||
|
||||
ClippingDrawCommandManager(FlatViewGroup flatViewGroup, DrawCommand[] drawCommands) {
|
||||
mFlatViewGroup = flatViewGroup;
|
||||
initialSetup(drawCommands);
|
||||
}
|
||||
|
||||
private void initialSetup(DrawCommand[] drawCommands) {
|
||||
mountDrawCommands(drawCommands);
|
||||
mountDrawCommands(
|
||||
drawCommands,
|
||||
mDrawViewIndexMap,
|
||||
mCommandMaxBottom,
|
||||
mCommandMinTop,
|
||||
true);
|
||||
updateClippingRect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mountDrawCommands(DrawCommand[] drawCommands) {
|
||||
public void mountDrawCommands(
|
||||
DrawCommand[] drawCommands,
|
||||
SparseIntArray drawViewIndexMap,
|
||||
float[] maxBottom,
|
||||
float[] minTop,
|
||||
boolean willMountViews) {
|
||||
mDrawCommands = drawCommands;
|
||||
mDrawViewMap.clear();
|
||||
for (DrawCommand drawCommand : mDrawCommands) {
|
||||
if (drawCommand instanceof DrawView) {
|
||||
DrawView drawView = (DrawView) drawCommand;
|
||||
mDrawViewMap.put(drawView.reactTag, drawView);
|
||||
mCommandMaxBottom = maxBottom;
|
||||
mCommandMinTop = minTop;
|
||||
mDrawViewIndexMap = drawViewIndexMap;
|
||||
if (mClippingRect.bottom != mClippingRect.top) {
|
||||
mStart = Arrays.binarySearch(mCommandMaxBottom, mClippingRect.top);
|
||||
if (mStart < 0) {
|
||||
// We don't care whether we matched or not, but positive indices are helpful.
|
||||
mStart = ~mStart;
|
||||
}
|
||||
mStop = Arrays.binarySearch(
|
||||
mCommandMinTop,
|
||||
mStart,
|
||||
mCommandMinTop.length,
|
||||
mClippingRect.bottom);
|
||||
if (mStop < 0) {
|
||||
// We don't care whether we matched or not, but positive indices are helpful.
|
||||
mStop = ~mStop;
|
||||
}
|
||||
if (!willMountViews) {
|
||||
// If we are not mounting views, we still need to update view indices and positions. It is
|
||||
// possible that a child changed size and we still need new clipping even though we are not
|
||||
// mounting views.
|
||||
updateClippingToCurrentRect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mountNodeRegions(NodeRegion[] nodeRegions, float[] maxBottom, float[] minTop) {
|
||||
mNodeRegions = nodeRegions;
|
||||
mRegionMaxBottom = maxBottom;
|
||||
mRegionMinTop = minTop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable NodeRegion virtualNodeRegionWithinBounds(float touchX, float touchY) {
|
||||
int i = Arrays.binarySearch(mRegionMinTop, touchY + 0.0001f);
|
||||
if (i < 0) {
|
||||
// We don't care whether we matched or not, but positive indices are helpful.
|
||||
i = ~i;
|
||||
}
|
||||
while (i-- > 0) {
|
||||
NodeRegion nodeRegion = mNodeRegions[i];
|
||||
if (!nodeRegion.mIsVirtual) {
|
||||
// only interested in virtual nodes
|
||||
continue;
|
||||
}
|
||||
if (mRegionMaxBottom[i] < touchY) {
|
||||
break;
|
||||
}
|
||||
if (nodeRegion.withinBounds(touchX, touchY)) {
|
||||
return nodeRegion;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable NodeRegion anyNodeRegionWithinBounds(float touchX, float touchY) {
|
||||
int i = Arrays.binarySearch(mRegionMinTop, touchY + 0.0001f);
|
||||
if (i < 0) {
|
||||
// We don't care whether we matched or not, but positive indices are helpful.
|
||||
i = ~i;
|
||||
}
|
||||
while (i-- > 0) {
|
||||
NodeRegion nodeRegion = mNodeRegions[i];
|
||||
if (mRegionMaxBottom[i] < touchY) {
|
||||
break;
|
||||
}
|
||||
if (nodeRegion.withinBounds(touchX, touchY)) {
|
||||
return nodeRegion;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void clip(int id, View view) {
|
||||
mClippedSubviews.put(id, view);
|
||||
}
|
||||
|
@ -78,13 +179,19 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
@Override
|
||||
public void mountViews(ViewResolver viewResolver, int[] viewsToAdd, int[] viewsToDetach) {
|
||||
for (int viewToAdd : viewsToAdd) {
|
||||
if (viewToAdd > 0) {
|
||||
// Views that are just temporarily detached are marked with a negative value.
|
||||
boolean newView = viewToAdd > 0;
|
||||
if (!newView) {
|
||||
viewToAdd = -viewToAdd;
|
||||
}
|
||||
int commandArrayIndex = mDrawViewIndexMap.get(viewToAdd);
|
||||
DrawView drawView = (DrawView) mDrawCommands[commandArrayIndex];
|
||||
View view = viewResolver.getView(drawView.reactTag);
|
||||
ensureViewHasNoParent(view);
|
||||
if (newView) {
|
||||
// This view was not previously attached to this parent.
|
||||
View view = viewResolver.getView(viewToAdd);
|
||||
ensureViewHasNoParent(view);
|
||||
DrawView drawView = Assertions.assertNotNull(mDrawViewMap.get(viewToAdd));
|
||||
drawView.mWasMounted = true;
|
||||
if (animating(view) || withinBounds(drawView)) {
|
||||
if (animating(view) || withinBounds(commandArrayIndex)) {
|
||||
// View should be drawn. This view can't currently be clipped because it wasn't
|
||||
// previously attached to this parent.
|
||||
mFlatViewGroup.addViewInLayout(view);
|
||||
|
@ -93,9 +200,6 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
}
|
||||
} else {
|
||||
// This view was previously attached, and just temporarily detached.
|
||||
DrawView drawView = Assertions.assertNotNull(mDrawViewMap.get(-viewToAdd));
|
||||
View view = viewResolver.getView(drawView.reactTag);
|
||||
ensureViewHasNoParent(view);
|
||||
if (drawView.mWasMounted) {
|
||||
// The DrawView has been mounted before.
|
||||
if (isNotClipped(drawView.reactTag)) {
|
||||
|
@ -109,7 +213,7 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
// The DrawView has not been mounted before, which means the bounds changed and triggered
|
||||
// a new DrawView when it was collected from the shadow node. We have a view with the
|
||||
// same id temporarily detached, but its bounds have changed.
|
||||
if (animating(view) || withinBounds(drawView)) {
|
||||
if (animating(view) || withinBounds(commandArrayIndex)) {
|
||||
// View should be drawn.
|
||||
if (isClipped(drawView.reactTag)) {
|
||||
// View was clipped, so add it.
|
||||
|
@ -151,13 +255,9 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
return animation != null && !animation.hasEnded();
|
||||
}
|
||||
|
||||
// Return true if a DrawView is currently onscreen.
|
||||
boolean withinBounds(DrawView drawView) {
|
||||
return mClippingRect.intersects(
|
||||
drawView.mLogicalLeft,
|
||||
drawView.mLogicalTop,
|
||||
drawView.mLogicalRight,
|
||||
drawView.mLogicalBottom);
|
||||
// Return true if a command index is currently onscreen.
|
||||
boolean withinBounds(int i) {
|
||||
return mStart <= i && i < mStop;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -169,35 +269,102 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
return false;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
boolean needsInvalidate = false;
|
||||
for (DrawCommand drawCommand : mDrawCommands) {
|
||||
if (drawCommand instanceof DrawView) {
|
||||
DrawView drawView = (DrawView) drawCommand;
|
||||
View view = mClippedSubviews.get(drawView.reactTag);
|
||||
if (view == null) {
|
||||
// Not clipped, visible
|
||||
view = mFlatViewGroup.getChildAt(index++);
|
||||
if (!animating(view) && !withinBounds(drawView)) {
|
||||
// Now off the screen. Don't invalidate in this case, as the canvas should not be
|
||||
// redrawn unless new elements are coming onscreen.
|
||||
clip(drawView.reactTag, view);
|
||||
mFlatViewGroup.removeViewsInLayout(--index, 1);
|
||||
}
|
||||
} else {
|
||||
// Clipped, invisible. We obviously aren't animating here, as if we were then we would not
|
||||
// have clipped in the first place.
|
||||
if (withinBounds(drawView)) {
|
||||
// Now on the screen. Invalidate as we have a new element to draw.
|
||||
unclip(drawView.reactTag);
|
||||
mFlatViewGroup.addViewInLayout(view, index++);
|
||||
needsInvalidate = true;
|
||||
}
|
||||
}
|
||||
int start = Arrays.binarySearch(mCommandMaxBottom, mClippingRect.top);
|
||||
if (start < 0) {
|
||||
// We don't care whether we matched or not, but positive indices are helpful.
|
||||
start = ~start;
|
||||
}
|
||||
int stop = Arrays.binarySearch(
|
||||
mCommandMinTop,
|
||||
start,
|
||||
mCommandMinTop.length,
|
||||
mClippingRect.bottom);
|
||||
if (stop < 0) {
|
||||
// We don't care whether we matched or not, but positive indices are helpful.
|
||||
stop = ~stop;
|
||||
}
|
||||
|
||||
if (mStart <= start && stop <= mStop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mStart = start;
|
||||
mStop = stop;
|
||||
|
||||
updateClippingToCurrentRect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateClippingToCurrentRect() {
|
||||
for (int i = 0, size = mFlatViewGroup.getChildCount(); i < size; i++) {
|
||||
View view = mFlatViewGroup.getChildAt(i);
|
||||
int index = mDrawViewIndexMap.get(view.getId());
|
||||
if (withinBounds(index) || animating(view)) {
|
||||
mViewsToKeep.add(view);
|
||||
} else {
|
||||
mViewsToRemove.append(i, view);
|
||||
clip(view.getId(), view);
|
||||
}
|
||||
}
|
||||
|
||||
return needsInvalidate;
|
||||
int removeSize = mViewsToRemove.size();
|
||||
boolean removeAll = removeSize > 2;
|
||||
|
||||
if (removeAll) {
|
||||
// Detach all, as we are changing quite a few views, whether flinging or otherwise.
|
||||
mFlatViewGroup.detachAllViewsFromParent();
|
||||
|
||||
for (int i = 0; i < removeSize; i++) {
|
||||
mFlatViewGroup.removeDetachedView(mViewsToRemove.valueAt(i));
|
||||
}
|
||||
} else {
|
||||
// Simple clipping sweep, as we are changing relatively few views.
|
||||
while (removeSize-- > 0) {
|
||||
mFlatViewGroup.removeViewsInLayout(mViewsToRemove.keyAt(removeSize), 1);
|
||||
}
|
||||
}
|
||||
mViewsToRemove.clear();
|
||||
|
||||
int current = mStart;
|
||||
int childIndex = 0;
|
||||
|
||||
for (int i = 0, size = mViewsToKeep.size(); i < size; i++) {
|
||||
View view = mViewsToKeep.get(i);
|
||||
int commandIndex = mDrawViewIndexMap.get(view.getId());
|
||||
if (current <= commandIndex) {
|
||||
while (current != commandIndex) {
|
||||
if (mDrawCommands[current] instanceof DrawView) {
|
||||
DrawView drawView = (DrawView) mDrawCommands[current];
|
||||
mFlatViewGroup.addViewInLayout(
|
||||
Assertions.assumeNotNull(mClippedSubviews.get(drawView.reactTag)),
|
||||
childIndex++);
|
||||
unclip(drawView.reactTag);
|
||||
}
|
||||
current++;
|
||||
}
|
||||
// We are currently at the command index, but we want to increment beyond it.
|
||||
current++;
|
||||
}
|
||||
if (removeAll) {
|
||||
mFlatViewGroup.attachViewToParent(view, childIndex);
|
||||
}
|
||||
// We want to make sure we increment the child index even if we didn't detach it to maintain
|
||||
// order.
|
||||
childIndex++;
|
||||
}
|
||||
mViewsToKeep.clear();
|
||||
|
||||
while (current < mStop) {
|
||||
if (mDrawCommands[current] instanceof DrawView) {
|
||||
DrawView drawView = (DrawView) mDrawCommands[current];
|
||||
mFlatViewGroup.addViewInLayout(
|
||||
Assertions.assumeNotNull(mClippedSubviews.get(drawView.reactTag)),
|
||||
childIndex++);
|
||||
unclip(drawView.reactTag);
|
||||
}
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -212,20 +379,42 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
for (DrawCommand drawCommand : mDrawCommands) {
|
||||
if (drawCommand instanceof DrawView) {
|
||||
if (isNotClipped(((DrawView) drawCommand).reactTag)) {
|
||||
drawCommand.draw(mFlatViewGroup, canvas);
|
||||
int commandIndex = mStart;
|
||||
int size = mFlatViewGroup.getChildCount();
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
int viewIndex = mDrawViewIndexMap.get(mFlatViewGroup.getChildAt(i).getId());
|
||||
if (mStop < viewIndex) {
|
||||
while (commandIndex < mStop) {
|
||||
mDrawCommands[commandIndex++].draw(mFlatViewGroup, canvas);
|
||||
}
|
||||
// else, don't draw, and don't increment index
|
||||
} else {
|
||||
drawCommand.draw(mFlatViewGroup, canvas);
|
||||
// We are now out of commands to draw, so we can just draw the remaining attached children.
|
||||
mDrawCommands[viewIndex].draw(mFlatViewGroup, canvas);
|
||||
while (++i != size) {
|
||||
viewIndex = mDrawViewIndexMap.get(mFlatViewGroup.getChildAt(i).getId());
|
||||
mDrawCommands[viewIndex].draw(mFlatViewGroup, canvas);
|
||||
}
|
||||
// Everything is drawn, lets get out of here.
|
||||
return;
|
||||
} else if (commandIndex <= viewIndex) {
|
||||
while (commandIndex < viewIndex) {
|
||||
mDrawCommands[commandIndex++].draw(mFlatViewGroup, canvas);
|
||||
}
|
||||
// Command index now == viewIndex, so increment beyond it.
|
||||
commandIndex++;
|
||||
}
|
||||
mDrawCommands[viewIndex].draw(mFlatViewGroup, canvas);
|
||||
}
|
||||
|
||||
// We have drawn all the views, now just draw the remaining draw commands.
|
||||
while (commandIndex < mStop) {
|
||||
mDrawCommands[commandIndex++].draw(mFlatViewGroup, canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void debugDraw(Canvas canvas) {
|
||||
// Draws clipped draw commands, but does not draw clipped views.
|
||||
for (DrawCommand drawCommand : mDrawCommands) {
|
||||
if (drawCommand instanceof DrawView) {
|
||||
if (isNotClipped(((DrawView) drawCommand).reactTag)) {
|
||||
|
|
|
@ -24,7 +24,8 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
/**
|
||||
* Abstract {@link DrawCommandManager} with directional clipping.
|
||||
*/
|
||||
/* package */ abstract class DirectionalClippingDrawCommandManager extends DrawCommandManager {
|
||||
/* package */ abstract class DirectionalClippingDrawCommandManager {
|
||||
// This will be fixed in the next diff!!!
|
||||
private final FlatViewGroup mFlatViewGroup;
|
||||
DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||
|
||||
|
@ -52,7 +53,6 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
updateClippingRect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mountDrawCommands(DrawCommand[] drawCommands) {
|
||||
mDrawCommands = drawCommands;
|
||||
mDrawViewMap.clear();
|
||||
|
@ -80,13 +80,12 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
return !mClippedSubviews.containsKey(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mountViews(ViewResolver viewResolver, int[] viewsToAdd, int[] viewsToDetach) {
|
||||
for (int viewToAdd : viewsToAdd) {
|
||||
if (viewToAdd > 0) {
|
||||
// This view was not previously attached to this parent.
|
||||
View view = viewResolver.getView(viewToAdd);
|
||||
ensureViewHasNoParent(view);
|
||||
// ensureViewHasNoParent(view);
|
||||
DrawView drawView = Assertions.assertNotNull(mDrawViewMap.get(viewToAdd));
|
||||
drawView.mWasMounted = true;
|
||||
if (animating(view) || withinBounds(drawView)) {
|
||||
|
@ -100,7 +99,7 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
// This view was previously attached, and just temporarily detached.
|
||||
DrawView drawView = Assertions.assertNotNull(mDrawViewMap.get(-viewToAdd));
|
||||
View view = viewResolver.getView(drawView.reactTag);
|
||||
ensureViewHasNoParent(view);
|
||||
// ensureViewHasNoParent(view);
|
||||
if (drawView.mWasMounted) {
|
||||
// The DrawView has been mounted before.
|
||||
if (isNotClipped(drawView.reactTag)) {
|
||||
|
@ -161,7 +160,6 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
return !(beforeRect(drawView) || afterRect(drawView));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateClippingRect() {
|
||||
ReactClippingViewGroupHelper.calculateClippingRect(mFlatViewGroup, mClippingRect);
|
||||
if (mFlatViewGroup.getParent() == null || mClippingRect.top == mClippingRect.bottom) {
|
||||
|
@ -201,17 +199,14 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
return needsInvalidate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getClippingRect(Rect outClippingRect) {
|
||||
outClippingRect.set(mClippingRect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<View> getDetachedViews() {
|
||||
return mClippedSubviews.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
for (DrawCommand drawCommand : mDrawCommands) {
|
||||
if (drawCommand instanceof DrawView) {
|
||||
|
@ -225,7 +220,6 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void debugDraw(Canvas canvas) {
|
||||
for (DrawCommand drawCommand : mDrawCommands) {
|
||||
if (drawCommand instanceof DrawView) {
|
||||
|
|
|
@ -16,7 +16,7 @@ import android.graphics.Canvas;
|
|||
* Instances of DrawCommand are created in background thread and passed to UI thread.
|
||||
* Once a DrawCommand is shared with UI thread, it can no longer be mutated in background thread.
|
||||
*/
|
||||
public interface DrawCommand {
|
||||
public abstract class DrawCommand {
|
||||
// used by StateBuilder, FlatViewGroup and FlatShadowNode
|
||||
/* package */ static final DrawCommand[] EMPTY_ARRAY = new DrawCommand[0];
|
||||
|
||||
|
@ -26,7 +26,7 @@ public interface DrawCommand {
|
|||
* @param parent The parent to get child information from, if needed
|
||||
* @param canvas The canvas to draw into
|
||||
*/
|
||||
public void draw(FlatViewGroup parent, Canvas canvas);
|
||||
abstract void draw(FlatViewGroup parent, Canvas canvas);
|
||||
|
||||
/**
|
||||
* Performs debug bounds drawing into the given canvas.
|
||||
|
@ -34,5 +34,13 @@ public interface DrawCommand {
|
|||
* @param parent The parent to get child information from, if needed
|
||||
* @param canvas The canvas to draw into
|
||||
*/
|
||||
public void debugDraw(FlatViewGroup parent, Canvas canvas);
|
||||
abstract void debugDraw(FlatViewGroup parent, Canvas canvas);
|
||||
|
||||
abstract float getLeft();
|
||||
|
||||
abstract float getTop();
|
||||
|
||||
abstract float getRight();
|
||||
|
||||
abstract float getBottom();
|
||||
}
|
||||
|
|
|
@ -9,15 +9,18 @@
|
|||
|
||||
package com.facebook.react.flat;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewParent;
|
||||
|
||||
/**
|
||||
* Underlying logic behind handling draw commands from {@link FlatViewGroup}.
|
||||
* Underlying logic behind handling clipping draw commands from {@link FlatViewGroup}.
|
||||
*/
|
||||
/* package */ abstract class DrawCommandManager {
|
||||
|
||||
|
@ -27,8 +30,20 @@ import android.view.ViewParent;
|
|||
* called after by the UIManager.
|
||||
*
|
||||
* @param drawCommands The draw commands to mount.
|
||||
* @param drawViewIndexMap Mapping of ids to index position within the draw command array.
|
||||
* @param maxBottom At each index i, the maximum bottom value (or right value in the case of
|
||||
* horizontal clipping) value of all draw commands at or below i.
|
||||
* @param minTop At each index i, the minimum top value (or left value in the case of horizontal
|
||||
* clipping) value of all draw commands at or below i.
|
||||
* @param willMountViews Whether we are going to also receive a mountViews command in this state
|
||||
* cycle.
|
||||
*/
|
||||
abstract void mountDrawCommands(DrawCommand[] drawCommands);
|
||||
abstract void mountDrawCommands(
|
||||
DrawCommand[] drawCommands,
|
||||
SparseIntArray drawViewIndexMap,
|
||||
float[] maxBottom,
|
||||
float[] minTop,
|
||||
boolean willMountViews);
|
||||
|
||||
/**
|
||||
* Add and detach a set of views. The views added here will already have a DrawView passed in
|
||||
|
@ -78,6 +93,36 @@ import android.view.ViewParent;
|
|||
*/
|
||||
abstract void debugDraw(Canvas canvas);
|
||||
|
||||
/**
|
||||
* Mount node regions, which are the hit boxes of the shadow node children of this FlatViewGroup,
|
||||
* though some may not have a corresponding draw command.
|
||||
*
|
||||
* @param nodeRegions Array of node regions to mount.
|
||||
* @param maxBottom At each index i, the maximum bottom value (or right value in the case of
|
||||
* horizontal clipping) value of all node regions at or below i.
|
||||
* @param minTop At each index i, the minimum top value (or left value in the case of horizontal
|
||||
* clipping) value of all draw commands at or below i.
|
||||
*/
|
||||
abstract void mountNodeRegions(NodeRegion[] nodeRegions, float[] maxBottom, float[] minTop);
|
||||
|
||||
/**
|
||||
* Find a matching node region for a touch.
|
||||
*
|
||||
* @param touchX X coordinate of touch.
|
||||
* @param touchY Y coordinate of touch.
|
||||
* @return Matching node region, or null if none are found.
|
||||
*/
|
||||
abstract @Nullable NodeRegion anyNodeRegionWithinBounds(float touchX, float touchY);
|
||||
|
||||
/**
|
||||
* Find a matching virtual node region for a touch.
|
||||
*
|
||||
* @param touchX X coordinate of touch.
|
||||
* @param touchY Y coordinate of touch.
|
||||
* @return Matching node region, or null if none are found.
|
||||
*/
|
||||
abstract @Nullable NodeRegion virtualNodeRegionWithinBounds(float touchX, float touchY);
|
||||
|
||||
/**
|
||||
* Throw a runtime exception if a view we are trying to attach is already parented.
|
||||
*
|
||||
|
@ -96,16 +141,4 @@ import android.view.ViewParent;
|
|||
DrawCommand[] drawCommands) {
|
||||
return new ClippingDrawCommandManager(flatViewGroup, drawCommands);
|
||||
}
|
||||
|
||||
static DrawCommandManager getVerticalClippingInstance(
|
||||
FlatViewGroup flatViewGroup,
|
||||
DrawCommand[] drawCommands) {
|
||||
return new VerticalClippingDrawCommandManager(flatViewGroup, drawCommands);
|
||||
}
|
||||
|
||||
static DrawCommandManager getHorizontalClippingInstance(
|
||||
FlatViewGroup flatViewGroup,
|
||||
DrawCommand[] drawCommands) {
|
||||
return new HorizontalClippingDrawCommandManager(flatViewGroup, drawCommands);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import com.facebook.react.bridge.ReadableArray;
|
|||
/**
|
||||
* Common interface for DrawImageWithPipeline and DrawImageWithDrawee.
|
||||
*/
|
||||
/* package */ interface DrawImage extends DrawCommand, AttachDetachListener {
|
||||
/* package */ interface DrawImage extends AttachDetachListener {
|
||||
/**
|
||||
* Returns true if an image source was assigned to the DrawImage.
|
||||
* A DrawImage with no source will not draw anything.
|
||||
|
|
|
@ -16,6 +16,7 @@ import android.graphics.Path;
|
|||
import android.graphics.RectF;
|
||||
|
||||
/* package */ final class DrawView extends AbstractDrawCommand {
|
||||
public static final DrawView[] EMPTY_ARRAY = new DrawView[0];
|
||||
// the minimum rounded clipping value before we actually do rounded clipping
|
||||
/* package */ static final float MINIMUM_ROUNDED_CLIPPING_VALUE = 0.5f;
|
||||
private final RectF TMP_RECT = new RectF();
|
||||
|
|
|
@ -15,7 +15,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.View;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -82,6 +82,55 @@ import com.facebook.react.uimanager.ViewManagerRegistry;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates DrawCommands and AttachDetachListeners of a clipping FlatViewGroup specified by a
|
||||
* reactTag.
|
||||
*
|
||||
* @param reactTag The react tag to lookup FlatViewGroup by.
|
||||
* @param drawCommands If non-null, new draw commands to execute during the drawing.
|
||||
* @param drawViewIndexMap Mapping of react tags to the index of the corresponding DrawView
|
||||
* command in the draw command array.
|
||||
* @param commandMaxBot At each index i, the maximum bottom value (or right value in the case of
|
||||
* horizontal clipping) value of all draw commands at or below i.
|
||||
* @param commandMinTop At each index i, the minimum top value (or left value in the case of
|
||||
* horizontal clipping) value of all draw commands at or below i.
|
||||
* @param listeners If non-null, new attach-detach listeners.
|
||||
* @param nodeRegions Node regions to mount.
|
||||
* @param regionMaxBot At each index i, the maximum bottom value (or right value in the case of
|
||||
* horizontal clipping) value of all node regions at or below i.
|
||||
* @param regionMinTop At each index i, the minimum top value (or left value in the case of
|
||||
* horizontal clipping) value of all draw commands at or below i.
|
||||
* @param willMountViews Whether we are going to also send a mountViews command in this state
|
||||
* cycle.
|
||||
*/
|
||||
/* package */ void updateClippingMountState(
|
||||
int reactTag,
|
||||
@Nullable DrawCommand[] drawCommands,
|
||||
SparseIntArray drawViewIndexMap,
|
||||
float[] commandMaxBot,
|
||||
float[] commandMinTop,
|
||||
@Nullable AttachDetachListener[] listeners,
|
||||
@Nullable NodeRegion[] nodeRegions,
|
||||
float[] regionMaxBot,
|
||||
float[] regionMinTop,
|
||||
boolean willMountViews) {
|
||||
FlatViewGroup view = (FlatViewGroup) resolveView(reactTag);
|
||||
if (drawCommands != null) {
|
||||
view.mountClippingDrawCommands(
|
||||
drawCommands,
|
||||
drawViewIndexMap,
|
||||
commandMaxBot,
|
||||
commandMinTop,
|
||||
willMountViews);
|
||||
}
|
||||
if (listeners != null) {
|
||||
view.mountAttachDetachListeners(listeners);
|
||||
}
|
||||
if (nodeRegions != null) {
|
||||
view.mountClippingNodeRegions(nodeRegions, regionMaxBot, regionMinTop);
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ void updateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) {
|
||||
View view = resolveView(reactTag);
|
||||
if (view instanceof FlatViewGroup) {
|
||||
|
|
|
@ -41,8 +41,9 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
private static final String PROP_IMPORTANT_FOR_ACCESSIBILITY = "importantForAccessibility";
|
||||
private static final String PROP_TEST_ID = "testID";
|
||||
private static final String PROP_TRANSFORM = "transform";
|
||||
private static final String PROP_REMOVE_CLIPPED_SUBVIEWS =
|
||||
protected static final String PROP_REMOVE_CLIPPED_SUBVIEWS =
|
||||
ReactClippingViewGroupHelper.PROP_REMOVE_CLIPPED_SUBVIEWS;
|
||||
protected static final String PROP_HORIZONTAL = "horizontal";
|
||||
private static final Rect LOGICAL_OFFSET_EMPTY = new Rect();
|
||||
// When we first initialize a backing view, we create a view we are going to throw away anyway,
|
||||
// so instead initialize with a shared view.
|
||||
|
@ -542,4 +543,8 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
/* package */ final void signalBackingViewIsCreated() {
|
||||
mBackingViewIsCreated = true;
|
||||
}
|
||||
|
||||
public boolean clipsSubviews() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ package com.facebook.react.flat;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
|
@ -73,6 +73,61 @@ import com.facebook.react.uimanager.UIViewOperationQueue;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UIOperation that updates DrawCommands for a View defined by reactTag.
|
||||
*/
|
||||
private final class UpdateClippingMountState implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
private final @Nullable DrawCommand[] mDrawCommands;
|
||||
private final SparseIntArray mDrawViewIndexMap;
|
||||
private final float[] mCommandMaxBot;
|
||||
private final float[] mCommandMinTop;
|
||||
private final @Nullable AttachDetachListener[] mAttachDetachListeners;
|
||||
private final @Nullable NodeRegion[] mNodeRegions;
|
||||
private final float[] mRegionMaxBot;
|
||||
private final float[] mRegionMinTop;
|
||||
private final boolean mWillMountViews;
|
||||
|
||||
private UpdateClippingMountState(
|
||||
int reactTag,
|
||||
@Nullable DrawCommand[] drawCommands,
|
||||
SparseIntArray drawViewIndexMap,
|
||||
float[] commandMaxBot,
|
||||
float[] commandMinTop,
|
||||
@Nullable AttachDetachListener[] listeners,
|
||||
@Nullable NodeRegion[] nodeRegions,
|
||||
float[] regionMaxBot,
|
||||
float[] regionMinTop,
|
||||
boolean willMountViews) {
|
||||
mReactTag = reactTag;
|
||||
mDrawCommands = drawCommands;
|
||||
mDrawViewIndexMap = drawViewIndexMap;
|
||||
mCommandMaxBot = commandMaxBot;
|
||||
mCommandMinTop = commandMinTop;
|
||||
mAttachDetachListeners = listeners;
|
||||
mNodeRegions = nodeRegions;
|
||||
mRegionMaxBot = regionMaxBot;
|
||||
mRegionMinTop = regionMinTop;
|
||||
mWillMountViews = willMountViews;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
mNativeViewHierarchyManager.updateClippingMountState(
|
||||
mReactTag,
|
||||
mDrawCommands,
|
||||
mDrawViewIndexMap,
|
||||
mCommandMaxBot,
|
||||
mCommandMinTop,
|
||||
mAttachDetachListeners,
|
||||
mNodeRegions,
|
||||
mRegionMaxBot,
|
||||
mRegionMinTop,
|
||||
mWillMountViews);
|
||||
}
|
||||
}
|
||||
|
||||
private final class UpdateViewGroup implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
|
@ -359,6 +414,33 @@ import com.facebook.react.uimanager.UIViewOperationQueue;
|
|||
nodeRegions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues a new UIOperation that will update DrawCommands for a View defined by reactTag.
|
||||
*/
|
||||
public void enqueueUpdateClippingMountState(
|
||||
int reactTag,
|
||||
@Nullable DrawCommand[] drawCommands,
|
||||
SparseIntArray drawViewIndexMap,
|
||||
float[] commandMaxBot,
|
||||
float[] commandMinTop,
|
||||
@Nullable AttachDetachListener[] listeners,
|
||||
@Nullable NodeRegion[] nodeRegions,
|
||||
float[] regionMaxBot,
|
||||
float[] regionMinTop,
|
||||
boolean willMountViews) {
|
||||
enqueueUIOperation(new UpdateClippingMountState(
|
||||
reactTag,
|
||||
drawCommands,
|
||||
drawViewIndexMap,
|
||||
commandMaxBot,
|
||||
commandMinTop,
|
||||
listeners,
|
||||
nodeRegions,
|
||||
regionMaxBot,
|
||||
regionMinTop,
|
||||
willMountViews));
|
||||
}
|
||||
|
||||
public void enqueueUpdateViewGroup(int reactTag, int[] viewsToAdd, int[] viewsToDetach) {
|
||||
enqueueUIOperation(new UpdateViewGroup(reactTag, viewsToAdd, viewsToDetach));
|
||||
}
|
||||
|
|
|
@ -22,11 +22,13 @@ import android.graphics.Color;
|
|||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.SoftAssertions;
|
||||
import com.facebook.react.touch.OnInterceptTouchEventListener;
|
||||
|
@ -90,7 +92,6 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
|
||||
private static final ArrayList<FlatViewGroup> LAYOUT_REQUESTS = new ArrayList<>();
|
||||
private static final Rect VIEW_BOUNDS = new Rect();
|
||||
private static final Rect EMPTY_RECT = new Rect();
|
||||
|
||||
private @Nullable InvalidateCallback mInvalidateCallback;
|
||||
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||
|
@ -565,11 +566,22 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
}
|
||||
|
||||
/* package */ void mountDrawCommands(DrawCommand[] drawCommands) {
|
||||
if (mDrawCommandManager != null) {
|
||||
mDrawCommandManager.mountDrawCommands(drawCommands);
|
||||
} else {
|
||||
mDrawCommands = drawCommands;
|
||||
}
|
||||
mDrawCommands = drawCommands;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/* package */ void mountClippingDrawCommands(
|
||||
DrawCommand[] drawCommands,
|
||||
SparseIntArray drawViewIndexMap,
|
||||
float[] maxBottom,
|
||||
float[] minTop,
|
||||
boolean willMountViews) {
|
||||
Assertions.assertNotNull(mDrawCommandManager).mountDrawCommands(
|
||||
drawCommands,
|
||||
drawViewIndexMap,
|
||||
maxBottom,
|
||||
minTop,
|
||||
willMountViews);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
@ -646,6 +658,14 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
mNodeRegions = nodeRegions;
|
||||
}
|
||||
|
||||
/* package */ void mountClippingNodeRegions(
|
||||
NodeRegion[] nodeRegions,
|
||||
float[] maxBottom,
|
||||
float[] minTop) {
|
||||
mNodeRegions = nodeRegions;
|
||||
Assertions.assertNotNull(mDrawCommandManager).mountNodeRegions(nodeRegions, maxBottom, minTop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount a list of views to add, and dismount a list of views to detach. Ids will not appear in
|
||||
* both lists, aka:
|
||||
|
@ -712,6 +732,10 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
attachViewToParent(view, -1, ensureLayoutParams(view.getLayoutParams()));
|
||||
}
|
||||
|
||||
/* package */ void attachViewToParent(View view, int index) {
|
||||
attachViewToParent(view, index, ensureLayoutParams(view.getLayoutParams()));
|
||||
}
|
||||
|
||||
private void processLayoutRequest() {
|
||||
mIsLayoutRequested = false;
|
||||
for (int i = 0, childCount = getChildCount(); i != childCount; ++i) {
|
||||
|
@ -769,6 +793,9 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
}
|
||||
|
||||
private @Nullable NodeRegion virtualNodeRegionWithinBounds(float touchX, float touchY) {
|
||||
if (mDrawCommandManager != null) {
|
||||
return mDrawCommandManager.virtualNodeRegionWithinBounds(touchX, touchY);
|
||||
}
|
||||
for (int i = mNodeRegions.length - 1; i >= 0; --i) {
|
||||
NodeRegion nodeRegion = mNodeRegions[i];
|
||||
if (!nodeRegion.mIsVirtual) {
|
||||
|
@ -784,6 +811,9 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
}
|
||||
|
||||
private @Nullable NodeRegion anyNodeRegionWithinBounds(float touchX, float touchY) {
|
||||
if (mDrawCommandManager != null) {
|
||||
return mDrawCommandManager.anyNodeRegionWithinBounds(touchX, touchY);
|
||||
}
|
||||
for (int i = mNodeRegions.length - 1; i >= 0; --i) {
|
||||
NodeRegion nodeRegion = mNodeRegions[i];
|
||||
if (nodeRegion.withinBounds(touchX, touchY)) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import javax.annotation.Nullable;
|
|||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.ReactStylesDiffMap;
|
||||
import com.facebook.react.uimanager.ViewProps;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.facebook.react.uimanager.annotations.ReactPropGroup;
|
||||
|
@ -21,6 +22,23 @@ import com.facebook.react.uimanager.annotations.ReactPropGroup;
|
|||
|
||||
private @Nullable DrawBorder mDrawBorder;
|
||||
|
||||
boolean mRemoveClippedSubviews;
|
||||
boolean mHorizontal;
|
||||
|
||||
@Override
|
||||
/* package */ void handleUpdateProperties(ReactStylesDiffMap styles) {
|
||||
mRemoveClippedSubviews = mRemoveClippedSubviews ||
|
||||
(styles.hasKey(PROP_REMOVE_CLIPPED_SUBVIEWS) &&
|
||||
styles.getBoolean(PROP_REMOVE_CLIPPED_SUBVIEWS, false));
|
||||
|
||||
if (mRemoveClippedSubviews) {
|
||||
mHorizontal = mHorizontal ||
|
||||
(styles.hasKey(PROP_HORIZONTAL) && styles.getBoolean(PROP_HORIZONTAL, false));
|
||||
}
|
||||
|
||||
super.handleUpdateProperties(styles);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void collectState(
|
||||
StateBuilder stateBuilder,
|
||||
|
@ -119,4 +137,9 @@ import com.facebook.react.uimanager.annotations.ReactPropGroup;
|
|||
invalidate();
|
||||
return mDrawBorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clipsSubviews() {
|
||||
return mRemoveClippedSubviews;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@ import javax.annotation.Nullable;
|
|||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import com.facebook.csslayout.Spacing;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.uimanager.OnLayoutEvent;
|
||||
|
@ -28,6 +31,9 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
* that Android finally can display.
|
||||
*/
|
||||
/* package */ final class StateBuilder {
|
||||
/* package */ static final float[] EMPTY_FLOAT_ARRAY = new float[0];
|
||||
/* package */ static final SparseArray<DrawView> EMPTY_SPARSE_DRAWVIEW = new SparseArray<>();
|
||||
/* package */ static final SparseIntArray EMPTY_SPARSE_INT = new SparseIntArray();
|
||||
|
||||
private static final boolean SKIP_UP_TO_DATE_NODES = true;
|
||||
|
||||
|
@ -329,12 +335,86 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
node.updateOverflowsContainer();
|
||||
}
|
||||
|
||||
// We need to finish the native children so that we can process clipping FlatViewGroup.
|
||||
final FlatShadowNode[] nativeChildren = mNativeChildren.finish();
|
||||
if (shouldUpdateMountState) {
|
||||
mOperationsQueue.enqueueUpdateMountState(
|
||||
node.getReactTag(),
|
||||
drawCommands,
|
||||
listeners,
|
||||
nodeRegions);
|
||||
if (node.clipsSubviews()) {
|
||||
// Node is a clipping FlatViewGroup, so lets do some calculations off the UI thread.
|
||||
// DrawCommandManager has a better explanation of the data incoming from these calculations,
|
||||
// and is where they are actually used.
|
||||
float[] commandMaxBottom = EMPTY_FLOAT_ARRAY;
|
||||
float[] commandMinTop = EMPTY_FLOAT_ARRAY;
|
||||
SparseIntArray drawViewIndexMap = EMPTY_SPARSE_INT;
|
||||
if (drawCommands != null) {
|
||||
drawViewIndexMap = new SparseIntArray();
|
||||
|
||||
commandMaxBottom = new float[drawCommands.length];
|
||||
commandMinTop = new float[drawCommands.length];
|
||||
|
||||
float last = 0;
|
||||
// Loop through the DrawCommands, keeping track of the maximum y we've seen if we only
|
||||
// iterated through items up to this position
|
||||
for (int i = 0; i < drawCommands.length; i++) {
|
||||
if (drawCommands[i] instanceof DrawView) {
|
||||
DrawView drawView = (DrawView) drawCommands[i];
|
||||
// These will generally be roughly sorted by id, so try to insert at the end if
|
||||
// possible.
|
||||
drawViewIndexMap.append(drawView.reactTag, i);
|
||||
last = Math.max(last, drawView.mLogicalBottom);
|
||||
} else {
|
||||
last = Math.max(last, drawCommands[i].getBottom());
|
||||
}
|
||||
commandMaxBottom[i] = last;
|
||||
}
|
||||
// Intentionally leave last as it was, since it's at the maximum bottom position we've
|
||||
// seen so far, we can use it again.
|
||||
// Loop through the DrawCommands backwards, keeping track of the minimum y we've seen at
|
||||
// this position
|
||||
for (int i = drawCommands.length - 1; i >= 0; i--) {
|
||||
if (drawCommands[i] instanceof DrawView) {
|
||||
last = Math.min(last, ((DrawView) drawCommands[i]).mLogicalTop);
|
||||
} else {
|
||||
last = Math.min(last, drawCommands[i].getTop());
|
||||
}
|
||||
commandMinTop[i] = last;
|
||||
}
|
||||
}
|
||||
float[] regionMaxBottom = EMPTY_FLOAT_ARRAY;
|
||||
float[] regionMinTop = EMPTY_FLOAT_ARRAY;
|
||||
if (nodeRegions != null) {
|
||||
regionMaxBottom = new float[nodeRegions.length];
|
||||
regionMinTop = new float[nodeRegions.length];
|
||||
|
||||
float last = 0;
|
||||
for (int i = 0; i < nodeRegions.length; i++) {
|
||||
last = Math.max(last, nodeRegions[i].mBottom);
|
||||
regionMaxBottom[i] = last;
|
||||
}
|
||||
for (int i = nodeRegions.length - 1; i >= 0; i--) {
|
||||
last = Math.min(last, nodeRegions[i].mTop);
|
||||
regionMinTop[i] = last;
|
||||
}
|
||||
}
|
||||
|
||||
boolean willMountViews = nativeChildren != null;
|
||||
mOperationsQueue.enqueueUpdateClippingMountState(
|
||||
node.getReactTag(),
|
||||
drawCommands,
|
||||
drawViewIndexMap,
|
||||
commandMaxBottom,
|
||||
commandMinTop,
|
||||
listeners,
|
||||
nodeRegions,
|
||||
regionMaxBottom,
|
||||
regionMinTop,
|
||||
willMountViews);
|
||||
} else {
|
||||
mOperationsQueue.enqueueUpdateMountState(
|
||||
node.getReactTag(),
|
||||
drawCommands,
|
||||
listeners,
|
||||
nodeRegions);
|
||||
}
|
||||
}
|
||||
|
||||
if (node.hasUnseenUpdates()) {
|
||||
|
@ -342,7 +422,6 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
node.markUpdateSeen();
|
||||
}
|
||||
|
||||
final FlatShadowNode[] nativeChildren = mNativeChildren.finish();
|
||||
if (nativeChildren != null) {
|
||||
updateNativeChildren(node, node.getNativeChildren(), nativeChildren);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue