mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
rounded Views with borders shows small gap
Summary: When a __rounded__ View on Android has a border, a small gap appears between the border and the center of the view (most noticeably when the background and border colors are the same) Since the border is drawn on top of the other layers, the approach here is to make the center of the View slightly larger so that there is an overlap with the border, and closing the visible gap There are 2 cases for a View with a border: 1. `borderWidth` is set for a consistent border width around all 4 edges 2. Uneven border widths are set (using `borderTopWidth`, `borderLeftWidth, ...) **How is a rounded rectangle drawn?** __Case 1__: `borderWidth` is set for a consistent border width around all 4 edges - Before: - first, `mInnerClipPathForBorderRadius` was used to draw the center of the View - then the border is drawn along the path of `mCenterDraw` with a stroke width of the border width - Now: - `mBackgroundColorRenderPath` is used to draw the center of the View and is exactly a slightly enlarged version of `mInnerClipPathForBorderRadius` __Case 2__: Uneven border widths are set (using borderTopWidth, borderLeftWidth, ...) - Before: - `mInnerClipPathForBorderRadius` was used to draw the center of the View - for each edge, a quadrilateral is drawn - `mOuterClipPathForBorderRadius` clips the outer edge of the border - `mInnerClipPathForBorderRadius` (same is used to draw the center of the View) clips the inner edge of the border - Now: - `mBackgroundColorRenderPath` is used to draw the center of the View, allowing `mInnerClipPathForBorderRadius` to persist as the path that clips the inner edge of the border When `mGapBetweenPaths` = 0, `mBackgroundColorRenderPath` == `mInnerClipPathForBorderRadius`, which is exactly the original implementation Changelog: [Internal][Fixed] - rounded Views with borders shows small gap Reviewed By: mdvacca Differential Revision: D39979567 fbshipit-source-id: 6db71d14ead6256e1b7becf73862e0a537c6a47b
This commit is contained in:
committed by
Facebook GitHub Bot
parent
6743d15329
commit
29caed22cc
@@ -126,4 +126,10 @@ public class ReactFeatureFlags {
|
||||
|
||||
// TODO (T136375139): Remove this once finish testing
|
||||
public static boolean enableAtomicRegisterSegment = false;
|
||||
|
||||
/**
|
||||
* Allow closing the small gap that appears between paths when drawing a rounded View with a
|
||||
* border.
|
||||
*/
|
||||
public static boolean enableCloseVisibleGapBetweenPaths = true;
|
||||
}
|
||||
|
||||
+59
-19
@@ -27,6 +27,7 @@ import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.react.modules.i18nmanager.I18nUtil;
|
||||
import com.facebook.react.uimanager.FloatUtil;
|
||||
import com.facebook.react.uimanager.Spacing;
|
||||
@@ -86,6 +87,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
private @Nullable BorderStyle mBorderStyle;
|
||||
|
||||
private @Nullable Path mInnerClipPathForBorderRadius;
|
||||
private @Nullable Path mBackgroundColorRenderPath;
|
||||
private @Nullable Path mOuterClipPathForBorderRadius;
|
||||
private @Nullable Path mPathForBorderRadiusOutline;
|
||||
private @Nullable Path mPathForBorder;
|
||||
@@ -107,6 +109,13 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
private int mColor = Color.TRANSPARENT;
|
||||
private int mAlpha = 255;
|
||||
|
||||
// There is a small gap between the edges of adjacent paths
|
||||
// such as between the mBackgroundColorRenderPath and its border.
|
||||
// The smallest amount (found to be 0.8f) is used to extend
|
||||
// the paths, overlapping them and closing the visible gap.
|
||||
private final float mGapBetweenPaths =
|
||||
ReactFeatureFlags.enableCloseVisibleGapBetweenPaths ? 0.8f : 0.0f;
|
||||
|
||||
private @Nullable float[] mBorderCornerRadii;
|
||||
private final Context mContext;
|
||||
private int mLayoutDirection;
|
||||
@@ -329,11 +338,15 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
updatePath();
|
||||
canvas.save();
|
||||
|
||||
// Clip outer border
|
||||
canvas.clipPath(mOuterClipPathForBorderRadius, Region.Op.INTERSECT);
|
||||
|
||||
// Draws the View without its border first (with background color fill)
|
||||
int useColor = ColorUtil.multiplyColorAlpha(mColor, mAlpha);
|
||||
if (Color.alpha(useColor) != 0) { // color is not transparent
|
||||
mPaint.setColor(useColor);
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
canvas.drawPath(mInnerClipPathForBorderRadius, mPaint);
|
||||
canvas.drawPath(mBackgroundColorRenderPath, mPaint);
|
||||
}
|
||||
|
||||
final RectF borderWidth = getDirectionAwareBorderInsets();
|
||||
@@ -369,8 +382,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
else {
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
// Draw border
|
||||
canvas.clipPath(mOuterClipPathForBorderRadius, Region.Op.INTERSECT);
|
||||
// Clip inner border
|
||||
canvas.clipPath(mInnerClipPathForBorderRadius, Region.Op.DIFFERENCE);
|
||||
|
||||
final boolean isRTL = getResolvedLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
|
||||
@@ -416,27 +428,29 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
final float top = mOuterClipTempRectForBorderRadius.top;
|
||||
final float bottom = mOuterClipTempRectForBorderRadius.bottom;
|
||||
|
||||
// mGapBetweenPaths is used to close the gap between the diagonal
|
||||
// edges of the quadrilaterals on adjacent sides of the rectangle
|
||||
if (borderWidth.left > 0) {
|
||||
final float x1 = left;
|
||||
final float y1 = top;
|
||||
final float y1 = top - mGapBetweenPaths;
|
||||
final float x2 = mInnerTopLeftCorner.x;
|
||||
final float y2 = mInnerTopLeftCorner.y;
|
||||
final float y2 = mInnerTopLeftCorner.y - mGapBetweenPaths;
|
||||
final float x3 = mInnerBottomLeftCorner.x;
|
||||
final float y3 = mInnerBottomLeftCorner.y;
|
||||
final float y3 = mInnerBottomLeftCorner.y + mGapBetweenPaths;
|
||||
final float x4 = left;
|
||||
final float y4 = bottom;
|
||||
final float y4 = bottom + mGapBetweenPaths;
|
||||
|
||||
drawQuadrilateral(canvas, colorLeft, x1, y1, x2, y2, x3, y3, x4, y4);
|
||||
}
|
||||
|
||||
if (borderWidth.top > 0) {
|
||||
final float x1 = left;
|
||||
final float x1 = left - mGapBetweenPaths;
|
||||
final float y1 = top;
|
||||
final float x2 = mInnerTopLeftCorner.x;
|
||||
final float x2 = mInnerTopLeftCorner.x - mGapBetweenPaths;
|
||||
final float y2 = mInnerTopLeftCorner.y;
|
||||
final float x3 = mInnerTopRightCorner.x;
|
||||
final float x3 = mInnerTopRightCorner.x + mGapBetweenPaths;
|
||||
final float y3 = mInnerTopRightCorner.y;
|
||||
final float x4 = right;
|
||||
final float x4 = right + mGapBetweenPaths;
|
||||
final float y4 = top;
|
||||
|
||||
drawQuadrilateral(canvas, colorTop, x1, y1, x2, y2, x3, y3, x4, y4);
|
||||
@@ -444,25 +458,25 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
|
||||
if (borderWidth.right > 0) {
|
||||
final float x1 = right;
|
||||
final float y1 = top;
|
||||
final float y1 = top - mGapBetweenPaths;
|
||||
final float x2 = mInnerTopRightCorner.x;
|
||||
final float y2 = mInnerTopRightCorner.y;
|
||||
final float y2 = mInnerTopRightCorner.y - mGapBetweenPaths;
|
||||
final float x3 = mInnerBottomRightCorner.x;
|
||||
final float y3 = mInnerBottomRightCorner.y;
|
||||
final float y3 = mInnerBottomRightCorner.y + mGapBetweenPaths;
|
||||
final float x4 = right;
|
||||
final float y4 = bottom;
|
||||
final float y4 = bottom + mGapBetweenPaths;
|
||||
|
||||
drawQuadrilateral(canvas, colorRight, x1, y1, x2, y2, x3, y3, x4, y4);
|
||||
}
|
||||
|
||||
if (borderWidth.bottom > 0) {
|
||||
final float x1 = left;
|
||||
final float x1 = left - mGapBetweenPaths;
|
||||
final float y1 = bottom;
|
||||
final float x2 = mInnerBottomLeftCorner.x;
|
||||
final float x2 = mInnerBottomLeftCorner.x - mGapBetweenPaths;
|
||||
final float y2 = mInnerBottomLeftCorner.y;
|
||||
final float x3 = mInnerBottomRightCorner.x;
|
||||
final float x3 = mInnerBottomRightCorner.x + mGapBetweenPaths;
|
||||
final float y3 = mInnerBottomRightCorner.y;
|
||||
final float x4 = right;
|
||||
final float x4 = right + mGapBetweenPaths;
|
||||
final float y4 = bottom;
|
||||
|
||||
drawQuadrilateral(canvas, colorBottom, x1, y1, x2, y2, x3, y3, x4, y4);
|
||||
@@ -484,6 +498,10 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
mInnerClipPathForBorderRadius = new Path();
|
||||
}
|
||||
|
||||
if (mBackgroundColorRenderPath == null) {
|
||||
mBackgroundColorRenderPath = new Path();
|
||||
}
|
||||
|
||||
if (mOuterClipPathForBorderRadius == null) {
|
||||
mOuterClipPathForBorderRadius = new Path();
|
||||
}
|
||||
@@ -513,6 +531,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
}
|
||||
|
||||
mInnerClipPathForBorderRadius.reset();
|
||||
mBackgroundColorRenderPath.reset();
|
||||
mOuterClipPathForBorderRadius.reset();
|
||||
mPathForBorderRadiusOutline.reset();
|
||||
mCenterDrawPath.reset();
|
||||
@@ -634,6 +653,27 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
},
|
||||
Path.Direction.CW);
|
||||
|
||||
// There is a small gap between mBackgroundColorRenderPath and its
|
||||
// border. mGapBetweenPaths is used to slightly enlarge the rectangle
|
||||
// (mInnerClipTempRectForBorderRadius), ensuring the border can be
|
||||
// drawn on top without the gap.
|
||||
mBackgroundColorRenderPath.addRoundRect(
|
||||
mInnerClipTempRectForBorderRadius.left - mGapBetweenPaths,
|
||||
mInnerClipTempRectForBorderRadius.top - mGapBetweenPaths,
|
||||
mInnerClipTempRectForBorderRadius.right + mGapBetweenPaths,
|
||||
mInnerClipTempRectForBorderRadius.bottom + mGapBetweenPaths,
|
||||
new float[] {
|
||||
innerTopLeftRadiusX,
|
||||
innerTopLeftRadiusY,
|
||||
innerTopRightRadiusX,
|
||||
innerTopRightRadiusY,
|
||||
innerBottomRightRadiusX,
|
||||
innerBottomRightRadiusY,
|
||||
innerBottomLeftRadiusX,
|
||||
innerBottomLeftRadiusY,
|
||||
},
|
||||
Path.Direction.CW);
|
||||
|
||||
mOuterClipPathForBorderRadius.addRoundRect(
|
||||
mOuterClipTempRectForBorderRadius,
|
||||
new float[] {
|
||||
|
||||
Reference in New Issue
Block a user