mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-04 09:27:53 +08:00
Summary: Adds a flag that can be set in FlatViewGroup to see known performance issues in Nodes objects. This is mostly useful in internal development of Nodes, and will be a dead code path when not set. Reviewed By: ahmedre Differential Revision: D3732675
236 lines
7.6 KiB
Java
236 lines
7.6 KiB
Java
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
package com.facebook.react.flat;
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Path;
|
|
import android.graphics.RectF;
|
|
|
|
/* package */ final class DrawView extends AbstractDrawCommand {
|
|
public static final DrawView[] EMPTY_ARRAY = new DrawView[0];
|
|
// the minimum rounded clipping value before we actually do rounded clipping
|
|
/* package */ static final float MINIMUM_ROUNDED_CLIPPING_VALUE = 0.5f;
|
|
private final RectF TMP_RECT = new RectF();
|
|
|
|
/* package */ final int reactTag;
|
|
// Indicates whether this DrawView has been previously mounted to a clipping FlatViewGroup. This
|
|
// lets us know that the bounds haven't changed, as a bounds change would trigger a new DrawView,
|
|
// which will set this to false for the new DrawView. This is safe, despite the dual access with
|
|
// FlatViewGroup, because the FlatViewGroup copy is only ever modified by the FlatViewGroup.
|
|
// Changing how this boolean is used should be handled with caution, as race conditions are the
|
|
// quickest way to create unreproducible super bugs.
|
|
/* package */ boolean mWasMounted;
|
|
|
|
// the clipping radius - if this is greater than MINIMUM_ROUNDED_CLIPPING_VALUE, we clip using
|
|
// a rounded path, otherwise we clip in a rectangular fashion.
|
|
private float mClipRadius;
|
|
|
|
// the path to clip against if we're doing path clipping for rounded borders.
|
|
@Nullable private Path mPath;
|
|
|
|
// These should only ever be set from within the DrawView, they serve to provide clipping bounds
|
|
// for FlatViewGroups, which have strange clipping when it comes to overflow: visible. They are
|
|
// left package protected to speed up direct access. For overflow visible, these are the adjusted
|
|
// bounds while taking overflowing elements into account, other wise they are just the regular
|
|
// bounds of the view.
|
|
/* package */ float mLogicalLeft;
|
|
/* package */ float mLogicalTop;
|
|
/* package */ float mLogicalRight;
|
|
/* package */ float mLogicalBottom;
|
|
|
|
public DrawView(int reactTag) {
|
|
this.reactTag = reactTag;
|
|
}
|
|
|
|
/**
|
|
* Similar to updateBoundsAndFreeze, but thread safe as the mounting flag is modified on the UI
|
|
* thread.
|
|
*
|
|
* @return A DrawView with the passed bounds and clipping bounds. If we can use the same
|
|
* DrawView, it will just be this, otherwise it will be a frozen copy.
|
|
*/
|
|
public DrawView collectDrawView(
|
|
float left,
|
|
float top,
|
|
float right,
|
|
float bottom,
|
|
float logicalLeft,
|
|
float logicalTop,
|
|
float logicalRight,
|
|
float logicalBottom,
|
|
float clipLeft,
|
|
float clipTop,
|
|
float clipRight,
|
|
float clipBottom,
|
|
float clipRadius) {
|
|
if (!isFrozen()) {
|
|
// We haven't collected this draw view yet, so we can just set everything.
|
|
setBounds(left, top, right, bottom);
|
|
setClipBounds(clipLeft, clipTop, clipRight, clipBottom);
|
|
setClipRadius(clipRadius);
|
|
setLogicalBounds(logicalLeft, logicalTop, logicalRight, logicalBottom);
|
|
freeze();
|
|
return this;
|
|
}
|
|
|
|
boolean boundsMatch = boundsMatch(left, top, right, bottom);
|
|
boolean clipBoundsMatch = clipBoundsMatch(clipLeft, clipTop, clipRight, clipBottom);
|
|
boolean clipRadiusMatch = mClipRadius == clipRadius;
|
|
boolean logicalBoundsMatch =
|
|
logicalBoundsMatch(logicalLeft, logicalTop, logicalRight, logicalBottom);
|
|
|
|
// See if we can reuse the draw view.
|
|
if (boundsMatch && clipBoundsMatch && clipRadiusMatch && logicalBoundsMatch) {
|
|
return this;
|
|
}
|
|
|
|
DrawView drawView = (DrawView) mutableCopy();
|
|
|
|
if (!boundsMatch) {
|
|
drawView.setBounds(left, top, right, bottom);
|
|
}
|
|
|
|
if (!clipBoundsMatch) {
|
|
drawView.setClipBounds(clipLeft, clipTop, clipRight, clipBottom);
|
|
}
|
|
|
|
if (!logicalBoundsMatch) {
|
|
drawView.setLogicalBounds(logicalLeft, logicalTop, logicalRight, logicalBottom);
|
|
}
|
|
|
|
if (!clipRadiusMatch || !boundsMatch) {
|
|
// If the bounds change, we need to update the clip path.
|
|
drawView.setClipRadius(clipRadius);
|
|
}
|
|
|
|
// It is very important that we unset this, as our spec is that newly created DrawViews
|
|
// are handled differently by the FlatViewGroup. This is needed because clone() maintains
|
|
// the previous state.
|
|
drawView.mWasMounted = false;
|
|
|
|
drawView.freeze();
|
|
|
|
return drawView;
|
|
}
|
|
|
|
private boolean logicalBoundsMatch(float left, float top, float right, float bottom) {
|
|
return left == mLogicalLeft && top == mLogicalTop &&
|
|
right == mLogicalRight && bottom == mLogicalBottom;
|
|
}
|
|
|
|
private void setLogicalBounds(float left, float top, float right, float bottom) {
|
|
// Do rounding up front and off of the UI thread.
|
|
mLogicalLeft = left;
|
|
mLogicalTop = top;
|
|
mLogicalRight = right;
|
|
mLogicalBottom = bottom;
|
|
}
|
|
|
|
@Override
|
|
public void draw(FlatViewGroup parent, Canvas canvas) {
|
|
onPreDraw(parent, canvas);
|
|
if (mNeedsClipping || mClipRadius > MINIMUM_ROUNDED_CLIPPING_VALUE) {
|
|
canvas.save(Canvas.CLIP_SAVE_FLAG);
|
|
applyClipping(canvas);
|
|
parent.drawNextChild(canvas);
|
|
canvas.restore();
|
|
} else {
|
|
parent.drawNextChild(canvas);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the clip radius. Should only be called when the clip radius is first set or when it
|
|
* changes, in order to avoid extra work.
|
|
*
|
|
* @param clipRadius The new clip radius.
|
|
*/
|
|
void setClipRadius(float clipRadius) {
|
|
mClipRadius = clipRadius;
|
|
if (clipRadius > MINIMUM_ROUNDED_CLIPPING_VALUE) {
|
|
// update the path that we'll clip based on
|
|
updateClipPath();
|
|
} else {
|
|
mPath = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the path with which we'll clip this view
|
|
*/
|
|
private void updateClipPath() {
|
|
mPath = new Path();
|
|
|
|
TMP_RECT.set(
|
|
getLeft(),
|
|
getTop(),
|
|
getRight(),
|
|
getBottom());
|
|
|
|
// set the path
|
|
mPath.addRoundRect(
|
|
TMP_RECT,
|
|
mClipRadius,
|
|
mClipRadius,
|
|
Path.Direction.CW);
|
|
}
|
|
|
|
@Override
|
|
protected void applyClipping(Canvas canvas) {
|
|
// only clip using a path if our radius is greater than some minimum threshold, because
|
|
// clipPath is more expensive than clipRect.
|
|
if (mClipRadius > MINIMUM_ROUNDED_CLIPPING_VALUE) {
|
|
canvas.clipPath(mPath);
|
|
} else {
|
|
super.applyClipping(canvas);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onDraw(Canvas canvas) {
|
|
// no op as we override draw.
|
|
}
|
|
|
|
@Override
|
|
protected void onDebugDraw(FlatViewGroup parent, Canvas canvas) {
|
|
parent.debugDrawNextChild(canvas);
|
|
}
|
|
|
|
@Override
|
|
protected void onDebugDrawHighlight(Canvas canvas) {
|
|
if (mPath != null) {
|
|
debugDrawWarningHighlight(canvas, "borderRadius: " + mClipRadius);
|
|
} else if (!boundsMatch(mLogicalLeft, mLogicalTop, mLogicalRight, mLogicalBottom)) {
|
|
StringBuilder warn = new StringBuilder("Overflow: { ");
|
|
String[] names = { "left: ", "top: ", "right: ", "bottom: "};
|
|
int i = 0;
|
|
float[] offsets = new float[4];
|
|
offsets[i++] = getLeft() - mLogicalLeft;
|
|
offsets[i++] = getTop() - mLogicalTop;
|
|
offsets[i++] = mLogicalRight - getRight();
|
|
offsets[i++] = mLogicalBottom - getBottom();
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
if (offsets[i] != 0f) {
|
|
warn.append(names[i]);
|
|
warn.append(offsets[i]);
|
|
warn.append(", ");
|
|
}
|
|
}
|
|
|
|
warn.append("}");
|
|
|
|
debugDrawCautionHighlight(canvas, warn.toString());
|
|
}
|
|
}
|
|
}
|