Support vertical and horizontal clipping with draw command managers.
Summary: Adds support for horizontal clipping, though the FlatViewGroup needs to be made aware still of which it is. Reviewed By: ahmedre Differential Revision: D3673501
This commit is contained in:
parent
192c99a4f6
commit
a4c4a88e27
|
@ -12,7 +12,6 @@ package com.facebook.react.flat;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -31,15 +30,15 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||||
* Implementation of a {@link DrawCommandManager} with clipping. Performs drawing by iterating
|
* Implementation of a {@link DrawCommandManager} with clipping. Performs drawing by iterating
|
||||||
* over an array of DrawCommands, executing them one by one except when the commands are clipped.
|
* over an array of DrawCommands, executing them one by one except when the commands are clipped.
|
||||||
*/
|
*/
|
||||||
/* package */ final class ClippingDrawCommandManager extends DrawCommandManager {
|
/* package */ abstract class ClippingDrawCommandManager extends DrawCommandManager {
|
||||||
private final FlatViewGroup mFlatViewGroup;
|
private final FlatViewGroup mFlatViewGroup;
|
||||||
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||||
private float[] mCommandMaxBottom = StateBuilder.EMPTY_FLOAT_ARRAY;
|
protected float[] mCommandMaxBottom = StateBuilder.EMPTY_FLOAT_ARRAY;
|
||||||
private float[] mCommandMinTop = StateBuilder.EMPTY_FLOAT_ARRAY;
|
protected float[] mCommandMinTop = StateBuilder.EMPTY_FLOAT_ARRAY;
|
||||||
|
|
||||||
private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY;
|
private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY;
|
||||||
private float[] mRegionMaxBottom = StateBuilder.EMPTY_FLOAT_ARRAY;
|
protected float[] mRegionMaxBottom = StateBuilder.EMPTY_FLOAT_ARRAY;
|
||||||
private float[] mRegionMinTop = StateBuilder.EMPTY_FLOAT_ARRAY;
|
protected float[] mRegionMinTop = StateBuilder.EMPTY_FLOAT_ARRAY;
|
||||||
|
|
||||||
// Onscreen bounds of draw command array.
|
// Onscreen bounds of draw command array.
|
||||||
private int mStart;
|
private int mStart;
|
||||||
|
@ -51,7 +50,7 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||||
// Map of views that are currently clipped.
|
// Map of views that are currently clipped.
|
||||||
private final Map<Integer, View> mClippedSubviews = new HashMap<>();
|
private final Map<Integer, View> mClippedSubviews = new HashMap<>();
|
||||||
|
|
||||||
private final Rect mClippingRect = new Rect();
|
protected final Rect mClippingRect = new Rect();
|
||||||
|
|
||||||
// Used in updating the clipping rect, as sometimes we want to detach all views, which means we
|
// 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
|
// need to temporarily store the views we are detaching and removing. These are always of size
|
||||||
|
@ -74,6 +73,33 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||||
updateClippingRect();
|
updateClippingRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return index of the first command that could be on the screen.
|
||||||
|
*/
|
||||||
|
abstract int commandStartIndex();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return index of the first command that is guaranteed to be off the screen, starting from the
|
||||||
|
* given start.
|
||||||
|
*/
|
||||||
|
abstract int commandStopIndex(int start);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return index of the first region that is guaranteed to be outside of the bounds for touch.
|
||||||
|
*/
|
||||||
|
abstract int regionStopIndex(float touchX, float touchY);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether an index and all indices before it are guaranteed to be out of bounds for the current
|
||||||
|
* touch.
|
||||||
|
*
|
||||||
|
* @param index The region index to check.
|
||||||
|
* @param touchX X coordinate.
|
||||||
|
* @param touchY Y coordinate.
|
||||||
|
* @return true if the index and all before it are out of bounds.
|
||||||
|
*/
|
||||||
|
abstract boolean regionAboveTouch(int index, float touchX, float touchY);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mountDrawCommands(
|
public void mountDrawCommands(
|
||||||
DrawCommand[] drawCommands,
|
DrawCommand[] drawCommands,
|
||||||
|
@ -86,20 +112,8 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||||
mCommandMinTop = minTop;
|
mCommandMinTop = minTop;
|
||||||
mDrawViewIndexMap = drawViewIndexMap;
|
mDrawViewIndexMap = drawViewIndexMap;
|
||||||
if (mClippingRect.bottom != mClippingRect.top) {
|
if (mClippingRect.bottom != mClippingRect.top) {
|
||||||
mStart = Arrays.binarySearch(mCommandMaxBottom, mClippingRect.top);
|
mStart = commandStartIndex();
|
||||||
if (mStart < 0) {
|
mStop = commandStopIndex(mStart);
|
||||||
// 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 (!willMountViews) {
|
||||||
// If we are not mounting views, we still need to update view indices and positions. It is
|
// 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
|
// possible that a child changed size and we still need new clipping even though we are not
|
||||||
|
@ -118,18 +132,14 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable NodeRegion virtualNodeRegionWithinBounds(float touchX, float touchY) {
|
public @Nullable NodeRegion virtualNodeRegionWithinBounds(float touchX, float touchY) {
|
||||||
int i = Arrays.binarySearch(mRegionMinTop, touchY + 0.0001f);
|
int i = regionStopIndex(touchX, touchY);
|
||||||
if (i < 0) {
|
|
||||||
// We don't care whether we matched or not, but positive indices are helpful.
|
|
||||||
i = ~i;
|
|
||||||
}
|
|
||||||
while (i-- > 0) {
|
while (i-- > 0) {
|
||||||
NodeRegion nodeRegion = mNodeRegions[i];
|
NodeRegion nodeRegion = mNodeRegions[i];
|
||||||
if (!nodeRegion.mIsVirtual) {
|
if (!nodeRegion.mIsVirtual) {
|
||||||
// only interested in virtual nodes
|
// only interested in virtual nodes
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (mRegionMaxBottom[i] < touchY) {
|
if (regionAboveTouch(i, touchX, touchY)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (nodeRegion.withinBounds(touchX, touchY)) {
|
if (nodeRegion.withinBounds(touchX, touchY)) {
|
||||||
|
@ -142,14 +152,10 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable NodeRegion anyNodeRegionWithinBounds(float touchX, float touchY) {
|
public @Nullable NodeRegion anyNodeRegionWithinBounds(float touchX, float touchY) {
|
||||||
int i = Arrays.binarySearch(mRegionMinTop, touchY + 0.0001f);
|
int i = regionStopIndex(touchX, touchY);
|
||||||
if (i < 0) {
|
|
||||||
// We don't care whether we matched or not, but positive indices are helpful.
|
|
||||||
i = ~i;
|
|
||||||
}
|
|
||||||
while (i-- > 0) {
|
while (i-- > 0) {
|
||||||
NodeRegion nodeRegion = mNodeRegions[i];
|
NodeRegion nodeRegion = mNodeRegions[i];
|
||||||
if (mRegionMaxBottom[i] < touchY) {
|
if (regionAboveTouch(i, touchX, touchY)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (nodeRegion.withinBounds(touchX, touchY)) {
|
if (nodeRegion.withinBounds(touchX, touchY)) {
|
||||||
|
@ -250,7 +256,7 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if a view is currently animating.
|
// Returns true if a view is currently animating.
|
||||||
static boolean animating(View view) {
|
private static boolean animating(View view) {
|
||||||
Animation animation = view.getAnimation();
|
Animation animation = view.getAnimation();
|
||||||
return animation != null && !animation.hasEnded();
|
return animation != null && !animation.hasEnded();
|
||||||
}
|
}
|
||||||
|
@ -269,22 +275,11 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int start = Arrays.binarySearch(mCommandMaxBottom, mClippingRect.top);
|
int start = commandStartIndex();
|
||||||
if (start < 0) {
|
int stop = commandStopIndex(start);
|
||||||
// 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) {
|
if (mStart <= start && stop <= mStop) {
|
||||||
|
// We would only be removing children, don't invalidate and don't bother changing the
|
||||||
|
// attached children.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,21 +377,24 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||||
int commandIndex = mStart;
|
int commandIndex = mStart;
|
||||||
int size = mFlatViewGroup.getChildCount();
|
int size = mFlatViewGroup.getChildCount();
|
||||||
|
|
||||||
|
// Iterate through the children, making sure that we draw any draw commands we haven't drawn
|
||||||
|
// that should happen before the next draw view.
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
|
// This is the command index of the next view that we need to draw. Since a view might be
|
||||||
|
// animating, this view is either before all the commands onscreen, onscreen, or after the
|
||||||
|
// onscreen commands.
|
||||||
int viewIndex = mDrawViewIndexMap.get(mFlatViewGroup.getChildAt(i).getId());
|
int viewIndex = mDrawViewIndexMap.get(mFlatViewGroup.getChildAt(i).getId());
|
||||||
if (mStop < viewIndex) {
|
if (mStop < viewIndex) {
|
||||||
|
// The current view is outside of the viewport bounds. We want to draw all the commands
|
||||||
|
// up to the stop, then draw all the views outside the viewport bounds.
|
||||||
while (commandIndex < mStop) {
|
while (commandIndex < mStop) {
|
||||||
mDrawCommands[commandIndex++].draw(mFlatViewGroup, canvas);
|
mDrawCommands[commandIndex++].draw(mFlatViewGroup, canvas);
|
||||||
}
|
}
|
||||||
// We are now out of commands to draw, so we can just draw the remaining attached children.
|
// We are now out of commands to draw, so we could just draw the remaining attached
|
||||||
mDrawCommands[viewIndex].draw(mFlatViewGroup, canvas);
|
// children, but the for loop logic will draw the rest anyway.
|
||||||
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) {
|
} else if (commandIndex <= viewIndex) {
|
||||||
|
// The viewIndex is within our onscreen bounds (or == stop). We want to draw all the
|
||||||
|
// commands from the current position to the current view, inclusive.
|
||||||
while (commandIndex < viewIndex) {
|
while (commandIndex < viewIndex) {
|
||||||
mDrawCommands[commandIndex++].draw(mFlatViewGroup, canvas);
|
mDrawCommands[commandIndex++].draw(mFlatViewGroup, canvas);
|
||||||
}
|
}
|
||||||
|
@ -406,7 +404,8 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||||
mDrawCommands[viewIndex].draw(mFlatViewGroup, canvas);
|
mDrawCommands[viewIndex].draw(mFlatViewGroup, canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have drawn all the views, now just draw the remaining draw commands.
|
// If we get here, it means we have drawn all the views, now just draw the remaining draw
|
||||||
|
// commands.
|
||||||
while (commandIndex < mStop) {
|
while (commandIndex < mStop) {
|
||||||
mDrawCommands[commandIndex++].draw(mFlatViewGroup, canvas);
|
mDrawCommands[commandIndex++].draw(mFlatViewGroup, canvas);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,235 +0,0 @@
|
||||||
/**
|
|
||||||
* 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 java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.animation.Animation;
|
|
||||||
|
|
||||||
import com.facebook.infer.annotation.Assertions;
|
|
||||||
import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract {@link DrawCommandManager} with directional clipping.
|
|
||||||
*/
|
|
||||||
/* package */ abstract class DirectionalClippingDrawCommandManager {
|
|
||||||
// This will be fixed in the next diff!!!
|
|
||||||
private final FlatViewGroup mFlatViewGroup;
|
|
||||||
DrawCommand[] mDrawCommands = DrawCommand.EMPTY_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 final Map<Integer, View> mClippedSubviews = new HashMap<>();
|
|
||||||
|
|
||||||
protected final Rect mClippingRect = new Rect();
|
|
||||||
|
|
||||||
abstract boolean beforeRect(DrawView drawView);
|
|
||||||
|
|
||||||
abstract boolean afterRect(DrawView drawView);
|
|
||||||
|
|
||||||
/* package */ DirectionalClippingDrawCommandManager(
|
|
||||||
FlatViewGroup flatViewGroup,
|
|
||||||
DrawCommand[] drawCommands) {
|
|
||||||
mFlatViewGroup = flatViewGroup;
|
|
||||||
initialSetup(drawCommands);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initialSetup(DrawCommand[] drawCommands) {
|
|
||||||
mountDrawCommands(drawCommands);
|
|
||||||
updateClippingRect();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mountDrawCommands(DrawCommand[] drawCommands) {
|
|
||||||
mDrawCommands = drawCommands;
|
|
||||||
mDrawViewMap.clear();
|
|
||||||
for (DrawCommand drawCommand : mDrawCommands) {
|
|
||||||
if (drawCommand instanceof DrawView) {
|
|
||||||
DrawView drawView = (DrawView) drawCommand;
|
|
||||||
mDrawViewMap.put(drawView.reactTag, drawView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clip(int id, View view) {
|
|
||||||
mClippedSubviews.put(id, view);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void unclip(int id) {
|
|
||||||
mClippedSubviews.remove(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isClipped(int id) {
|
|
||||||
return mClippedSubviews.containsKey(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isNotClipped(int id) {
|
|
||||||
return !mClippedSubviews.containsKey(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
DrawView drawView = Assertions.assertNotNull(mDrawViewMap.get(viewToAdd));
|
|
||||||
drawView.mWasMounted = true;
|
|
||||||
if (animating(view) || withinBounds(drawView)) {
|
|
||||||
// View should be drawn. This view can't currently be clipped because it wasn't
|
|
||||||
// previously attached to this parent.
|
|
||||||
mFlatViewGroup.addViewInLayout(view);
|
|
||||||
} else {
|
|
||||||
clip(drawView.reactTag, view);
|
|
||||||
}
|
|
||||||
} 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)) {
|
|
||||||
// The DrawView is not clipped. Attach it.
|
|
||||||
mFlatViewGroup.attachViewToParent(view);
|
|
||||||
}
|
|
||||||
// else The DrawView has been previously mounted and is clipped, so don't attach it.
|
|
||||||
} else {
|
|
||||||
// We are mounting it, so lets get this part out of the way.
|
|
||||||
drawView.mWasMounted = true;
|
|
||||||
// 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)) {
|
|
||||||
// View should be drawn.
|
|
||||||
if (isClipped(drawView.reactTag)) {
|
|
||||||
// View was clipped, so add it.
|
|
||||||
mFlatViewGroup.addViewInLayout(view);
|
|
||||||
unclip(drawView.reactTag);
|
|
||||||
} else {
|
|
||||||
// View was just temporarily removed, so attach it. We already know it isn't clipped,
|
|
||||||
// so no need to unclip it.
|
|
||||||
mFlatViewGroup.attachViewToParent(view);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// View should be clipped.
|
|
||||||
if (isNotClipped(drawView.reactTag)) {
|
|
||||||
// View was onscreen.
|
|
||||||
mFlatViewGroup.removeDetachedView(view);
|
|
||||||
clip(drawView.reactTag, view);
|
|
||||||
}
|
|
||||||
// else view is already clipped and not within bounds.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int viewToDetach : viewsToDetach) {
|
|
||||||
View view = viewResolver.getView(viewToDetach);
|
|
||||||
if (view.getParent() != null) {
|
|
||||||
throw new RuntimeException("Trying to remove view not owned by FlatViewGroup");
|
|
||||||
} else {
|
|
||||||
mFlatViewGroup.removeDetachedView(view);
|
|
||||||
}
|
|
||||||
// The view isn't clipped anymore, but gone entirely.
|
|
||||||
unclip(viewToDetach);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if a view is currently animating.
|
|
||||||
static boolean animating(View view) {
|
|
||||||
Animation animation = view.getAnimation();
|
|
||||||
return animation != null && !animation.hasEnded();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true if a DrawView is currently onscreen.
|
|
||||||
boolean withinBounds(DrawView drawView) {
|
|
||||||
return !(beforeRect(drawView) || afterRect(drawView));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean updateClippingRect() {
|
|
||||||
ReactClippingViewGroupHelper.calculateClippingRect(mFlatViewGroup, mClippingRect);
|
|
||||||
if (mFlatViewGroup.getParent() == null || mClippingRect.top == mClippingRect.bottom) {
|
|
||||||
// If we are unparented or are clipping to an empty rect, no op. Return false so we don't
|
|
||||||
// invalidate.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return needsInvalidate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void getClippingRect(Rect outClippingRect) {
|
|
||||||
outClippingRect.set(mClippingRect);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<View> getDetachedViews() {
|
|
||||||
return mClippedSubviews.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void draw(Canvas canvas) {
|
|
||||||
for (DrawCommand drawCommand : mDrawCommands) {
|
|
||||||
if (drawCommand instanceof DrawView) {
|
|
||||||
if (isNotClipped(((DrawView) drawCommand).reactTag)) {
|
|
||||||
drawCommand.draw(mFlatViewGroup, canvas);
|
|
||||||
}
|
|
||||||
// else, don't draw, and don't increment index
|
|
||||||
} else {
|
|
||||||
drawCommand.draw(mFlatViewGroup, canvas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugDraw(Canvas canvas) {
|
|
||||||
for (DrawCommand drawCommand : mDrawCommands) {
|
|
||||||
if (drawCommand instanceof DrawView) {
|
|
||||||
if (isNotClipped(((DrawView) drawCommand).reactTag)) {
|
|
||||||
drawCommand.debugDraw(mFlatViewGroup, canvas);
|
|
||||||
}
|
|
||||||
// else, don't draw, and don't increment index
|
|
||||||
} else {
|
|
||||||
drawCommand.debugDraw(mFlatViewGroup, canvas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -136,9 +136,9 @@ import android.view.ViewParent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static DrawCommandManager getClippingInstance(
|
static DrawCommandManager getVerticalClippingInstance(
|
||||||
FlatViewGroup flatViewGroup,
|
FlatViewGroup flatViewGroup,
|
||||||
DrawCommand[] drawCommands) {
|
DrawCommand[] drawCommands) {
|
||||||
return new ClippingDrawCommandManager(flatViewGroup, drawCommands);
|
return new VerticalDrawCommandManager(flatViewGroup, drawCommands);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,14 +37,15 @@ import android.graphics.RectF;
|
||||||
// the path to clip against if we're doing path clipping for rounded borders.
|
// the path to clip against if we're doing path clipping for rounded borders.
|
||||||
@Nullable private Path mPath;
|
@Nullable private Path mPath;
|
||||||
|
|
||||||
// These should only ever be set from within the DrawView, their only purpose is to prevent
|
// These should only ever be set from within the DrawView, they serve to provide clipping bounds
|
||||||
// excessive rounding on the UI thread in FlatViewGroup, and they are left package protected to
|
// for FlatViewGroups, which have strange clipping when it comes to overflow: visible. They are
|
||||||
// speed up direct access. For overflow visible, these are the adjusted bounds while taking
|
// left package protected to speed up direct access. For overflow visible, these are the adjusted
|
||||||
// overflowing elements into account.
|
// bounds while taking overflowing elements into account, other wise they are just the regular
|
||||||
/* package */ int mLogicalLeft;
|
// bounds of the view.
|
||||||
/* package */ int mLogicalTop;
|
/* package */ float mLogicalLeft;
|
||||||
/* package */ int mLogicalRight;
|
/* package */ float mLogicalTop;
|
||||||
/* package */ int mLogicalBottom;
|
/* package */ float mLogicalRight;
|
||||||
|
/* package */ float mLogicalBottom;
|
||||||
|
|
||||||
public DrawView(int reactTag) {
|
public DrawView(int reactTag) {
|
||||||
this.reactTag = reactTag;
|
this.reactTag = reactTag;
|
||||||
|
@ -62,10 +63,10 @@ import android.graphics.RectF;
|
||||||
float top,
|
float top,
|
||||||
float right,
|
float right,
|
||||||
float bottom,
|
float bottom,
|
||||||
int logicalLeft,
|
float logicalLeft,
|
||||||
int logicalTop,
|
float logicalTop,
|
||||||
int logicalRight,
|
float logicalRight,
|
||||||
int logicalBottom,
|
float logicalBottom,
|
||||||
float clipLeft,
|
float clipLeft,
|
||||||
float clipTop,
|
float clipTop,
|
||||||
float clipRight,
|
float clipRight,
|
||||||
|
@ -116,12 +117,12 @@ import android.graphics.RectF;
|
||||||
return drawView;
|
return drawView;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean logicalBoundsMatch(int left, int top, int right, int bottom) {
|
private boolean logicalBoundsMatch(float left, float top, float right, float bottom) {
|
||||||
return left == mLogicalLeft && top == mLogicalTop &&
|
return left == mLogicalLeft && top == mLogicalTop &&
|
||||||
right == mLogicalRight && bottom == mLogicalBottom;
|
right == mLogicalRight && bottom == mLogicalBottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setLogicalBounds(int left, int top, int right, int bottom) {
|
private void setLogicalBounds(float left, float top, float right, float bottom) {
|
||||||
// Do rounding up front and off of the UI thread.
|
// Do rounding up front and off of the UI thread.
|
||||||
mLogicalLeft = left;
|
mLogicalLeft = left;
|
||||||
mLogicalTop = top;
|
mLogicalTop = top;
|
||||||
|
|
|
@ -506,10 +506,10 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||||
top,
|
top,
|
||||||
right,
|
right,
|
||||||
bottom,
|
bottom,
|
||||||
Math.round(left + mLogicalOffset.left),
|
left + mLogicalOffset.left,
|
||||||
Math.round(top + mLogicalOffset.top),
|
top + mLogicalOffset.top,
|
||||||
Math.round(right + mLogicalOffset.right),
|
right + mLogicalOffset.right,
|
||||||
Math.round(bottom + mLogicalOffset.bottom),
|
bottom + mLogicalOffset.bottom,
|
||||||
clipLeft,
|
clipLeft,
|
||||||
clipTop,
|
clipTop,
|
||||||
clipRight,
|
clipRight,
|
||||||
|
@ -547,4 +547,8 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||||
public boolean clipsSubviews() {
|
public boolean clipsSubviews() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isHorizontal() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -876,10 +876,6 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ void detachView(int index) {
|
|
||||||
detachViewFromParent(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getClippingRect(Rect outClippingRect) {
|
public void getClippingRect(Rect outClippingRect) {
|
||||||
if (mDrawCommandManager == null) {
|
if (mDrawCommandManager == null) {
|
||||||
|
@ -905,7 +901,7 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"Trying to transition FlatViewGroup from clipping to non-clipping state");
|
"Trying to transition FlatViewGroup from clipping to non-clipping state");
|
||||||
}
|
}
|
||||||
mDrawCommandManager = DrawCommandManager.getClippingInstance(this, mDrawCommands);
|
mDrawCommandManager = DrawCommandManager.getVerticalClippingInstance(this, mDrawCommands);
|
||||||
mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||||
// We don't need an invalidate here because this can't cause new views to come onscreen, since
|
// We don't need an invalidate here because this can't cause new views to come onscreen, since
|
||||||
// everything was unclipped.
|
// everything was unclipped.
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link DrawCommandManager} with horizontal clipping (The view scrolls left and right).
|
|
||||||
*/
|
|
||||||
/* package */ final class HorizontalClippingDrawCommandManager extends
|
|
||||||
DirectionalClippingDrawCommandManager {
|
|
||||||
|
|
||||||
/* package */ HorizontalClippingDrawCommandManager(
|
|
||||||
FlatViewGroup flatViewGroup,
|
|
||||||
DrawCommand[] drawCommands) {
|
|
||||||
super(flatViewGroup, drawCommands);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean beforeRect(DrawView drawView) {
|
|
||||||
return drawView.mLogicalRight <= mClippingRect.left;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean afterRect(DrawView drawView) {
|
|
||||||
return drawView.mLogicalLeft >= mClippingRect.right;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
/**
|
||||||
|
* 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 java.util.Arrays;
|
||||||
|
|
||||||
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link DrawCommandManager} with horizontal clipping (The view scrolls left and right).
|
||||||
|
*/
|
||||||
|
/* package */ final class HorizontalDrawCommandManager extends ClippingDrawCommandManager {
|
||||||
|
|
||||||
|
/* package */ HorizontalDrawCommandManager(
|
||||||
|
FlatViewGroup flatViewGroup,
|
||||||
|
DrawCommand[] drawCommands) {
|
||||||
|
super(flatViewGroup, drawCommands);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int commandStartIndex() {
|
||||||
|
int start = Arrays.binarySearch(mCommandMaxBottom, mClippingRect.left);
|
||||||
|
// We don't care whether we matched or not, but positive indices are helpful. The binary search
|
||||||
|
// returns ~index in the case that it isn't a match, so reverse that here.
|
||||||
|
return start < 0 ? ~start : start;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int commandStopIndex(int start) {
|
||||||
|
int stop = Arrays.binarySearch(
|
||||||
|
mCommandMinTop,
|
||||||
|
start,
|
||||||
|
mCommandMinTop.length,
|
||||||
|
mClippingRect.right);
|
||||||
|
// We don't care whether we matched or not, but positive indices are helpful. The binary search
|
||||||
|
// returns ~index in the case that it isn't a match, so reverse that here.
|
||||||
|
return stop < 0 ? ~stop : stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int regionStopIndex(float touchX, float touchY) {
|
||||||
|
int stop = Arrays.binarySearch(mRegionMinTop, touchX + 0.0001f);
|
||||||
|
// We don't care whether we matched or not, but positive indices are helpful. The binary search
|
||||||
|
// returns ~index in the case that it isn't a match, so reverse that here.
|
||||||
|
return stop < 0 ? ~stop : stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean regionAboveTouch(int index, float touchX, float touchY) {
|
||||||
|
return mRegionMaxBottom[index] < touchX;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These should never be called from the UI thread, as the reason they exist is to do work off the
|
||||||
|
// UI thread.
|
||||||
|
public static void fillMaxMinArrays(NodeRegion[] regions, float[] maxBottom, float[] minTop) {
|
||||||
|
float last = 0;
|
||||||
|
for (int i = 0; i < regions.length; i++) {
|
||||||
|
last = Math.max(last, regions[i].mRight);
|
||||||
|
maxBottom[i] = last;
|
||||||
|
}
|
||||||
|
for (int i = regions.length - 1; i >= 0; i--) {
|
||||||
|
last = Math.min(last, regions[i].mLeft);
|
||||||
|
minTop[i] = last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fillMaxMinArrays(
|
||||||
|
DrawCommand[] commands,
|
||||||
|
float[] maxBottom,
|
||||||
|
float[] minTop,
|
||||||
|
SparseIntArray drawViewIndexMap) {
|
||||||
|
float last = 0;
|
||||||
|
// Loop through the DrawCommands, keeping track of the maximum we've seen if we only iterated
|
||||||
|
// through items up to this position.
|
||||||
|
for (int i = 0; i < commands.length; i++) {
|
||||||
|
if (commands[i] instanceof DrawView) {
|
||||||
|
DrawView drawView = (DrawView) commands[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.mLogicalRight);
|
||||||
|
} else {
|
||||||
|
last = Math.max(last, commands[i].getRight());
|
||||||
|
}
|
||||||
|
maxBottom[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 backwards, keeping track of the minimum we've seen at this position.
|
||||||
|
for (int i = commands.length - 1; i >= 0; i--) {
|
||||||
|
if (commands[i] instanceof DrawView) {
|
||||||
|
last = Math.min(last, ((DrawView) commands[i]).mLogicalLeft);
|
||||||
|
} else {
|
||||||
|
last = Math.min(last, commands[i].getLeft());
|
||||||
|
}
|
||||||
|
minTop[i] = last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -351,32 +351,12 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
||||||
commandMaxBottom = new float[drawCommands.length];
|
commandMaxBottom = new float[drawCommands.length];
|
||||||
commandMinTop = new float[drawCommands.length];
|
commandMinTop = new float[drawCommands.length];
|
||||||
|
|
||||||
float last = 0;
|
if (node.isHorizontal()) {
|
||||||
// Loop through the DrawCommands, keeping track of the maximum y we've seen if we only
|
HorizontalDrawCommandManager
|
||||||
// iterated through items up to this position
|
.fillMaxMinArrays(drawCommands, commandMaxBottom, commandMinTop, drawViewIndexMap);
|
||||||
for (int i = 0; i < drawCommands.length; i++) {
|
} else {
|
||||||
if (drawCommands[i] instanceof DrawView) {
|
VerticalDrawCommandManager
|
||||||
DrawView drawView = (DrawView) drawCommands[i];
|
.fillMaxMinArrays(drawCommands, commandMaxBottom, commandMinTop, drawViewIndexMap);
|
||||||
// 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[] regionMaxBottom = EMPTY_FLOAT_ARRAY;
|
||||||
|
@ -385,14 +365,12 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
||||||
regionMaxBottom = new float[nodeRegions.length];
|
regionMaxBottom = new float[nodeRegions.length];
|
||||||
regionMinTop = new float[nodeRegions.length];
|
regionMinTop = new float[nodeRegions.length];
|
||||||
|
|
||||||
float last = 0;
|
if (node.isHorizontal()) {
|
||||||
for (int i = 0; i < nodeRegions.length; i++) {
|
HorizontalDrawCommandManager
|
||||||
last = Math.max(last, nodeRegions[i].mBottom);
|
.fillMaxMinArrays(nodeRegions, regionMaxBottom, regionMinTop);
|
||||||
regionMaxBottom[i] = last;
|
} else {
|
||||||
}
|
VerticalDrawCommandManager
|
||||||
for (int i = nodeRegions.length - 1; i >= 0; i--) {
|
.fillMaxMinArrays(nodeRegions, regionMaxBottom, regionMinTop);
|
||||||
last = Math.min(last, nodeRegions[i].mTop);
|
|
||||||
regionMinTop[i] = last;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link DrawCommandManager} with vertical clipping (The view scrolls up and down).
|
|
||||||
*/
|
|
||||||
/* package */ final class VerticalClippingDrawCommandManager extends
|
|
||||||
DirectionalClippingDrawCommandManager {
|
|
||||||
|
|
||||||
/* package */ VerticalClippingDrawCommandManager(
|
|
||||||
FlatViewGroup flatViewGroup,
|
|
||||||
DrawCommand[] drawCommands) {
|
|
||||||
super(flatViewGroup, drawCommands);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean beforeRect(DrawView drawView) {
|
|
||||||
return drawView.mLogicalBottom <= mClippingRect.top;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean afterRect(DrawView drawView) {
|
|
||||||
return drawView.mLogicalTop >= mClippingRect.bottom;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/**
|
||||||
|
* 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 java.util.Arrays;
|
||||||
|
|
||||||
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link DrawCommandManager} with vertical clipping (The view scrolls up and down).
|
||||||
|
*/
|
||||||
|
/* package */ final class VerticalDrawCommandManager extends ClippingDrawCommandManager {
|
||||||
|
|
||||||
|
/* package */ VerticalDrawCommandManager(
|
||||||
|
FlatViewGroup flatViewGroup,
|
||||||
|
DrawCommand[] drawCommands) {
|
||||||
|
super(flatViewGroup, drawCommands);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int commandStartIndex() {
|
||||||
|
int start = Arrays.binarySearch(mCommandMaxBottom, mClippingRect.top);
|
||||||
|
// We don't care whether we matched or not, but positive indices are helpful. The binary search
|
||||||
|
// returns ~index in the case that it isn't a match, so reverse that here.
|
||||||
|
return start < 0 ? ~start : start;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int commandStopIndex(int start) {
|
||||||
|
int stop = Arrays.binarySearch(
|
||||||
|
mCommandMinTop,
|
||||||
|
start,
|
||||||
|
mCommandMinTop.length,
|
||||||
|
mClippingRect.bottom);
|
||||||
|
// We don't care whether we matched or not, but positive indices are helpful. The binary search
|
||||||
|
// returns ~index in the case that it isn't a match, so reverse that here.
|
||||||
|
return stop < 0 ? ~stop : stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int regionStopIndex(float touchX, float touchY) {
|
||||||
|
int stop = Arrays.binarySearch(mRegionMinTop, touchY + 0.0001f);
|
||||||
|
// We don't care whether we matched or not, but positive indices are helpful. The binary search
|
||||||
|
// returns ~index in the case that it isn't a match, so reverse that here.
|
||||||
|
return stop < 0 ? ~stop : stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean regionAboveTouch(int index, float touchX, float touchY) {
|
||||||
|
return mRegionMaxBottom[index] < touchY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These should never be called from the UI thread, as the reason they exist is to do work off
|
||||||
|
// the UI thread.
|
||||||
|
public static void fillMaxMinArrays(NodeRegion[] regions, float[] maxBot, float[] minTop) {
|
||||||
|
float last = 0;
|
||||||
|
for (int i = 0; i < regions.length; i++) {
|
||||||
|
last = Math.max(last, regions[i].mBottom);
|
||||||
|
maxBot[i] = last;
|
||||||
|
}
|
||||||
|
for (int i = regions.length - 1; i >= 0; i--) {
|
||||||
|
last = Math.min(last, regions[i].mTop);
|
||||||
|
minTop[i] = last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These should never be called from the UI thread, as the reason they exist is to do work off
|
||||||
|
// the UI thread.
|
||||||
|
public static void fillMaxMinArrays(
|
||||||
|
DrawCommand[] commands,
|
||||||
|
float[] maxBot,
|
||||||
|
float[] minTop,
|
||||||
|
SparseIntArray drawViewIndexMap) {
|
||||||
|
float last = 0;
|
||||||
|
// Loop through the DrawCommands, keeping track of the maximum we've seen if we only iterated
|
||||||
|
// through items up to this position.
|
||||||
|
for (int i = 0; i < commands.length; i++) {
|
||||||
|
if (commands[i] instanceof DrawView) {
|
||||||
|
DrawView drawView = (DrawView) commands[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, commands[i].getBottom());
|
||||||
|
}
|
||||||
|
maxBot[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 backwards, keeping track of the minimum we've seen at this position.
|
||||||
|
for (int i = commands.length - 1; i >= 0; i--) {
|
||||||
|
if (commands[i] instanceof DrawView) {
|
||||||
|
last = Math.min(last, ((DrawView) commands[i]).mLogicalTop);
|
||||||
|
} else {
|
||||||
|
last = Math.min(last, commands[i].getTop());
|
||||||
|
}
|
||||||
|
minTop[i] = last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue