Skip to content

Commit

Permalink
Improve RectButton border styles on Android (#2798)
Browse files Browse the repository at this point in the history
## Description

This PR enables the use of `borderWidth`, `borderColor` and
`borderStyle` to style the `RectButton` component on Android.
It's a continuation of #2792 

Fixes #477 

## Test plan

Some examples were added to the `RectButtonBorders` screen in the
example app using the border styles changed in this PR.
  • Loading branch information
camilossantos2809 authored Mar 11, 2024
1 parent 1f723ec commit d0cea67
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ public void setProperty(T view, String propName, @Nullable Object value) {
case "touchSoundDisabled":
mViewManager.setTouchSoundDisabled(view, value == null ? false : (boolean) value);
break;
case "borderWidth":
mViewManager.setBorderWidth(view, value == null ? 0f : ((Double) value).floatValue());
break;
case "borderColor":
mViewManager.setBorderColor(view, ColorPropConverter.getColor(value, view.getContext()));
break;
case "borderStyle":
mViewManager.setBorderStyle(view, value == null ? "solid" : (String) value);
break;
default:
super.setProperty(view, propName, value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ public interface RNGestureHandlerButtonManagerInterface<T extends View> {
void setRippleColor(T view, @Nullable Integer value);
void setRippleRadius(T view, int value);
void setTouchSoundDisabled(T view, boolean value);
void setBorderWidth(T view, float value);
void setBorderColor(T view, @Nullable Integer value);
void setBorderStyle(T view, @Nullable String value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import android.annotation.TargetApi
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.DashPathEffect
import android.graphics.Paint
import android.graphics.PathEffect
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.PaintDrawable
Expand Down Expand Up @@ -85,6 +88,21 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
view.borderBottomRightRadius = borderBottomRightRadius
}

@ReactProp(name = "borderWidth")
override fun setBorderWidth(view: ButtonViewGroup, borderWidth: Float) {
view.borderWidth = borderWidth
}

@ReactProp(name = "borderColor")
override fun setBorderColor(view: ButtonViewGroup, borderColor: Int?) {
view.borderColor = borderColor
}

@ReactProp(name = "borderStyle")
override fun setBorderStyle(view: ButtonViewGroup, borderStyle: String?) {
view.borderStyle = borderStyle
}

@ReactProp(name = "rippleColor")
override fun setRippleColor(view: ButtonViewGroup, rippleColor: Int?) {
view.rippleColor = rippleColor
Expand Down Expand Up @@ -151,6 +169,18 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
set(radius) = withBackgroundUpdate {
field = radius * resources.displayMetrics.density
}
var borderWidth = 0f
set(width) = withBackgroundUpdate {
field = width * resources.displayMetrics.density
}
var borderColor: Int? = null
set(color) = withBackgroundUpdate {
field = color
}
var borderStyle: String? = "solid"
set(style) = withBackgroundUpdate {
field = style
}

private val hasBorderRadii: Boolean
get() = borderRadius != 0f ||
Expand Down Expand Up @@ -199,6 +229,14 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R
.toFloatArray()
}

private fun buildBorderStyle(): PathEffect? {
return when (borderStyle) {
"dotted" -> DashPathEffect(floatArrayOf(borderWidth, borderWidth, borderWidth, borderWidth), 0f)
"dashed" -> DashPathEffect(floatArrayOf(borderWidth * 3, borderWidth * 3, borderWidth * 3, borderWidth * 3), 0f)
else -> null
}
}

override fun setBackgroundColor(color: Int) = withBackgroundUpdate {
_backgroundColor = color
}
Expand Down Expand Up @@ -250,12 +288,23 @@ class RNGestureHandlerButtonViewManager : ViewGroupManager<ButtonViewGroup>(), R

private fun updateBackgroundColor(backgroundColor: Int, selectable: Drawable?) {
val colorDrawable = PaintDrawable(backgroundColor)
val borderDrawable = PaintDrawable(Color.TRANSPARENT)

if (hasBorderRadii) {
colorDrawable.setCornerRadii(buildBorderRadii())
borderDrawable.setCornerRadii(buildBorderRadii())
}

if (borderWidth > 0f) {
borderDrawable.paint.apply {
style = Paint.Style.STROKE
strokeWidth = borderWidth
color = borderColor ?: Color.BLACK
pathEffect = buildBorderStyle()
}
}

val layerDrawable = LayerDrawable(if (selectable != null) arrayOf(colorDrawable, selectable) else arrayOf(colorDrawable))
val layerDrawable = LayerDrawable(if (selectable != null) arrayOf(colorDrawable, selectable, borderDrawable) else arrayOf(colorDrawable, borderDrawable))
background = layerDrawable
}

Expand Down
122 changes: 65 additions & 57 deletions example/src/release_tests/rectButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,77 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-native';
import { RectButton } from 'react-native-gesture-handler';

export default function RectButtonBorders() {
return (
<View style={styles.container}>
<Button type="borderRadius" />
<Button type="borderTopLeftRadius" />
<Button type="borderTopRightRadius" />
<Button type="borderBottomLeftRadius" />
<Button type="borderBottomRightRadius" />
<RectButton
style={[
styles.button,
{ borderTopLeftRadius: 16, borderTopRightRadius: 16 },
]}
onPress={() => alert(`Pressed borderTopRadius!`)}>
<Text>border Top Radius</Text>
</RectButton>
<RectButton
style={[
styles.button,
{ borderBottomLeftRadius: 16, borderBottomRightRadius: 16 },
]}
onPress={() => alert(`Pressed borderTopRadius!`)}>
<Text>border Bottom Radius</Text>
</RectButton>
<RectButton
style={[
styles.button,
{
borderRadius: 8,
borderTopLeftRadius: 32,
borderTopRightRadius: 32,
},
]}
onPress={() => alert(`Pressed borderTopRadius!`)}>
<Text>borderRadius and Top Radius</Text>
</RectButton>
<RectButton
style={[
styles.button,
{
borderRadius: 8,
borderBottomLeftRadius: 32,
borderBottomRightRadius: 32,
},
]}
onPress={() => alert(`Pressed borderTopRadius!`)}>
<Text>borderRadius and Bottom Radius</Text>
</RectButton>
<Button text="Border Radius" style={{ borderRadius: 16 }} />
<Button
text="Border Top Left Radius"
style={{ borderTopLeftRadius: 16 }}
/>
<Button
text="Border Top Right Radius"
style={{ borderTopRightRadius: 16 }}
/>
<Button
text="Border Bottom Left Radius"
style={{ borderBottomLeftRadius: 16 }}
/>
<Button
text="Border Bottom Right Radius"
style={{ borderBottomRightRadius: 16 }}
/>
<Button
text="Border Top Radius"
style={{ borderTopLeftRadius: 16, borderTopRightRadius: 16 }}
/>
<Button
text="Border Bottom Radius"
style={{ borderBottomLeftRadius: 16, borderBottomRightRadius: 16 }}
/>
<Button
text="Border Radius and Top Radius"
style={{
borderRadius: 8,
borderTopLeftRadius: 32,
borderTopRightRadius: 32,
}}
/>
<Button
text="Border Radius and Bottom Radius"
style={{
borderRadius: 8,
borderBottomLeftRadius: 32,
borderBottomRightRadius: 32,
}}
/>
<Button
text="Border Dashed"
style={{ borderStyle: 'dashed', borderWidth: 2, borderColor: 'red' }}
/>
<Button
text="Border Dotted"
style={{ borderStyle: 'dotted', borderWidth: 2, borderColor: 'red' }}
/>
<Button
text="Border Solid"
style={{ borderStyle: 'solid', borderWidth: 2, borderColor: 'red' }}
/>
</View>
);
}

type BorderTypes =
| 'borderRadius'
| 'borderTopLeftRadius'
| 'borderTopRightRadius'
| 'borderBottomLeftRadius'
| 'borderBottomRightRadius';
type ButtonProps = { style: StyleProp<ViewStyle>; text: string };

function Button({ style, text }: ButtonProps) {
const rectButtonStyles: StyleProp<ViewStyle> = [styles.button, style];

const onPress = () => alert(`Pressed ${text}!`);

function Button({ type }: { type: BorderTypes }) {
return (
<RectButton
style={[styles.button, { [type]: 16 }]}
onPress={() => alert(`Pressed ${type}!`)}>
<Text style={styles.text}>{type}</Text>
<RectButton style={rectButtonStyles} onPress={onPress}>
<Text style={styles.text}>{text}</Text>
</RectButton>
);
}
Expand All @@ -87,8 +92,11 @@ const styles = StyleSheet.create({
justifyContent: 'center',
alignItems: 'center',
padding: 8,
borderWidth: 1,
borderColor: '#001a72',
},
text: {
color: '#f8f9ff',
textAlign: 'center',
},
});
4 changes: 4 additions & 0 deletions src/specs/RNGestureHandlerButtonNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati
import type {
Int32,
WithDefault,
Float,
} from 'react-native/Libraries/Types/CodegenTypes';
import type { ViewProps, ColorValue } from 'react-native';

Expand All @@ -13,6 +14,9 @@ interface NativeProps extends ViewProps {
rippleColor?: ColorValue;
rippleRadius?: Int32;
touchSoundDisabled?: WithDefault<boolean, false>;
borderWidth?: Float;
borderColor?: ColorValue;
borderStyle?: WithDefault<string, 'solid'>;
}

export default codegenNativeComponent<NativeProps>('RNGestureHandlerButton');

0 comments on commit d0cea67

Please sign in to comment.