From 4937a4c5cd2494a875019bfeb691026b0abfe58e Mon Sep 17 00:00:00 2001 From: mattds Date: Mon, 8 Feb 2016 10:42:31 -0800 Subject: [PATCH] Added support for corner radii in Android Summary: This is a cut down version of a previous pull request with just the 4 corners catered for. Closes https://github.com/facebook/react-native/pull/4252 Reviewed By: svcscm Differential Revision: D2911959 Pulled By: androidtrunkagent fb-gh-sync-id: 7ddcd684d90d4d92ccefed906c0126e92818dcde --- Examples/UIExplorer/BorderExample.js | 29 +++++++ .../facebook/react/uimanager/ViewProps.java | 5 ++ .../view/ReactViewBackgroundDrawable.java | 78 ++++++++++++++++--- .../react/views/view/ReactViewGroup.java | 4 + .../react/views/view/ReactViewManager.java | 20 ++++- 5 files changed, 124 insertions(+), 12 deletions(-) diff --git a/Examples/UIExplorer/BorderExample.js b/Examples/UIExplorer/BorderExample.js index c6c22f5531f038..1c547ea6ea21c9 100644 --- a/Examples/UIExplorer/BorderExample.js +++ b/Examples/UIExplorer/BorderExample.js @@ -98,6 +98,20 @@ var styles = StyleSheet.create({ marginRight: 10, backgroundColor: 'lightgrey', }, + border9: { + borderWidth: 10, + borderTopLeftRadius: 10, + borderBottomRightRadius: 20, + borderColor: 'black', + }, + border10: { + borderWidth: 10, + backgroundColor: 'white', + borderTopLeftRadius: 10, + borderBottomRightRadius: 20, + borderColor: 'black', + elevation: 10 + } }); exports.title = 'Border'; @@ -180,4 +194,19 @@ exports.examples = [ ); } }, + { + title: 'Corner Radii', + description: 'borderTopLeftRadius & borderBottomRightRadius', + render() { + return ; + } + }, + { + title: 'Corner Radii / Elevation', + description: 'borderTopLeftRadius & borderBottomRightRadius & elevation', + platform: 'android', + render() { + return ; + } + }, ]; diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java index 33e52fe95b2159..3cef8895ac77c9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java @@ -77,6 +77,11 @@ public class ViewProps { public static final String BORDER_TOP_WIDTH = "borderTopWidth"; public static final String BORDER_RIGHT_WIDTH = "borderRightWidth"; public static final String BORDER_BOTTOM_WIDTH = "borderBottomWidth"; + public static final String BORDER_RADIUS = "borderRadius"; + public static final String BORDER_TOP_LEFT_RADIUS = "borderTopLeftRadius"; + public static final String BORDER_TOP_RIGHT_RADIUS = "borderTopRightRadius"; + public static final String BORDER_BOTTOM_LEFT_RADIUS = "borderBottomLeftRadius"; + public static final String BORDER_BOTTOM_RIGHT_RADIUS = "borderBottomRightRadius"; public static final int[] BORDER_SPACING_TYPES = { Spacing.ALL, Spacing.LEFT, Spacing.RIGHT, Spacing.TOP, Spacing.BOTTOM }; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java index f36f70db7f46ef..742adbd84b1ba9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java @@ -11,6 +11,7 @@ import javax.annotation.Nullable; +import java.util.Arrays; import java.util.Locale; import android.graphics.Canvas; @@ -78,7 +79,9 @@ private static enum BorderStyle { /* Used for rounded border and rounded background */ private @Nullable PathEffect mPathEffectForBorderStyle; private @Nullable Path mPathForBorderRadius; + private @Nullable Path mPathForBorderRadiusOutline; private @Nullable RectF mTempRectForBorderRadius; + private @Nullable RectF mTempRectForBorderRadiusOutline; private boolean mNeedUpdatePathForBorderRadius = false; private float mBorderRadius = CSSConstants.UNDEFINED; @@ -87,9 +90,11 @@ private static enum BorderStyle { private int mColor = Color.TRANSPARENT; private int mAlpha = 255; + private @Nullable float[] mBorderCornerRadii; + @Override public void draw(Canvas canvas) { - if (!CSSConstants.isUndefined(mBorderRadius) && mBorderRadius > 0) { + if ((!CSSConstants.isUndefined(mBorderRadius) && mBorderRadius > 0) || mBorderCornerRadii != null) { drawRoundedBackgroundWithBorders(canvas); } else { drawRectangularBackgroundWithBorders(canvas); @@ -132,11 +137,10 @@ public void getOutline(Outline outline) { super.getOutline(outline); return; } - if(!CSSConstants.isUndefined(mBorderRadius) && mBorderRadius > 0) { - float extraRadiusFromBorderWidth = (mBorderWidth != null) - ? mBorderWidth.get(Spacing.ALL) / 2f - : 0; - outline.setRoundRect(getBounds(), mBorderRadius + extraRadiusFromBorderWidth); + if((!CSSConstants.isUndefined(mBorderRadius) && mBorderRadius > 0) || mBorderCornerRadii != null) { + updatePath(); + + outline.setConvexPath(mPathForBorderRadiusOutline); } else { outline.setRect(getBounds()); } @@ -181,8 +185,22 @@ public void setBorderStyle(@Nullable String style) { } public void setRadius(float radius) { - if (mBorderRadius != radius) { + if (!FloatUtil.floatsEqual(mBorderRadius,radius)) { mBorderRadius = radius; + mNeedUpdatePathForBorderRadius = true; + invalidateSelf(); + } + } + + public void setRadius(float radius, int position) { + if (mBorderCornerRadii == null) { + mBorderCornerRadii = new float[4]; + Arrays.fill(mBorderCornerRadii, CSSConstants.UNDEFINED); + } + + if (!FloatUtil.floatsEqual(mBorderCornerRadii[position], radius)) { + mBorderCornerRadii[position] = radius; + mNeedUpdatePathForBorderRadius = true; invalidateSelf(); } } @@ -225,19 +243,61 @@ private void updatePath() { if (mPathForBorderRadius == null) { mPathForBorderRadius = new Path(); mTempRectForBorderRadius = new RectF(); + mPathForBorderRadiusOutline = new Path(); + mTempRectForBorderRadiusOutline = new RectF(); } + mPathForBorderRadius.reset(); + mPathForBorderRadiusOutline.reset(); + mTempRectForBorderRadius.set(getBounds()); + mTempRectForBorderRadiusOutline.set(getBounds()); float fullBorderWidth = getFullBorderWidth(); if (fullBorderWidth > 0) { mTempRectForBorderRadius.inset(fullBorderWidth * 0.5f, fullBorderWidth * 0.5f); } + + float defaultBorderRadius = !CSSConstants.isUndefined(mBorderRadius) ? mBorderRadius : 0; + float topLeftRadius = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[0]) ? mBorderCornerRadii[0] : defaultBorderRadius; + float topRightRadius = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[1]) ? mBorderCornerRadii[1] : defaultBorderRadius; + float bottomRightRadius = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[2]) ? mBorderCornerRadii[2] : defaultBorderRadius; + float bottomLeftRadius = mBorderCornerRadii != null && !CSSConstants.isUndefined(mBorderCornerRadii[3]) ? mBorderCornerRadii[3] : defaultBorderRadius; + + mPathForBorderRadius.addRoundRect( mTempRectForBorderRadius, - mBorderRadius, - mBorderRadius, + new float[] { + topLeftRadius, + topLeftRadius, + topRightRadius, + topRightRadius, + bottomRightRadius, + bottomRightRadius, + bottomLeftRadius, + bottomLeftRadius + }, Path.Direction.CW); + float extraRadiusForOutline = 0; + + if (mBorderWidth != null) { + extraRadiusForOutline = mBorderWidth.get(Spacing.ALL) / 2f; + } + + mPathForBorderRadiusOutline.addRoundRect( + mTempRectForBorderRadiusOutline, + new float[] { + topLeftRadius + extraRadiusForOutline, + topLeftRadius + extraRadiusForOutline, + topRightRadius + extraRadiusForOutline, + topRightRadius + extraRadiusForOutline, + bottomRightRadius + extraRadiusForOutline, + bottomRightRadius + extraRadiusForOutline, + bottomLeftRadius + extraRadiusForOutline, + bottomLeftRadius + extraRadiusForOutline + }, + Path.Direction.CW); + mPathEffectForBorderStyle = mBorderStyle != null ? mBorderStyle.getPathEffect(getFullBorderWidth()) : null; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java index 88ac93397859a8..bf57e47a65543d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java @@ -207,6 +207,10 @@ public void setBorderRadius(float borderRadius) { getOrCreateReactViewBackground().setRadius(borderRadius); } + public void setBorderRadius(float borderRadius, int position) { + getOrCreateReactViewBackground().setRadius(borderRadius, position); + } + public void setBorderStyle(@Nullable String style) { getOrCreateReactViewBackground().setBorderStyle(style); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java index 5e761b0f7a2e58..789b294e1f9425 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java @@ -51,9 +51,23 @@ public void setAccessible(ReactViewGroup view, boolean accessible) { view.setFocusable(accessible); } - @ReactProp(name = "borderRadius") - public void setBorderRadius(ReactViewGroup view, float borderRadius) { - view.setBorderRadius(PixelUtil.toPixelFromDIP(borderRadius)); + @ReactPropGroup(names = { + ViewProps.BORDER_RADIUS, + ViewProps.BORDER_TOP_LEFT_RADIUS, + ViewProps.BORDER_TOP_RIGHT_RADIUS, + ViewProps.BORDER_BOTTOM_RIGHT_RADIUS, + ViewProps.BORDER_BOTTOM_LEFT_RADIUS + }, defaultFloat = CSSConstants.UNDEFINED) + public void setBorderRadius(ReactViewGroup view, int index, float borderRadius) { + if (!CSSConstants.isUndefined(borderRadius)) { + borderRadius = PixelUtil.toPixelFromDIP(borderRadius); + } + + if (index == 0) { + view.setBorderRadius(borderRadius); + } else { + view.setBorderRadius(borderRadius, index - 1); + } } @ReactProp(name = "borderStyle")