diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/AbstractDrawCommand.java b/ReactAndroid/src/main/java/com/facebook/react/flat/AbstractDrawCommand.java index 5c7196d07..c8729923c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/AbstractDrawCommand.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/AbstractDrawCommand.java @@ -10,6 +10,7 @@ package com.facebook.react.flat; import android.graphics.Canvas; +import android.graphics.Color; /** * Base class for all DrawCommands. Becomes immutable once it has its bounds set. Until then, a @@ -40,6 +41,26 @@ import android.graphics.Canvas; } } + protected static int getDebugBorderColor() { + return Color.CYAN; + } + + protected String getDebugName() { + return getClass().getSimpleName().substring(4); + } + + @Override + public void debugDraw(FlatViewGroup parent, Canvas canvas) { + parent.debugDrawNamedRect( + canvas, + getDebugBorderColor(), + getDebugName(), + mLeft, + mTop, + mRight, + mBottom); + } + protected void onPreDraw(FlatViewGroup parent, Canvas canvas) { } diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawCommand.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawCommand.java index 3191180c8..3a79338a4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawCommand.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawCommand.java @@ -23,7 +23,16 @@ public interface DrawCommand { /** * Performs drawing into the given canvas. * + * @param parent The parent to get child information from, if needed * @param canvas The canvas to draw into */ public void draw(FlatViewGroup parent, Canvas canvas); + + /** + * Performs debug bounds drawing into the given canvas. + * + * @param parent The parent to get child information from, if needed + * @param canvas The canvas to draw into + */ + public void debugDraw(FlatViewGroup parent, Canvas canvas); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawView.java b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawView.java index 9fdad200d..207acef53 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/DrawView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/DrawView.java @@ -32,4 +32,9 @@ import android.graphics.Canvas; parent.drawNextChild(canvas); } } + + @Override + public void debugDraw(FlatViewGroup parent, Canvas canvas) { + parent.debugDrawNextChild(canvas); + } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java index eb0a7b4ba..77f422d7d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatViewGroup.java @@ -20,6 +20,8 @@ import java.util.Map; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.view.MotionEvent; @@ -81,6 +83,15 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper; } } + private static final boolean DEBUG_DRAW = false; + private static final boolean DEBUG_DRAW_TEXT = false; + private boolean mAndroidDebugDraw; + private static Paint sDebugTextPaint; + private static Paint sDebugTextBackgroundPaint; + private static Paint sDebugRectPaint; + private static Paint sDebugCornerPaint; + private static Rect sDebugRect; + private static final ArrayList LAYOUT_REQUESTS = new ArrayList<>(); private static final Rect VIEW_BOUNDS = new Rect(); private static final Rect EMPTY_RECT = new Rect(); @@ -162,8 +173,15 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper; return nodeRegion != null && nodeRegion.mIsVirtual; } + // This is hidden in the Android ViewGroup, but still gets called in super.dispatchDraw. + protected void onDebugDraw(Canvas canvas) { + // Android is drawing layout bounds, so we should as well. + mAndroidDebugDraw = true; + } + @Override public void dispatchDraw(Canvas canvas) { + mAndroidDebugDraw = false; super.dispatchDraw(canvas); if (mRemoveClippedSubviews) { @@ -189,11 +207,30 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper; } mDrawChildIndex = 0; + if (DEBUG_DRAW || mAndroidDebugDraw) { + initDebugDrawResources(); + debugDraw(canvas); + } + if (mHotspot != null) { mHotspot.draw(canvas); } } + private void debugDraw(Canvas canvas) { + for (DrawCommand drawCommand : mDrawCommands) { + if (drawCommand instanceof DrawView) { + if (!((DrawView) drawCommand).isViewGroupClipped) { + drawCommand.debugDraw(this, canvas); + } + // else, don't draw, and don't increment index + } else { + drawCommand.debugDraw(this, canvas); + } + } + mDrawChildIndex = 0; + } + @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { // suppress @@ -201,6 +238,153 @@ import com.facebook.react.views.view.ReactClippingViewGroupHelper; return false; } + /* package */ void debugDrawNextChild(Canvas canvas) { + View child = getChildAt(mDrawChildIndex); + // Draw FlatViewGroups a different color than regular child views. + int color = child instanceof FlatViewGroup ? Color.DKGRAY : Color.RED; + debugDrawRect( + canvas, + color, + child.getLeft(), + child.getTop(), + child.getRight(), + child.getBottom()); + ++mDrawChildIndex; + } + + // Used in debug drawing. + private int dipsToPixels(int dips) { + float scale = getResources().getDisplayMetrics().density; + return (int) (dips * scale + 0.5f); + } + + // Used in debug drawing. + private static void fillRect(Canvas canvas, Paint paint, float x1, float y1, float x2, float y2) { + if (x1 != x2 && y1 != y2) { + if (x1 > x2) { + float tmp = x1; x1 = x2; x2 = tmp; + } + if (y1 > y2) { + float tmp = y1; y1 = y2; y2 = tmp; + } + canvas.drawRect(x1, y1, x2, y2, paint); + } + } + + // Used in debug drawing. + private static int sign(float x) { + return (x >= 0) ? 1 : -1; + } + + // Used in debug drawing. + private static void drawCorner( + Canvas c, + Paint paint, + float x1, + float y1, + float dx, + float dy, + float lw) { + fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy)); + fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy); + } + + // Used in debug drawing. + private static void drawRectCorners( + Canvas canvas, + float x1, + float y1, + float x2, + float y2, + Paint paint, + int lineLength, + int lineWidth) { + drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth); + drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth); + drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth); + drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth); + } + + private void initDebugDrawResources() { + if (sDebugTextPaint == null) { + sDebugTextPaint = new Paint(); + sDebugTextPaint.setTextAlign(Paint.Align.RIGHT); + sDebugTextPaint.setTextSize(dipsToPixels(9)); + sDebugTextPaint.setColor(Color.RED); + } + if (sDebugTextBackgroundPaint == null) { + sDebugTextBackgroundPaint = new Paint(); + sDebugTextBackgroundPaint.setColor(Color.WHITE); + sDebugTextBackgroundPaint.setAlpha(200); + sDebugTextBackgroundPaint.setStyle(Paint.Style.FILL); + } + if (sDebugRectPaint == null) { + sDebugRectPaint = new Paint(); + sDebugRectPaint.setAlpha(100); + sDebugRectPaint.setStyle(Paint.Style.STROKE); + } + if (sDebugCornerPaint == null) { + sDebugCornerPaint = new Paint(); + sDebugCornerPaint.setAlpha(200); + sDebugCornerPaint.setColor(Color.rgb(63, 127, 255)); + sDebugCornerPaint.setStyle(Paint.Style.FILL); + } + if (sDebugRect == null) { + sDebugRect = new Rect(); + } + } + + private void debugDrawRect( + Canvas canvas, + int color, + float left, + float top, + float right, + float bottom) { + debugDrawNamedRect(canvas, color, "", left, top, right, bottom); + } + + /* package */ void debugDrawNamedRect( + Canvas canvas, + int color, + String name, + float left, + float top, + float right, + float bottom) { + if (DEBUG_DRAW_TEXT && !name.isEmpty()) { + sDebugTextPaint.getTextBounds(name, 0, name.length(), sDebugRect); + int inset = dipsToPixels(2); + float textRight = right - inset - 1; + float textBottom = bottom - inset - 1; + canvas.drawRect( + textRight - sDebugRect.right - inset, + textBottom + sDebugRect.top - inset, + textRight + inset, + textBottom + inset, + sDebugTextBackgroundPaint); + canvas.drawText(name, textRight, textBottom, sDebugTextPaint); + } + // Retain the alpha component. + sDebugRectPaint.setColor((sDebugRectPaint.getColor() & 0xFF000000) | (color & 0x00FFFFFF)); + sDebugRectPaint.setAlpha(100); + canvas.drawRect( + left, + top, + right - 1, + bottom - 1, + sDebugRectPaint); + drawRectCorners( + canvas, + left, + top, + right, + bottom, + sDebugCornerPaint, + dipsToPixels(8), + dipsToPixels(1)); + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // nothing to do here