Add comments to FlatViewGroup, DrawCommandManage, ElementsList and StateBuilder.
Summary: Documentate all of the things. Reviewed By: ahmedre Differential Revision: D3700420
This commit is contained in:
parent
3adbf1e822
commit
8600723402
|
@ -27,8 +27,123 @@ import com.facebook.infer.annotation.Assertions;
|
|||
import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Abstract class for a {@link DrawCommandManager} with directional clipping. Allows support for
|
||||
* vertical and horizontal clipping by implementing abstract methods.
|
||||
*
|
||||
* Uses two dynamic programming arrays to efficiently update which views and commands are onscreen,
|
||||
* while not having to sort the incoming draw commands. The draw commands are loosely sorted, as
|
||||
* they represent a flattening of the normal view hierarchy, and we use that information to quickly
|
||||
* find children that should be considered onscreen. One array keeps track of, for each index, the
|
||||
* maximum bottom position that occurs at or before that index; the other keeps track of the
|
||||
* minimum top position that occurs at or after that index. Given the following children:
|
||||
*
|
||||
* +---------------------------------+ 0 (Y coordinate)
|
||||
* | 0 |
|
||||
* | +-----------+ | 10
|
||||
* | | 1 | |
|
||||
* | | | +--------------+ | 20
|
||||
* | | | | 3 | |
|
||||
* | +-----------+ | | | 30
|
||||
* | | | |
|
||||
* | +-----------+ | | | 40
|
||||
* | | 2 | | | |
|
||||
* | | | +--------------+ | 50
|
||||
* | | | |
|
||||
* | +-----------+ | 60
|
||||
* | |
|
||||
* +---------------------------------+ 70
|
||||
*
|
||||
* +-----------+ 80
|
||||
* | 4 |
|
||||
* | | +--------------+ 90
|
||||
* | | | 6 |
|
||||
* +-----------+ | | 100
|
||||
* | |
|
||||
* +-----------+ | | 110
|
||||
* | 5 | | |
|
||||
* | | +--------------+ 120
|
||||
* | |
|
||||
* +-----------+ 130
|
||||
*
|
||||
* The two arrays are:
|
||||
* 0 1 2 3 4 5 6
|
||||
* Max Bottom: [70, 70, 70, 70, 100, 130, 130]
|
||||
* Min Top: [ 0, 0, 0, 0, 80, 90, 90]
|
||||
*
|
||||
* We can then binary search for the first max bottom that is below our rect, and the first min top
|
||||
* that is above our rect.
|
||||
*
|
||||
* If the top and bottom of the rect are 55 and 85, respectively, we will start drawing at index 0
|
||||
* and stop at index 4.
|
||||
*
|
||||
* +---------------------------------+ 0 (Y coordinate)
|
||||
* | 0 |
|
||||
* | +-----------+ | 10
|
||||
* | | 1 | |
|
||||
* | | | +--------------+ | 20
|
||||
* | | | | 3 | |
|
||||
* | +-----------+ | | | 30
|
||||
* | | | |
|
||||
* | +-----------+ | | | 40
|
||||
* | | 2 | | | |
|
||||
* | | | +--------------+ | 50
|
||||
* - -| -| - - - -| - - - - - - |- - -
|
||||
* | +-----------+ | 60
|
||||
* | |
|
||||
* +---------------------------------+ 70
|
||||
*
|
||||
* +-----------+ 80
|
||||
* - - -| 4 - - - | - - - - - - - - -
|
||||
* | | +--------------+ 90
|
||||
* | | | 6 |
|
||||
* +-----------+ | | 100
|
||||
* | |
|
||||
* +-----------+ | | 110
|
||||
* | 5 | | |
|
||||
* | | +--------------+ 120
|
||||
* | |
|
||||
* +-----------+ 130
|
||||
*
|
||||
* If the top and bottom are 75 and 105 respectively, we will start drawing at index 4 and stop at
|
||||
* index 6.
|
||||
*
|
||||
* +---------------------------------+ 0 (Y coordinate)
|
||||
* | 0 |
|
||||
* | +-----------+ | 10
|
||||
* | | 1 | |
|
||||
* | | | +--------------+ | 20
|
||||
* | | | | 3 | |
|
||||
* | +-----------+ | | | 30
|
||||
* | | | |
|
||||
* | +-----------+ | | | 40
|
||||
* | | 2 | | | |
|
||||
* | | | +--------------+ | 50
|
||||
* | | | |
|
||||
* | +-----------+ | 60
|
||||
* | |
|
||||
* +---------------------------------+ 70
|
||||
* - - - - - - - - - - - - - - - -
|
||||
* +-----------+ 80
|
||||
* | 4 |
|
||||
* | | +--------------+ 90
|
||||
* | | | 6 |
|
||||
* +-----------+ | | 100
|
||||
* - - - - - - - |- - - - - | - - -
|
||||
* +-----------+ | | 110
|
||||
* | 5 | | |
|
||||
* | | +--------------+ 120
|
||||
* | |
|
||||
* +-----------+ 130
|
||||
*
|
||||
* While this doesn't map exactly to all of the commands that could be clipped, it means that
|
||||
* children which contain other children (a pretty common case when flattening views) are clipped
|
||||
* or unclipped as one logical unit. This has the side effect of minimizing the amount of
|
||||
* invalidates coming from minor clipping rect adjustments. The underlying dynamic programming
|
||||
* arrays can be calculated off the UI thread in O(n) time, requiring just two passes through the
|
||||
* command array.
|
||||
*
|
||||
* We do a similar optimization when searching for matching node regions, as node regions are
|
||||
* loosely sorted as well when clipping.
|
||||
*/
|
||||
/* package */ abstract class ClippingDrawCommandManager extends DrawCommandManager {
|
||||
private final FlatViewGroup mFlatViewGroup;
|
||||
|
@ -63,6 +178,13 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
initialSetup(drawCommands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initially setup this instance. Makes sure the draw commands are mounted, and that our
|
||||
* clipping rect reflects our current bounds.
|
||||
*
|
||||
* @param drawCommands The list of current draw commands. In current implementations, this will
|
||||
* always be DrawCommand.EMPTY_ARRAY
|
||||
*/
|
||||
private void initialSetup(DrawCommand[] drawCommands) {
|
||||
mountDrawCommands(
|
||||
drawCommands,
|
||||
|
@ -255,14 +377,18 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
}
|
||||
}
|
||||
|
||||
// Returns true if a view is currently animating.
|
||||
/**
|
||||
* Returns true if a view is currently animating.
|
||||
*/
|
||||
private static boolean animating(View view) {
|
||||
Animation animation = view.getAnimation();
|
||||
return animation != null && !animation.hasEnded();
|
||||
}
|
||||
|
||||
// Return true if a command index is currently onscreen.
|
||||
boolean withinBounds(int i) {
|
||||
/**
|
||||
* Returns true if a command index is currently onscreen.
|
||||
*/
|
||||
private boolean withinBounds(int i) {
|
||||
return mStart <= i && i < mStop;
|
||||
}
|
||||
|
||||
|
@ -291,6 +417,22 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used either after we have updated the current rect, or when we have mounted new commands and
|
||||
* the rect hasn't changed. Updates the clipping after mStart and mStop have been set to the
|
||||
* correct values. For draw commands, this is all it takes to update the command mounting, as
|
||||
* draw commands are only attached in a conceptual sense, and rely on the android view
|
||||
* hierarchy.
|
||||
*
|
||||
* For native children, we have to walk through our current views and remove any that are no
|
||||
* longer on screen, and add those that are newly on screen. As an optimization for fling, if we
|
||||
* are removing two or more native views we instead detachAllViews from the {@link FlatViewGroup}
|
||||
* and re-attach or add as needed.
|
||||
*
|
||||
* This approximation is roughly correct, as we tend to add and remove the same amount of views,
|
||||
* and each add and remove pair is O(n); detachAllViews and re-attach requires two passes, so
|
||||
* using this once we are removing more than two native views is a good breakpoint.
|
||||
*/
|
||||
private void updateClippingToCurrentRect() {
|
||||
for (int i = 0, size = mFlatViewGroup.getChildCount(); i < size; i++) {
|
||||
View view = mFlatViewGroup.getChildAt(i);
|
||||
|
@ -372,6 +514,47 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper;
|
|||
return mClippedSubviews.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the unclipped commands on the given canvas. This would be much simpler if we didn't
|
||||
* have to worry about animating views, as we could simply:
|
||||
*
|
||||
* for (int i = start; i < stop; i++) {
|
||||
* drawCommands[i].draw(...);
|
||||
* }
|
||||
*
|
||||
* This is complicated however by animating views, which may occur before or after the current
|
||||
* clipping rect. Consider the following array:
|
||||
*
|
||||
* +--------------+
|
||||
* | DrawView | 0
|
||||
* | *animating* |
|
||||
* +--------------+
|
||||
* | DrawCommmand | 1
|
||||
* | *clipped* |
|
||||
* +--------------+
|
||||
* | DrawCommand | 2 start
|
||||
* | |
|
||||
* +--------------+
|
||||
* | DrawCommand | 3
|
||||
* | |
|
||||
* +--------------+
|
||||
* | DrawView | 4
|
||||
* | |
|
||||
* +--------------+
|
||||
* | DrawView | 5 stop
|
||||
* | *clipped* |
|
||||
* +--------------+
|
||||
* | DrawView | 6
|
||||
* | *animating* |
|
||||
* +--------------+
|
||||
*
|
||||
* 2, 3, and 4 are onscreen according to bounds, while 0 and 6 are onscreen according to
|
||||
* animation. We have to walk through the attached children making sure to draw any draw
|
||||
* commands that should be drawn before that draw view, as well as making sure not to draw any
|
||||
* draw commands that are out of bounds.
|
||||
*
|
||||
* @param canvas The canvas to draw on.
|
||||
*/
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
int commandIndex = mStart;
|
||||
|
|
|
@ -20,7 +20,8 @@ import android.view.View;
|
|||
import android.view.ViewParent;
|
||||
|
||||
/**
|
||||
* Underlying logic behind handling clipping draw commands from {@link FlatViewGroup}.
|
||||
* Underlying logic which handles draw commands, views and node regions when clipping in a
|
||||
* {@link FlatViewGroup}.
|
||||
*/
|
||||
/* package */ abstract class DrawCommandManager {
|
||||
|
||||
|
@ -136,6 +137,13 @@ import android.view.ViewParent;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a draw command manager that will clip vertically (The view scrolls up and down).
|
||||
*
|
||||
* @param flatViewGroup FlatViewGroup to use for drawing.
|
||||
* @param drawCommands List of commands to mount.
|
||||
* @return Vertically clipping draw command manager.
|
||||
*/
|
||||
static DrawCommandManager getVerticalClippingInstance(
|
||||
FlatViewGroup flatViewGroup,
|
||||
DrawCommand[] drawCommands) {
|
||||
|
|
|
@ -39,7 +39,7 @@ import com.facebook.react.views.imagehelper.MultiSourceHelper;
|
|||
import com.facebook.react.views.imagehelper.MultiSourceHelper.MultiSourceResult;
|
||||
|
||||
/**
|
||||
* DrawImageWithDrawee is DrawCommand that can draw a local or remote image.
|
||||
* DrawImageWithDrawee is a DrawCommand that can draw a local or remote image.
|
||||
* It uses DraweeRequestHelper internally to fetch and cache the images.
|
||||
*/
|
||||
/* package */ final class DrawImageWithDrawee extends AbstractDrawCommand
|
||||
|
|
|
@ -14,34 +14,49 @@ import java.util.ArrayList;
|
|||
import java.lang.reflect.Array;
|
||||
|
||||
/**
|
||||
* Helper class that supports 3 main operations: start(), add() an element and finish().
|
||||
* Diffing scope stack class that supports 3 main operations: start(), add() an element and
|
||||
* finish().
|
||||
*
|
||||
* When started, it takes a baseline array to compare to. When adding a new element, it checks
|
||||
* whether a corresponding element in baseline array is the same. On finish(), it will return null
|
||||
* if baseline array contains exactly the same elements that were added with a sequence of add()
|
||||
* calls, or a new array the recorded elements:
|
||||
* calls, or a new array of the recorded elements:
|
||||
*
|
||||
* Example 1:
|
||||
* -----
|
||||
* start([A])
|
||||
* add(A)
|
||||
* finish() -> null (because [A] == [A])
|
||||
* start([A])
|
||||
* add(A)
|
||||
* finish() -> null (because [A] == [A])
|
||||
*
|
||||
* Example 2:
|
||||
* ----
|
||||
* start([A])
|
||||
* add(B)
|
||||
* finish() -> [B] (because [A] != [B])
|
||||
* start([A])
|
||||
* add(B)
|
||||
* finish() -> [B] (because [A] != [B])
|
||||
*
|
||||
* Example 3:
|
||||
* ----
|
||||
* start([A])
|
||||
* add(B)
|
||||
* add(A)
|
||||
* finish() -> [B, A] (because [B, A] != [A])
|
||||
*
|
||||
* Example 4:
|
||||
* ----
|
||||
* start([A, B])
|
||||
* add(B)
|
||||
* add(A)
|
||||
* finish() -> [B, A] (because [B, A] != [A, B])
|
||||
*
|
||||
* It is important that start/finish can be nested:
|
||||
* ----
|
||||
* start([A])
|
||||
* add(A)
|
||||
* start([B])
|
||||
* add(B)
|
||||
* finish() -> null
|
||||
* add(C)
|
||||
* finish() -> [A, C]
|
||||
* start([A])
|
||||
* add(A)
|
||||
* start([B])
|
||||
* add(B)
|
||||
* finish() -> null
|
||||
* add(C)
|
||||
* finish() -> [A, C]
|
||||
*
|
||||
* StateBuilder is using this class to check if e.g. a DrawCommand list for a given View needs to be
|
||||
* updated.
|
||||
|
@ -54,7 +69,12 @@ import java.lang.reflect.Array;
|
|||
int size;
|
||||
}
|
||||
|
||||
// List of scopes. These are never cleared, but instead recycled when a new scope is needed at
|
||||
// a given depth.
|
||||
private final ArrayList<Scope> mScopesStack = new ArrayList<>();
|
||||
// Working list of all new elements we are gathering across scopes. Whenever we get a call to
|
||||
// finish() we pop the new elements off the collection, either discarding them if there was no
|
||||
// change from the base or accumulating and returning them as a list of new elements.
|
||||
private final ArrayDeque<E> mElements = new ArrayDeque<>();
|
||||
private final E[] mEmptyArray;
|
||||
private Scope mCurrentScope = null;
|
||||
|
@ -78,8 +98,8 @@ import java.lang.reflect.Array;
|
|||
}
|
||||
|
||||
/**
|
||||
* Finished current scope, and returns null if there were no changes recorded, or a new array
|
||||
* containing all the recorded elements otherwise.
|
||||
* Finish current scope, returning null if there were no changes recorded, or a new array
|
||||
* containing all the newly recorded elements otherwise.
|
||||
*/
|
||||
public E[] finish() {
|
||||
Scope scope = getCurrentScope();
|
||||
|
@ -96,14 +116,15 @@ import java.lang.reflect.Array;
|
|||
}
|
||||
}
|
||||
|
||||
// to prevent leaks
|
||||
// To prevent resource leaks.
|
||||
scope.elements = null;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new element to the list. This method can be optimized to avoid inserts on same elements.
|
||||
* Adds a new element to the list. This method can be optimized to avoid inserts on same
|
||||
* elements, but would involve copying from scope.elements when we extract elements.
|
||||
*/
|
||||
public void add(E element) {
|
||||
Scope scope = getCurrentScope();
|
||||
|
@ -119,7 +140,7 @@ import java.lang.reflect.Array;
|
|||
}
|
||||
|
||||
/**
|
||||
* Resets all references to the elements to null to avoid memory leaks.
|
||||
* Resets all references to elements in our new stack to null to avoid memory leaks.
|
||||
*/
|
||||
public void clear() {
|
||||
if (getCurrentScope() != null) {
|
||||
|
@ -129,7 +150,8 @@ import java.lang.reflect.Array;
|
|||
}
|
||||
|
||||
/**
|
||||
* Extracts last size elements into an array.
|
||||
* Extracts last size elements into an array. Used to extract our new array of items from our
|
||||
* stack when the new items != old items.
|
||||
*/
|
||||
private E[] extractElements(int size) {
|
||||
if (size == 0) {
|
||||
|
@ -151,15 +173,18 @@ import java.lang.reflect.Array;
|
|||
private void pushScope() {
|
||||
++mScopeIndex;
|
||||
if (mScopeIndex == mScopesStack.size()) {
|
||||
// We reached a new deepest scope, we need to create a scope for this depth.
|
||||
mCurrentScope = new Scope();
|
||||
mScopesStack.add(mCurrentScope);
|
||||
} else {
|
||||
// We have had a scope at this depth before, lets recycle it.
|
||||
mCurrentScope = mScopesStack.get(mScopeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores last save current scope.
|
||||
* Restores last saved current scope. Doesn't actually remove the scope, as scopes are
|
||||
* recycled.
|
||||
*/
|
||||
private void popScope() {
|
||||
--mScopeIndex;
|
||||
|
|
|
@ -42,16 +42,64 @@ import com.facebook.react.views.image.ImageLoadEvent;
|
|||
import com.facebook.react.views.view.ReactClippingViewGroup;
|
||||
|
||||
/**
|
||||
* A view that FlatShadowNode hierarchy maps to. Performs drawing by iterating over
|
||||
* array of DrawCommands, executing them one by one. In the case of clipping, the underlying logic
|
||||
* is handled by {@link DrawCommandManager}. This lets us separate logic, while also allowing us
|
||||
* to save on memory for data structures only used in clipping.
|
||||
* A view that the {@link FlatShadowNode} hierarchy maps to. Can mount and draw native views as
|
||||
* well as draw commands. We reuse some of Android's ViewGroup logic, but in Nodes we try to
|
||||
* minimize the amount of shadow nodes that map to native children, so we have a lot of logic
|
||||
* specific to draw commands.
|
||||
*
|
||||
* In a very simple case with no Android children, the FlatViewGroup will receive:
|
||||
*
|
||||
* flatViewGroup.mountDrawCommands(...);
|
||||
* flatViewGroup.dispatchDraw(...);
|
||||
*
|
||||
* The draw commands are mounted, then draw iterates through and draws them one by one.
|
||||
*
|
||||
* In a simple case where there are native children:
|
||||
*
|
||||
* flatViewGroup.mountDrawCommands(...);
|
||||
* flatViewGroup.detachAllViewsFromParent(...);
|
||||
* flatViewGroup.mountViews(...);
|
||||
* flatViewGroup.dispatchDraw(...);
|
||||
*
|
||||
* Draw commands are mounted, with a draw view command for each mounted view. As an optimization
|
||||
* we then detach all views from the FlatViewGroup, then allow mountViews to selectively reattach
|
||||
* and add views in order. We do this as adding a single view is a O(n) operation (On average you
|
||||
* have to move all the views in the array to the right one position), as is dropping and re-adding
|
||||
* all views (One pass to clear the array and one pass to re-attach detached children and add new
|
||||
* children).
|
||||
*
|
||||
* FlatViewGroups also have arrays of node regions, which are little more than a rects that
|
||||
* represents a touch target. Native views contain their own touch logic, but not all react tags
|
||||
* map to native views. We use node regions to find touch targets among commands as well as nodes
|
||||
* which map to native views.
|
||||
*
|
||||
* In the case of clipping, much of the underlying logic for is handled by
|
||||
* {@link DrawCommandManager}. This lets us separate logic, while also allowing us to save on
|
||||
* memory for data structures only used in clipping. In a case of a clipping FlatViewGroup which
|
||||
* is scrolling:
|
||||
*
|
||||
* flatViewGroup.setRemoveClippedSubviews(true);
|
||||
* flatViewGroup.mountClippingDrawCommands(...);
|
||||
* flatViewGroup.detachAllViewsFromParent(...);
|
||||
* flatViewGroup.mountViews(...);
|
||||
* flatViewGroup.updateClippingRect(...);
|
||||
* flatViewGroup.dispatchDraw(...);
|
||||
* flatViewGroup.updateClippingRect(...);
|
||||
* flatViewGroup.dispatchDraw(...);
|
||||
* flatViewGroup.updateClippingRect(...);
|
||||
* flatViewGroup.dispatchDraw(...);
|
||||
*
|
||||
* Setting remove clipped subviews creates a {@link DrawCommandManager} to handle clipping, which
|
||||
* allows the rest of the methods to simply call in to draw command manager to handle the clipping
|
||||
* logic.
|
||||
*/
|
||||
/* package */ final class FlatViewGroup extends ViewGroup
|
||||
implements ReactInterceptingViewGroup, ReactClippingViewGroup,
|
||||
ReactCompoundViewGroup, ReactHitSlopView, ReactPointerEventsView, FlatMeasuredViewGroup {
|
||||
/**
|
||||
* Helper class that allows AttachDetachListener to invalidate the hosting View.
|
||||
* Helper class that allows our AttachDetachListeners to invalidate the hosting View. When a
|
||||
* listener gets an attach it is passed an invalidate callback for the FlatViewGroup it is being
|
||||
* attached to.
|
||||
*/
|
||||
static final class InvalidateCallback extends WeakReference<FlatViewGroup> {
|
||||
|
||||
|
@ -69,6 +117,12 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Propogates image load events to javascript if the hosting view is still alive.
|
||||
*
|
||||
* @param reactTag The view id.
|
||||
* @param imageLoadEvent The event type.
|
||||
*/
|
||||
public void dispatchImageLoadEvent(int reactTag, int imageLoadEvent) {
|
||||
FlatViewGroup view = get();
|
||||
if (view == null) {
|
||||
|
@ -82,6 +136,7 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
}
|
||||
}
|
||||
|
||||
// Resources for debug drawing.
|
||||
private static final boolean DEBUG_DRAW = false;
|
||||
private static final boolean DEBUG_DRAW_TEXT = false;
|
||||
private boolean mAndroidDebugDraw;
|
||||
|
@ -94,10 +149,14 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
private static final ArrayList<FlatViewGroup> LAYOUT_REQUESTS = new ArrayList<>();
|
||||
private static final Rect VIEW_BOUNDS = new Rect();
|
||||
|
||||
// An invalidate callback singleton for this FlatViewGroup.
|
||||
private @Nullable InvalidateCallback mInvalidateCallback;
|
||||
private DrawCommand[] mDrawCommands = DrawCommand.EMPTY_ARRAY;
|
||||
private AttachDetachListener[] mAttachDetachListeners = AttachDetachListener.EMPTY_ARRAY;
|
||||
private NodeRegion[] mNodeRegions = NodeRegion.EMPTY_ARRAY;
|
||||
|
||||
// The index of the next native child to draw. This is used in dispatchDraw to check that we are
|
||||
// actually drawing all of our attached children, then is reset to 0.
|
||||
private int mDrawChildIndex = 0;
|
||||
private boolean mIsAttached = false;
|
||||
private boolean mIsLayoutRequested = false;
|
||||
|
@ -108,6 +167,7 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
private @Nullable OnInterceptTouchEventListener mOnInterceptTouchEventListener;
|
||||
|
||||
private static final ArrayList<View> EMPTY_DETACHED_VIEWS = new ArrayList<>(0);
|
||||
// Provides clipping, drawing and node region finding logic if subview clipping is enabled.
|
||||
private @Nullable DrawCommandManager mDrawCommandManager;
|
||||
|
||||
private @Nullable Rect mHitSlopRect;
|
||||
|
@ -165,12 +225,22 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
return nodeRegion != null && nodeRegion.mIsVirtual;
|
||||
}
|
||||
|
||||
// This is hidden in the Android ViewGroup, but still gets called in super.dispatchDraw.
|
||||
/**
|
||||
* Secretly Overrides the hidden ViewGroup.onDebugDraw method. This is hidden in the Android
|
||||
* ViewGroup, but still gets called in super.dispatchDraw. Overriding here allows us to draw
|
||||
* layout bounds for Nodes when android is drawing layout bounds.
|
||||
*/
|
||||
protected void onDebugDraw(Canvas canvas) {
|
||||
// Android is drawing layout bounds, so we should as well.
|
||||
mAndroidDebugDraw = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw FlatViewGroup on a canvas. Also checks that all children are drawn, as a draw view calls
|
||||
* back to the FlatViewGroup to draw each child.
|
||||
*
|
||||
* @param canvas The canvas to draw on.
|
||||
*/
|
||||
@Override
|
||||
public void dispatchDraw(Canvas canvas) {
|
||||
mAndroidDebugDraw = false;
|
||||
|
@ -217,6 +287,38 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
mDrawChildIndex = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This override exists to suppress the default drawing behaviour of the ViewGroup. dispatchDraw
|
||||
* calls super.dispatchDraw, which lets Android perform some of our child management logic.
|
||||
* super.dispatchDraw then calls our drawChild, which is suppressed.
|
||||
*
|
||||
* dispatchDraw within the FlatViewGroup then calls super.drawChild, which actually draws the
|
||||
* child.
|
||||
*
|
||||
* // Pseudocode example.
|
||||
* Class FlatViewGroup {
|
||||
* void dispatchDraw() {
|
||||
* super.dispatchDraw(); // Eventually calls our drawChild, which is a no op.
|
||||
* super.drawChild(); // Calls the actual drawChild.
|
||||
* }
|
||||
*
|
||||
* boolean drawChild(...) {
|
||||
* // No op.
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Class ViewGroup {
|
||||
* void dispatchDraw() {
|
||||
* drawChild(); // No op.
|
||||
* }
|
||||
*
|
||||
* boolean drawChild(...) {
|
||||
* getChildAt(...).draw();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @return false, as we are suppressing drawChild.
|
||||
*/
|
||||
@Override
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
// suppress
|
||||
|
@ -224,6 +326,11 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw layout bounds for the next child.
|
||||
*
|
||||
* @param canvas The canvas to draw on.
|
||||
*/
|
||||
/* package */ void debugDrawNextChild(Canvas canvas) {
|
||||
View child = getChildAt(mDrawChildIndex);
|
||||
// Draw FlatViewGroups a different color than regular child views.
|
||||
|
@ -291,6 +398,10 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that we only initialize one instance of each of our layout bounds drawing
|
||||
* resources.
|
||||
*/
|
||||
private void initDebugDrawResources() {
|
||||
if (sDebugTextPaint == null) {
|
||||
sDebugTextPaint = new Paint();
|
||||
|
@ -320,6 +431,17 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in drawing layout bounds, draws a layout bounds rectangle similar to the Android default
|
||||
* implementation, with a specifiable border color.
|
||||
*
|
||||
* @param canvas The canvas to draw on.
|
||||
* @param color The border color of the layout bounds.
|
||||
* @param left Left bound of the rectangle.
|
||||
* @param top Top bound of the rectangle.
|
||||
* @param right Right bound of the rectangle.
|
||||
* @param bottom Bottom bound of the rectangle.
|
||||
*/
|
||||
private void debugDrawRect(
|
||||
Canvas canvas,
|
||||
int color,
|
||||
|
@ -330,6 +452,19 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
debugDrawNamedRect(canvas, color, "", left, top, right, bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in drawing layout bounds, draws a layout bounds rectangle similar to the Android default
|
||||
* implementation, with a specifiable border color. Also draws a name text in the bottom right
|
||||
* corner of the rectangle if DEBUG_DRAW_TEXT is set.
|
||||
*
|
||||
* @param canvas The canvas to draw on.
|
||||
* @param color The border color of the layout bounds.
|
||||
* @param name Name to be drawn on top of the rectangle if DEBUG_DRAW_TEXT is set.
|
||||
* @param left Left bound of the rectangle.
|
||||
* @param top Top bound of the rectangle.
|
||||
* @param right Right bound of the rectangle.
|
||||
* @param bottom Bottom bound of the rectangle.
|
||||
*/
|
||||
/* package */ void debugDrawNamedRect(
|
||||
Canvas canvas,
|
||||
int color,
|
||||
|
@ -385,7 +520,7 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
if (mIsAttached) {
|
||||
// this is possible, unfortunately.
|
||||
// This is possible, unfortunately.
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -552,6 +687,12 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the next child of the FlatViewGroup. Each draw view calls FlatViewGroup.drawNextChild,
|
||||
* which keeps track of the current child index to draw.
|
||||
*
|
||||
* @param canvas The canvas to draw on.
|
||||
*/
|
||||
/* package */ void drawNextChild(Canvas canvas) {
|
||||
View child = getChildAt(mDrawChildIndex);
|
||||
if (child instanceof FlatViewGroup) {
|
||||
|
@ -568,11 +709,38 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
++mDrawChildIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount a list of draw commands to this FlatViewGroup. Draw commands sometimes map to a view,
|
||||
* as in the case of {@link DrawView}, and sometimes to a simple canvas operation. We only
|
||||
* receive a call to mount draw commands when our commands have changed, so we always invalidate.
|
||||
*
|
||||
* A call to mount draw commands will only be followed by a call to mount views if the draw view
|
||||
* commands within the draw command array have changed since last mount.
|
||||
*
|
||||
* @param drawCommands The draw commands to mount.
|
||||
*/
|
||||
/* package */ void mountDrawCommands(DrawCommand[] drawCommands) {
|
||||
mDrawCommands = drawCommands;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount a list of draw commands to this FlatViewGroup, which is clipping subviews. Clipping
|
||||
* logic is handled by a {@link DrawCommandManager}, which provides a better explanation of
|
||||
* these arguments and logic.
|
||||
*
|
||||
* A call to mount draw commands will only be followed by a call to mount views if the draw view
|
||||
* commands within the draw command array have changed since last mount, which is indicated here
|
||||
* by willMountViews.
|
||||
*
|
||||
* @param drawCommands The draw commands to mount.
|
||||
* @param drawViewIndexMap See {@link DrawCommandManager}.
|
||||
* @param maxBottom See {@link DrawCommandManager}.
|
||||
* @param minTop See {@link DrawCommandManager}.
|
||||
* @param willMountViews True if we will also receive a mountViews call. If we are going to
|
||||
* receive a call to mount views, that will take care of updating the commands that are
|
||||
* currently onscreen, otherwise we need to update the onscreen commands.
|
||||
*/
|
||||
/* package */ void mountClippingDrawCommands(
|
||||
DrawCommand[] drawCommands,
|
||||
SparseIntArray drawViewIndexMap,
|
||||
|
@ -589,9 +757,10 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
}
|
||||
|
||||
/**
|
||||
* Finds a NodeRegion which matches the said reactTag
|
||||
* @param reactTag the reactTag to look for
|
||||
* @return the NodeRegion, or NodeRegion.EMPTY
|
||||
* Return the NodeRegion which matches a reactTag, or EMPTY if none match.
|
||||
*
|
||||
* @param reactTag The reactTag to look for
|
||||
* @return The matching NodeRegion, or NodeRegion.EMPTY if none match.
|
||||
*/
|
||||
/* package */ NodeRegion getNodeRegionForTag(int reactTag) {
|
||||
for (NodeRegion region : mNodeRegions) {
|
||||
|
@ -607,7 +776,7 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
* strong reference to. This is used by the FlatNativeViewHierarchyManager to explicitly clean up
|
||||
* those views when removing this parent.
|
||||
*
|
||||
* @return a Collection of Views to clean up
|
||||
* @return A Collection of Views to clean up.
|
||||
*/
|
||||
Collection<View> getDetachedViews() {
|
||||
if (mDrawCommandManager == null) {
|
||||
|
@ -618,9 +787,10 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
|
||||
/**
|
||||
* Remove the detached view from the parent
|
||||
* This is used during cleanup to trigger onDetachedFromWindow on any views that were in a
|
||||
* temporary detached state due to them being clipped. This is called for cleanup of said views
|
||||
* by FlatNativeViewHierarchyManager.
|
||||
* This is used in the DrawCommandManagers and during cleanup to trigger onDetachedFromWindow on
|
||||
* any views that were in a temporary detached state due to them being clipped. This is called
|
||||
* for cleanup of said views by FlatNativeViewHierarchyManager.
|
||||
*
|
||||
* @param view the detached View to remove
|
||||
*/
|
||||
void removeDetachedView(View view) {
|
||||
|
@ -637,6 +807,16 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
super.removeAllViewsInLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounts attach detach listeners to a FlatViewGroup. The Nodes spec states that children and
|
||||
* commands deal gracefully with multiple attaches and detaches, and as long as:
|
||||
*
|
||||
* attachCount - detachCount > 0
|
||||
*
|
||||
* Then children still consider themselves as attached.
|
||||
*
|
||||
* @param listeners The listeners to mount.
|
||||
*/
|
||||
/* package */ void mountAttachDetachListeners(AttachDetachListener[] listeners) {
|
||||
if (mIsAttached) {
|
||||
// Ordering of the following 2 statements is very important. While logically it makes sense to
|
||||
|
@ -657,10 +837,25 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
mAttachDetachListeners = listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount node regions to a FlatViewGroup. A node region is a touch target for a react tag. As
|
||||
* not all react tags map to a view, we use node regions to determine whether a non-native region
|
||||
* should receive a touch.
|
||||
*
|
||||
* @param nodeRegions The node regions to mount.
|
||||
*/
|
||||
/* package */ void mountNodeRegions(NodeRegion[] nodeRegions) {
|
||||
mNodeRegions = nodeRegions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount node regions in clipping. See {@link DrawCommandManager} for more complete
|
||||
* documentation.
|
||||
*
|
||||
* @param nodeRegions The node regions to mount.
|
||||
* @param maxBottom See {@link DrawCommandManager}.
|
||||
* @param minTop See {@link DrawCommandManager}.
|
||||
*/
|
||||
/* package */ void mountClippingNodeRegions(
|
||||
NodeRegion[] nodeRegions,
|
||||
float[] maxBottom,
|
||||
|
@ -723,18 +918,40 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the protected addViewInLayout call for the {@link DrawCommandManager}.
|
||||
*
|
||||
* @param view The view to add.
|
||||
*/
|
||||
/* package */ void addViewInLayout(View view) {
|
||||
addViewInLayout(view, -1, ensureLayoutParams(view.getLayoutParams()), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the protected addViewInLayout call for the {@link DrawCommandManager}.
|
||||
*
|
||||
* @param view The view to add.
|
||||
* @param index The index position at which to add this child.
|
||||
*/
|
||||
/* package */ void addViewInLayout(View view, int index) {
|
||||
addViewInLayout(view, index, ensureLayoutParams(view.getLayoutParams()), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the protected attachViewToParent call for the {@link DrawCommandManager}.
|
||||
*
|
||||
* @param view The view to attach.
|
||||
*/
|
||||
/* package */ void attachViewToParent(View view) {
|
||||
attachViewToParent(view, -1, ensureLayoutParams(view.getLayoutParams()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the protected attachViewToParent call for the {@link DrawCommandManager}.
|
||||
*
|
||||
* @param view The view to attach.
|
||||
* @param index The index position at which to attach this child.
|
||||
*/
|
||||
/* package */ void attachViewToParent(View view, int index) {
|
||||
attachViewToParent(view, index, ensureLayoutParams(view.getLayoutParams()));
|
||||
}
|
||||
|
@ -754,6 +971,10 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the view hierarchy is updated in {@link StateBuilder}, to process all the
|
||||
* FlatViewGroups that have requested layout.
|
||||
*/
|
||||
/* package */ static void processLayoutRequests() {
|
||||
for (int i = 0, numLayoutRequests = LAYOUT_REQUESTS.size(); i != numLayoutRequests; ++i) {
|
||||
FlatViewGroup flatViewGroup = LAYOUT_REQUESTS.get(i);
|
||||
|
@ -795,6 +1016,14 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
return new Rect(left, top, right, bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a virtual node region matching the specified x and y touch. Virtual in this case
|
||||
* means simply that the node region represents a command, rather than a native view.
|
||||
*
|
||||
* @param touchX The touch x coordinate.
|
||||
* @param touchY The touch y coordinate.
|
||||
* @return A virtual node region matching the specified touch, or null if no regions match.
|
||||
*/
|
||||
private @Nullable NodeRegion virtualNodeRegionWithinBounds(float touchX, float touchY) {
|
||||
if (mDrawCommandManager != null) {
|
||||
return mDrawCommandManager.virtualNodeRegionWithinBounds(touchX, touchY);
|
||||
|
@ -813,6 +1042,14 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a node region matching the specified x and y touch. Will search regions which
|
||||
* representing both commands and native views.
|
||||
*
|
||||
* @param touchX The touch x coordinate.
|
||||
* @param touchY The touch y coordinate.
|
||||
* @return A node region matching the specified touch, or null if no regions match.
|
||||
*/
|
||||
private @Nullable NodeRegion anyNodeRegionWithinBounds(float touchX, float touchY) {
|
||||
if (mDrawCommandManager != null) {
|
||||
return mDrawCommandManager.anyNodeRegionWithinBounds(touchX, touchY);
|
||||
|
@ -835,6 +1072,11 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate attach to a list of listeners, passing a callback by which they can invalidate.
|
||||
*
|
||||
* @param listeners List of listeners to attach.
|
||||
*/
|
||||
private void dispatchOnAttached(AttachDetachListener[] listeners) {
|
||||
int numListeners = listeners.length;
|
||||
if (numListeners == 0) {
|
||||
|
@ -847,6 +1089,11 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an invalidate callback singleton for this view instance.
|
||||
*
|
||||
* @return Invalidate callback singleton.
|
||||
*/
|
||||
private InvalidateCallback getInvalidateCallback() {
|
||||
if (mInvalidateCallback == null) {
|
||||
mInvalidateCallback = new InvalidateCallback(this);
|
||||
|
@ -854,6 +1101,11 @@ import com.facebook.react.views.view.ReactClippingViewGroup;
|
|||
return mInvalidateCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate detach to a list of listeners.
|
||||
*
|
||||
* @param listeners List of listeners to detach.
|
||||
*/
|
||||
private static void dispatchOnDetached(AttachDetachListener[] listeners) {
|
||||
for (AttachDetachListener listener : listeners) {
|
||||
listener.onDetached();
|
||||
|
|
|
@ -57,24 +57,44 @@ import android.util.SparseIntArray;
|
|||
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) {
|
||||
/**
|
||||
* Populates the max and min arrays for a given set of node regions.
|
||||
*
|
||||
* This should never be called from the UI thread, as the reason it exists is to do work off the
|
||||
* UI thread.
|
||||
*
|
||||
* @param regions The regions that will eventually be mounted.
|
||||
* @param maxRight At each index i, the maximum right value of all regions at or below i.
|
||||
* @param minLeft At each index i, the minimum left value of all regions at or below i.
|
||||
*/
|
||||
public static void fillMaxMinArrays(NodeRegion[] regions, float[] maxRight, float[] minLeft) {
|
||||
float last = 0;
|
||||
for (int i = 0; i < regions.length; i++) {
|
||||
last = Math.max(last, regions[i].mRight);
|
||||
maxBottom[i] = last;
|
||||
maxRight[i] = last;
|
||||
}
|
||||
for (int i = regions.length - 1; i >= 0; i--) {
|
||||
last = Math.min(last, regions[i].mLeft);
|
||||
minTop[i] = last;
|
||||
minLeft[i] = last;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the max and min arrays for a given set of draw commands. Also populates a mapping of
|
||||
* react tags to their index position in the command array.
|
||||
*
|
||||
* This should never be called from the UI thread, as the reason it exists is to do work off the
|
||||
* UI thread.
|
||||
*
|
||||
* @param commands The draw commands that will eventually be mounted.
|
||||
* @param maxRight At each index i, the maximum right value of all draw commands at or below i.
|
||||
* @param minLeft At each index i, the minimum left value of all draw commands at or below i.
|
||||
* @param drawViewIndexMap Mapping of ids to index position within the draw command array.
|
||||
*/
|
||||
public static void fillMaxMinArrays(
|
||||
DrawCommand[] commands,
|
||||
float[] maxBottom,
|
||||
float[] minTop,
|
||||
float[] maxRight,
|
||||
float[] minLeft,
|
||||
SparseIntArray drawViewIndexMap) {
|
||||
float last = 0;
|
||||
// Loop through the DrawCommands, keeping track of the maximum we've seen if we only iterated
|
||||
|
@ -88,7 +108,7 @@ import android.util.SparseIntArray;
|
|||
} else {
|
||||
last = Math.max(last, commands[i].getRight());
|
||||
}
|
||||
maxBottom[i] = last;
|
||||
maxRight[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.
|
||||
|
@ -100,7 +120,7 @@ import android.util.SparseIntArray;
|
|||
} else {
|
||||
last = Math.min(last, commands[i].getLeft());
|
||||
}
|
||||
minTop[i] = last;
|
||||
minLeft[i] = last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,17 +26,17 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
|
||||
/**
|
||||
* Shadow node hierarchy by itself cannot display UI, it is only a representation of what UI should
|
||||
* be from JavaScript perspective. StateBuilder is a helper class that can walk the shadow node tree
|
||||
* and collect information that can then be passed to UI thread and applied to a hierarchy of Views
|
||||
* that Android finally can display.
|
||||
* be from JavaScript perspective. StateBuilder is a helper class that walks the shadow node tree
|
||||
* and collects information into an operation queue that is run on the UI thread and applied to the
|
||||
* non-shadow hierarchy of Views that Android can finally 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;
|
||||
|
||||
// Optimization to avoid re-allocating zero length arrays.
|
||||
private static final int[] EMPTY_INT_ARRAY = new int[0];
|
||||
|
||||
private final FlatUIViewOperationQueue mOperationsQueue;
|
||||
|
@ -70,8 +70,9 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
}
|
||||
|
||||
/**
|
||||
* Given a root of the laid-out shadow node hierarchy, walks the tree and generates an array of
|
||||
* DrawCommands that will then mount in UI thread to a root FlatViewGroup so that it can draw.
|
||||
* Given a root of the laid-out shadow node hierarchy, walks the tree and generates arrays from
|
||||
* element lists that are mounted in the UI thread to FlatViewGroups to handle drawing, touch,
|
||||
* and other logic.
|
||||
*/
|
||||
/* package */ void applyUpdates(FlatShadowNode node) {
|
||||
float width = node.getLayoutWidth();
|
||||
|
@ -95,6 +96,13 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
updateViewBounds(node, left, top, right, bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run after the shadow node hierarchy is updated. Detaches all children from Views that are
|
||||
* changing their native children, updates views, and dispatches commands before discarding any
|
||||
* dropped views.
|
||||
*
|
||||
* @param eventDispatcher Dispatcher for onLayout events.
|
||||
*/
|
||||
void afterUpdateViewHierarchy(EventDispatcher eventDispatcher) {
|
||||
if (mDetachAllChildrenFromViews != null) {
|
||||
int[] viewsToDetachAllChildrenFrom = collectViewTags(mViewsToDetachAllChildrenFrom);
|
||||
|
@ -109,7 +117,7 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
}
|
||||
mUpdateViewBoundsOperations.clear();
|
||||
|
||||
// Process view manager commands after bounds operations, so that any ui operations have already
|
||||
// Process view manager commands after bounds operations, so that any UI operations have already
|
||||
// happened before we actually dispatch the view manager command. This prevents things like
|
||||
// commands going to empty parents and views not yet being created.
|
||||
for (int i = 0, size = mViewManagerCommands.size(); i != size; i++) {
|
||||
|
@ -143,16 +151,33 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds a DrawCommand for current mountable node.
|
||||
* Adds a draw command to the element list for the current scope. Allows collectState within the
|
||||
* shadow node to add commands.
|
||||
*
|
||||
* @param drawCommand The draw command to add.
|
||||
*/
|
||||
/* package */ void addDrawCommand(AbstractDrawCommand drawCommand) {
|
||||
mDrawCommands.add(drawCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to the element list for the current scope. Allows collectState within the
|
||||
* shadow node to add listeners.
|
||||
*
|
||||
* @param listener The listener to add
|
||||
*/
|
||||
/* package */ void addAttachDetachListener(AttachDetachListener listener) {
|
||||
mAttachDetachListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a command for a view manager to the queue. We have to delay adding it to the operations
|
||||
* queue until we have added our view moves, creations and updates.
|
||||
*
|
||||
* @param reactTag The react tag of the command target.
|
||||
* @param commandId ID of the command.
|
||||
* @param commandArgs Arguments for the command.
|
||||
*/
|
||||
/* package */ void enqueueViewManagerCommand(
|
||||
int reactTag,
|
||||
int commandId,
|
||||
|
@ -161,11 +186,17 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
mOperationsQueue.createViewManagerCommand(reactTag, commandId, commandArgs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a backing view for a node, or update the backing view if it has already been created.
|
||||
*
|
||||
* @param node The node to create the backing view for.
|
||||
* @param styles Styles for the view.
|
||||
*/
|
||||
/* package */ void enqueueCreateOrUpdateView(
|
||||
FlatShadowNode node,
|
||||
@Nullable ReactStylesDiffMap styles) {
|
||||
if (node.isBackingViewCreated()) {
|
||||
// if the View is already created, make sure propagate new styles.
|
||||
// If the View is already created, make sure to propagate the new styles.
|
||||
mOperationsQueue.enqueueUpdateProperties(
|
||||
node.getReactTag(),
|
||||
node.getViewClass(),
|
||||
|
@ -181,6 +212,11 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a backing view for a node if not already created.
|
||||
*
|
||||
* @param node The node to create the backing view for.
|
||||
*/
|
||||
/* package */ void ensureBackingViewIsCreated(FlatShadowNode node) {
|
||||
if (node.isBackingViewCreated()) {
|
||||
return;
|
||||
|
@ -192,10 +228,28 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
node.signalBackingViewIsCreated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue dropping of the view for a node that has a backing view. Used in conjuction with
|
||||
* remove the node from the shadow hierarchy.
|
||||
*
|
||||
* @param node The node to drop the backing view for.
|
||||
*/
|
||||
/* package */ void dropView(FlatShadowNode node) {
|
||||
mViewsToDrop.add(node.getReactTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a node region to the element list for the current scope. Allows collectState to add
|
||||
* regions.
|
||||
*
|
||||
* @param node The node to add a region for.
|
||||
* @param left Bound of the region.
|
||||
* @param top Bound of the region.
|
||||
* @param right Bound of the region.
|
||||
* @param bottom Bound of the region.
|
||||
* @param isVirtual True if the region does not map to a native view. Used to determine touch
|
||||
* targets.
|
||||
*/
|
||||
private void addNodeRegion(
|
||||
FlatShadowNode node,
|
||||
float left,
|
||||
|
@ -212,6 +266,12 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
mNodeRegions.add(node.getNodeRegion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a native child to the element list for the current scope. Allows collectState to add
|
||||
* native children.
|
||||
*
|
||||
* @param nativeChild The view-backed native child to add.
|
||||
*/
|
||||
private void addNativeChild(FlatShadowNode nativeChild) {
|
||||
mNativeChildren.add(nativeChild);
|
||||
}
|
||||
|
@ -244,8 +304,9 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
}
|
||||
|
||||
/**
|
||||
* Collects state (DrawCommands) for a given node that will mount to a View.
|
||||
* Returns true if this node or any of its descendants that mount to View generated any updates.
|
||||
* Collects state (Draw commands, listeners, regions, native children) for a given node that will
|
||||
* mount to a View. Returns true if this node or any of its descendants that mount to View
|
||||
* generated any updates.
|
||||
*/
|
||||
private boolean collectStateForMountableNode(
|
||||
FlatShadowNode node,
|
||||
|
@ -413,6 +474,14 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles updating the children of a node when they change. Updates the shadow node and
|
||||
* enqueues state updates that will eventually be run on the UI thread.
|
||||
*
|
||||
* @param node The node to update native children for.
|
||||
* @param oldNativeChildren The previously mounted native children.
|
||||
* @param newNativeChildren The newly mounted native children.
|
||||
*/
|
||||
private void updateNativeChildren(
|
||||
FlatShadowNode node,
|
||||
FlatShadowNode[] oldNativeChildren,
|
||||
|
@ -470,7 +539,9 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
}
|
||||
|
||||
/**
|
||||
* Recursively walks node tree from a given node and collects DrawCommands.
|
||||
* Recursively walks node tree from a given node and collects draw commands, listeners, node
|
||||
* regions and native children. Calls collect state on the node, then processNodeAndCollectState
|
||||
* for the recursion.
|
||||
*/
|
||||
private boolean collectStateRecursively(
|
||||
FlatShadowNode node,
|
||||
|
@ -548,6 +619,11 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively walks this node and child nodes, marking the layout state as UP_TO_DATE.
|
||||
*
|
||||
* @param node The node to recur down from.
|
||||
*/
|
||||
private void markLayoutSeenRecursively(ReactShadowNode node) {
|
||||
if (node.hasNewLayout()) {
|
||||
node.markLayoutSeen();
|
||||
|
@ -559,8 +635,8 @@ import com.facebook.react.uimanager.events.EventDispatcher;
|
|||
}
|
||||
|
||||
/**
|
||||
* Collects state and updates View boundaries for a given node tree.
|
||||
* Returns true if this node or any of its descendants that mount to View generated any updates.
|
||||
* Collects state and enqueues View boundary updates for a given node tree. Returns true if
|
||||
* this node or any of its descendants that mount to View generated any updates.
|
||||
*/
|
||||
private boolean processNodeAndCollectState(
|
||||
FlatShadowNode node,
|
||||
|
|
|
@ -57,13 +57,21 @@ import android.util.SparseIntArray;
|
|||
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) {
|
||||
/**
|
||||
* Populates the max and min arrays for a given set of node regions.
|
||||
*
|
||||
* This should never be called from the UI thread, as the reason it exists is to do work off the
|
||||
* UI thread.
|
||||
*
|
||||
* @param regions The regions that will eventually be mounted.
|
||||
* @param maxBottom At each index i, the maximum bottom value of all regions at or below i.
|
||||
* @param minTop At each index i, the minimum top value of all regions at or below i.
|
||||
*/
|
||||
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].mBottom);
|
||||
maxBot[i] = last;
|
||||
maxBottom[i] = last;
|
||||
}
|
||||
for (int i = regions.length - 1; i >= 0; i--) {
|
||||
last = Math.min(last, regions[i].mTop);
|
||||
|
@ -71,11 +79,21 @@ import android.util.SparseIntArray;
|
|||
}
|
||||
}
|
||||
|
||||
// These should never be called from the UI thread, as the reason they exist is to do work off
|
||||
// the UI thread.
|
||||
/**
|
||||
* Populates the max and min arrays for a given set of draw commands. Also populates a mapping of
|
||||
* react tags to their index position in the command array.
|
||||
*
|
||||
* This should never be called from the UI thread, as the reason it exists is to do work off the
|
||||
* UI thread.
|
||||
*
|
||||
* @param commands The draw commands that will eventually be mounted.
|
||||
* @param maxBottom At each index i, the maximum bottom value of all draw commands at or below i.
|
||||
* @param minTop At each index i, the minimum top value of all draw commands at or below i.
|
||||
* @param drawViewIndexMap Mapping of ids to index position within the draw command array.
|
||||
*/
|
||||
public static void fillMaxMinArrays(
|
||||
DrawCommand[] commands,
|
||||
float[] maxBot,
|
||||
float[] maxBottom,
|
||||
float[] minTop,
|
||||
SparseIntArray drawViewIndexMap) {
|
||||
float last = 0;
|
||||
|
@ -90,7 +108,7 @@ import android.util.SparseIntArray;
|
|||
} else {
|
||||
last = Math.max(last, commands[i].getBottom());
|
||||
}
|
||||
maxBot[i] = last;
|
||||
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.
|
||||
|
|
Loading…
Reference in New Issue