Skip to content

Commit 456cf3d

Browse files
smarkifacebook-github-bot
authored andcommitted
Fix ReactSwitch for non RippleDrawable backgrounds (#32468)
Summary: ReactSwitch component is crashing on Android when it is initialised with both a backgroundColor and thumbColor, `style={{ backgroundColor: "anyColor" }} thumbColor="anyColor"`, due to IllegalCastException. When setting a background color, BaseViewManagerDelegate is calling `setBackgroundColor` which replaces the background drawable with a ColorDrawale, hence [this line](https://github.com/facebook/react-native/blob/72ea0e111fccd99456abf3f974439432145585e3/ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitch.java#L68) fails. Instead, given the ripple effect needs to be preserved, one should initialise a RippleDrawable using the current background drawable and set it as the background of the switch. Given the RippleDrawable should be preserved, overriding the `setBackgroundColor` seemed the sensible thing to do. ## Changelog [Android] [Fixed] - Fix crash when a Switch is initialised with both backgroundColor and thumbColor. Pull Request resolved: #32468 Test Plan: ### Setup: Initialise an empty React Native project. Add a switch component: `<Switch style={{backgroundColor: 'red'}} thumbColor={'#356'} />` Run the project `yarn android` ### Current state (RN 65+): Red screen will show highlighting an IllegalCastException. <img src="https://user-images.githubusercontent.com/4354327/138616661-3ba1370c-6a2b-48c2-ba70-b99415a4256f.png" width="200"/> ### With fix: - The component is expected to have a red background. - When pressed a ripple effect shows inside the backgrounds bounding box. - Business as usual otherwise. `backgroundColor` with `thumbColor`: ![backgroundColor + thumbColor](https://user-images.githubusercontent.com/4354327/138615603-141660d2-a5cd-49d7-aa5e-9c93ebc6d680.gif) Just `thumbColor`: ![Screen Recording 2021-10-25 at 00 23 57](https://user-images.githubusercontent.com/4354327/138615658-baa380dd-2cbb-4d0f-a25e-a003ef67c977.gif) Reviewed By: ShikaSD Differential Revision: D31895690 Pulled By: cortinico fbshipit-source-id: 60af16de7db61440ccfbf11d67a3d945dd90b562
1 parent f58c496 commit 456cf3d

File tree

2 files changed

+45
-8
lines changed

2 files changed

+45
-8
lines changed

ReactAndroid/src/main/java/com/facebook/react/views/switchview/ReactSwitch.java

+22-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import android.content.Context;
1111
import android.content.res.ColorStateList;
1212
import android.graphics.PorterDuff;
13+
import android.graphics.drawable.ColorDrawable;
1314
import android.graphics.drawable.Drawable;
1415
import android.graphics.drawable.RippleDrawable;
1516
import android.os.Build;
@@ -48,6 +49,18 @@ public void setChecked(boolean checked) {
4849
}
4950
}
5051

52+
@Override
53+
public void setBackgroundColor(int color) {
54+
// Ensure RippleDrawable is preserved for >=21
55+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
56+
setBackground(
57+
new RippleDrawable(
58+
createRippleDrawableColorStateList(color), new ColorDrawable(color), null));
59+
} else {
60+
super.setBackgroundColor(color);
61+
}
62+
}
63+
5164
void setColor(Drawable drawable, @Nullable Integer color) {
5265
if (color == null) {
5366
drawable.clearColorFilter();
@@ -63,14 +76,10 @@ public void setTrackColor(@Nullable Integer color) {
6376
public void setThumbColor(@Nullable Integer color) {
6477
setColor(super.getThumbDrawable(), color);
6578

66-
// Set the ripple color with thumb color if >= LOLLIPOP
67-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
68-
RippleDrawable ripple = (RippleDrawable) super.getBackground();
69-
ColorStateList customColorState =
70-
new ColorStateList(
71-
new int[][] {new int[] {android.R.attr.state_pressed}}, new int[] {color});
72-
73-
ripple.setColor(customColorState);
79+
// Set the ripple color if background is instance of RippleDrawable
80+
if (color != null && super.getBackground() instanceof RippleDrawable) {
81+
ColorStateList customColorState = createRippleDrawableColorStateList(color);
82+
((RippleDrawable) super.getBackground()).setColor(customColorState);
7483
}
7584
}
7685

@@ -113,4 +122,9 @@ private void setTrackColor(boolean checked) {
113122
setTrackColor(currentTrackColor);
114123
}
115124
}
125+
126+
private ColorStateList createRippleDrawableColorStateList(@Nullable Integer color) {
127+
return new ColorStateList(
128+
new int[][] {new int[] {android.R.attr.state_pressed}}, new int[] {color});
129+
}
116130
}

packages/rn-tester/js/examples/Switch/SwitchExample.js

+23
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,23 @@ class OnChangeExample extends React.Component<{...}, $FlowFixMeState> {
244244
}
245245
}
246246

247+
class ContainerBackgroundColorStyleExample extends React.Component<
248+
{...},
249+
$FlowFixMeState,
250+
> {
251+
render() {
252+
return (
253+
<View>
254+
<Switch
255+
style={{backgroundColor: 'blue'}}
256+
thumbColor="white"
257+
value={true}
258+
/>
259+
</View>
260+
);
261+
}
262+
}
263+
247264
exports.title = 'Switch';
248265
exports.documentationURL = 'https://reactnative.dev/docs/switch';
249266
exports.category = 'UI';
@@ -291,6 +308,12 @@ exports.examples = [
291308
return <OnChangeExample />;
292309
},
293310
},
311+
{
312+
title: "The container's background color can be set",
313+
render(): React.Element<any> {
314+
return <ContainerBackgroundColorStyleExample />;
315+
},
316+
},
294317
];
295318

296319
if (Platform.OS === 'ios') {

0 commit comments

Comments
 (0)