Android: Implement border(Start|End)Width for non-rounded borders

Reviewed By: achen1

Differential Revision: D5917755

fbshipit-source-id: 3ec0ab1a1e191f5f6fd956691995e95937a513cc
This commit is contained in:
Ramanpreet Nara 2017-10-18 19:29:55 -07:00 committed by Facebook Github Bot
parent 04a8c62313
commit 7ed7593b2b
7 changed files with 110 additions and 15 deletions

View File

@ -624,18 +624,24 @@ public class LayoutShadowNode extends ReactShadowNodeImpl {
padding.recycle(); padding.recycle();
} }
@ReactPropGroup(names = { @ReactPropGroup(
names = {
ViewProps.BORDER_WIDTH, ViewProps.BORDER_WIDTH,
ViewProps.BORDER_LEFT_WIDTH, ViewProps.BORDER_START_WIDTH,
ViewProps.BORDER_RIGHT_WIDTH, ViewProps.BORDER_END_WIDTH,
ViewProps.BORDER_TOP_WIDTH, ViewProps.BORDER_TOP_WIDTH,
ViewProps.BORDER_BOTTOM_WIDTH, ViewProps.BORDER_BOTTOM_WIDTH,
}, defaultFloat = YogaConstants.UNDEFINED) ViewProps.BORDER_LEFT_WIDTH,
ViewProps.BORDER_RIGHT_WIDTH,
},
defaultFloat = YogaConstants.UNDEFINED
)
public void setBorderWidths(int index, float borderWidth) { public void setBorderWidths(int index, float borderWidth) {
if (isVirtual()) { if (isVirtual()) {
return; return;
} }
setBorder(ViewProps.BORDER_SPACING_TYPES[index], PixelUtil.toPixelFromDIP(borderWidth)); int spacingType = maybeTransformLeftRightToStartEnd(ViewProps.BORDER_SPACING_TYPES[index]);
setBorder(spacingType, PixelUtil.toPixelFromDIP(borderWidth));
} }
@ReactPropGroup( @ReactPropGroup(

View File

@ -103,6 +103,8 @@ public class ViewProps {
public static final String BORDER_WIDTH = "borderWidth"; public static final String BORDER_WIDTH = "borderWidth";
public static final String BORDER_LEFT_WIDTH = "borderLeftWidth"; public static final String BORDER_LEFT_WIDTH = "borderLeftWidth";
public static final String BORDER_START_WIDTH = "borderStartWidth";
public static final String BORDER_END_WIDTH = "borderEndWidth";
public static final String BORDER_TOP_WIDTH = "borderTopWidth"; public static final String BORDER_TOP_WIDTH = "borderTopWidth";
public static final String BORDER_RIGHT_WIDTH = "borderRightWidth"; public static final String BORDER_RIGHT_WIDTH = "borderRightWidth";
public static final String BORDER_BOTTOM_WIDTH = "borderBottomWidth"; public static final String BORDER_BOTTOM_WIDTH = "borderBottomWidth";
@ -117,7 +119,13 @@ public class ViewProps {
public static final String BORDER_TOP_COLOR = "borderTopColor"; public static final String BORDER_TOP_COLOR = "borderTopColor";
public static final String BORDER_BOTTOM_COLOR = "borderBottomColor"; public static final String BORDER_BOTTOM_COLOR = "borderBottomColor";
public static final int[] BORDER_SPACING_TYPES = { public static final int[] BORDER_SPACING_TYPES = {
Spacing.ALL, Spacing.START, Spacing.END, Spacing.TOP, Spacing.BOTTOM Spacing.ALL,
Spacing.START,
Spacing.END,
Spacing.TOP,
Spacing.BOTTOM,
Spacing.LEFT,
Spacing.RIGHT
}; };
public static final int[] PADDING_MARGIN_SPACING_TYPES = { public static final int[] PADDING_MARGIN_SPACING_TYPES = {
Spacing.ALL, Spacing.ALL,

View File

@ -16,6 +16,7 @@ android_library(
react_native_target("java/com/facebook/react/touch:touch"), react_native_target("java/com/facebook/react/touch:touch"),
react_native_target("java/com/facebook/react/views/common:common"), react_native_target("java/com/facebook/react/views/common:common"),
react_native_target("java/com/facebook/react/uimanager:uimanager"), react_native_target("java/com/facebook/react/uimanager:uimanager"),
react_native_target("java/com/facebook/react/modules/i18nmanager:i18nmanager"),
react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), react_native_target("java/com/facebook/react/uimanager/annotations:annotations"),
], ],
) )

View File

@ -9,6 +9,7 @@
package com.facebook.react.views.view; package com.facebook.react.views.view;
import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.ColorFilter; import android.graphics.ColorFilter;
@ -23,7 +24,9 @@ import android.graphics.RectF;
import android.graphics.Region; import android.graphics.Region;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.view.View;
import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.modules.i18nmanager.I18nUtil;
import com.facebook.react.uimanager.FloatUtil; import com.facebook.react.uimanager.FloatUtil;
import com.facebook.react.uimanager.Spacing; import com.facebook.react.uimanager.Spacing;
import com.facebook.yoga.YogaConstants; import com.facebook.yoga.YogaConstants;
@ -105,6 +108,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
private int mAlpha = 255; private int mAlpha = 255;
private @Nullable float[] mBorderCornerRadii; private @Nullable float[] mBorderCornerRadii;
private final Context mContext;
public enum BorderRadiusLocation { public enum BorderRadiusLocation {
TOP_LEFT, TOP_LEFT,
@ -113,6 +117,10 @@ public class ReactViewBackgroundDrawable extends Drawable {
BOTTOM_LEFT BOTTOM_LEFT
} }
public ReactViewBackgroundDrawable(Context context) {
mContext = context;
}
@Override @Override
public void draw(Canvas canvas) { public void draw(Canvas canvas) {
updatePathEffect(); updatePathEffect();
@ -810,9 +818,14 @@ public class ReactViewBackgroundDrawable extends Drawable {
mPaint.setStyle(Paint.Style.FILL); mPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(getBounds(), mPaint); canvas.drawRect(getBounds(), mPaint);
} }
// maybe draw borders? // maybe draw borders?
if (getBorderWidth(Spacing.LEFT) > 0 || getBorderWidth(Spacing.TOP) > 0 || if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
getBorderWidth(Spacing.RIGHT) > 0 || getBorderWidth(Spacing.BOTTOM) > 0) { && (getBorderWidth(Spacing.START) > 0 || getBorderWidth(Spacing.END) > 0))
|| getBorderWidth(Spacing.LEFT) > 0
|| getBorderWidth(Spacing.TOP) > 0
|| getBorderWidth(Spacing.RIGHT) > 0
|| getBorderWidth(Spacing.BOTTOM) > 0) {
Rect bounds = getBounds(); Rect bounds = getBounds();
int borderLeft = getBorderWidth(Spacing.LEFT); int borderLeft = getBorderWidth(Spacing.LEFT);
@ -824,6 +837,39 @@ public class ReactViewBackgroundDrawable extends Drawable {
int colorRight = getBorderColor(Spacing.RIGHT); int colorRight = getBorderColor(Spacing.RIGHT);
int colorBottom = getBorderColor(Spacing.BOTTOM); int colorBottom = getBorderColor(Spacing.BOTTOM);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
final boolean isRTL = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
int borderStart = getBorderWidth(Spacing.START);
int borderEnd = getBorderWidth(Spacing.END);
if (I18nUtil.getInstance().doesRTLFlipLeftAndRightStyles(mContext)) {
if (borderStart < 0) {
borderStart = borderLeft;
}
if (borderEnd < 0) {
borderEnd = borderRight;
}
final int directionAwareBorderLeft = isRTL ? borderEnd : borderStart;
final int directionAwareBorderRight = isRTL ? borderStart : borderEnd;
borderLeft = directionAwareBorderLeft;
borderRight = directionAwareBorderRight;
} else {
final int directionAwareBorderLeft = isRTL ? borderEnd : borderStart;
final int directionAwareBorderRight = isRTL ? borderStart : borderEnd;
if (directionAwareBorderLeft >= 0) {
borderLeft = directionAwareBorderLeft;
}
if (directionAwareBorderRight >= 0) {
borderRight = directionAwareBorderRight;
}
}
}
int left = bounds.left; int left = bounds.left;
int top = bounds.top; int top = bounds.top;
@ -961,7 +1007,12 @@ public class ReactViewBackgroundDrawable extends Drawable {
} }
private int getBorderWidth(int position) { private int getBorderWidth(int position) {
return mBorderWidth != null ? Math.round(mBorderWidth.get(position)) : 0; if (mBorderWidth == null) {
return 0;
}
final float width = mBorderWidth.get(position);
return YogaConstants.isUndefined(width) ? -1 : Math.round(width);
} }
private static int colorFromAlphaAndRGBComponents(float alpha, float rgb) { private static int colorFromAlphaAndRGBComponents(float alpha, float rgb) {

View File

@ -21,7 +21,7 @@ public class ReactViewBackgroundManager {
private ReactViewBackgroundDrawable getOrCreateReactViewBackground() { private ReactViewBackgroundDrawable getOrCreateReactViewBackground() {
if (mReactBackgroundDrawable == null) { if (mReactBackgroundDrawable == null) {
mReactBackgroundDrawable = new ReactViewBackgroundDrawable(); mReactBackgroundDrawable = new ReactViewBackgroundDrawable(mView.getContext());
Drawable backgroundDrawable = mView.getBackground(); Drawable backgroundDrawable = mView.getBackground();
ViewHelper.setBackground( ViewHelper.setBackground(
mView, null); // required so that drawable callback is cleared before we add the mView, null); // required so that drawable callback is cleared before we add the

View File

@ -24,6 +24,7 @@ import android.view.ViewGroup;
import android.view.animation.Animation; import android.view.animation.Animation;
import com.facebook.infer.annotation.Assertions; import com.facebook.infer.annotation.Assertions;
import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.modules.i18nmanager.I18nUtil;
import com.facebook.react.touch.OnInterceptTouchEventListener; import com.facebook.react.touch.OnInterceptTouchEventListener;
import com.facebook.react.touch.ReactHitSlopView; import com.facebook.react.touch.ReactHitSlopView;
import com.facebook.react.touch.ReactInterceptingViewGroup; import com.facebook.react.touch.ReactInterceptingViewGroup;
@ -105,10 +106,10 @@ public class ReactViewGroup extends ViewGroup implements
private boolean mNeedsOffscreenAlphaCompositing = false; private boolean mNeedsOffscreenAlphaCompositing = false;
private final ViewGroupDrawingOrderHelper mDrawingOrderHelper; private final ViewGroupDrawingOrderHelper mDrawingOrderHelper;
private @Nullable Path mPath; private @Nullable Path mPath;
private int mLayoutDirection;
public ReactViewGroup(Context context) { public ReactViewGroup(Context context) {
super(context); super(context);
mDrawingOrderHelper = new ViewGroupDrawingOrderHelper(this); mDrawingOrderHelper = new ViewGroupDrawingOrderHelper(this);
} }
@ -126,6 +127,15 @@ public class ReactViewGroup extends ViewGroup implements
// No-op since UIManagerModule handles actually laying out children. // No-op since UIManagerModule handles actually laying out children.
} }
@Override
public void onRtlPropertiesChanged(int layoutDirection) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (mReactBackgroundDrawable != null) {
mReactBackgroundDrawable.setLayoutDirection(mLayoutDirection);
}
}
}
@Override @Override
public void requestLayout() { public void requestLayout() {
// No-op, terminate `requestLayout` here, UIManagerModule handles laying out children and // No-op, terminate `requestLayout` here, UIManagerModule handles laying out children and
@ -565,7 +575,7 @@ public class ReactViewGroup extends ViewGroup implements
private ReactViewBackgroundDrawable getOrCreateReactViewBackground() { private ReactViewBackgroundDrawable getOrCreateReactViewBackground() {
if (mReactBackgroundDrawable == null) { if (mReactBackgroundDrawable == null) {
mReactBackgroundDrawable = new ReactViewBackgroundDrawable(); mReactBackgroundDrawable = new ReactViewBackgroundDrawable(getContext());
Drawable backgroundDrawable = getBackground(); Drawable backgroundDrawable = getBackground();
updateBackgroundDrawable( updateBackgroundDrawable(
null); // required so that drawable callback is cleared before we add the null); // required so that drawable callback is cleared before we add the
@ -577,6 +587,14 @@ public class ReactViewGroup extends ViewGroup implements
new LayerDrawable(new Drawable[] {mReactBackgroundDrawable, backgroundDrawable}); new LayerDrawable(new Drawable[] {mReactBackgroundDrawable, backgroundDrawable});
updateBackgroundDrawable(layerDrawable); updateBackgroundDrawable(layerDrawable);
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mLayoutDirection =
I18nUtil.getInstance().isRTL(getContext())
? LAYOUT_DIRECTION_RTL
: LAYOUT_DIRECTION_LTR;
mReactBackgroundDrawable.setLayoutDirection(mLayoutDirection);
}
} }
return mReactBackgroundDrawable; return mReactBackgroundDrawable;
} }

View File

@ -42,7 +42,13 @@ public class ReactViewManager extends ViewGroupManager<ReactViewGroup> {
public static final String REACT_CLASS = ViewProps.VIEW_CLASS_NAME; public static final String REACT_CLASS = ViewProps.VIEW_CLASS_NAME;
private static final int[] SPACING_TYPES = { private static final int[] SPACING_TYPES = {
Spacing.ALL, Spacing.LEFT, Spacing.RIGHT, Spacing.TOP, Spacing.BOTTOM, Spacing.ALL,
Spacing.LEFT,
Spacing.RIGHT,
Spacing.TOP,
Spacing.BOTTOM,
Spacing.START,
Spacing.END,
}; };
private static final int CMD_HOTSPOT_UPDATE = 1; private static final int CMD_HOTSPOT_UPDATE = 1;
private static final int CMD_SET_PRESSED = 2; private static final int CMD_SET_PRESSED = 2;
@ -127,13 +133,18 @@ public class ReactViewManager extends ViewGroupManager<ReactViewGroup> {
view.setNeedsOffscreenAlphaCompositing(needsOffscreenAlphaCompositing); view.setNeedsOffscreenAlphaCompositing(needsOffscreenAlphaCompositing);
} }
@ReactPropGroup(names = { @ReactPropGroup(
names = {
ViewProps.BORDER_WIDTH, ViewProps.BORDER_WIDTH,
ViewProps.BORDER_LEFT_WIDTH, ViewProps.BORDER_LEFT_WIDTH,
ViewProps.BORDER_RIGHT_WIDTH, ViewProps.BORDER_RIGHT_WIDTH,
ViewProps.BORDER_TOP_WIDTH, ViewProps.BORDER_TOP_WIDTH,
ViewProps.BORDER_BOTTOM_WIDTH, ViewProps.BORDER_BOTTOM_WIDTH,
}, defaultFloat = YogaConstants.UNDEFINED) ViewProps.BORDER_START_WIDTH,
ViewProps.BORDER_END_WIDTH,
},
defaultFloat = YogaConstants.UNDEFINED
)
public void setBorderWidth(ReactViewGroup view, int index, float width) { public void setBorderWidth(ReactViewGroup view, int index, float width) {
if (!YogaConstants.isUndefined(width)) { if (!YogaConstants.isUndefined(width)) {
width = PixelUtil.toPixelFromDIP(width); width = PixelUtil.toPixelFromDIP(width);