diff --git a/packages/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js b/packages/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js
index 55b770d26a3..f5d06e24595 100644
--- a/packages/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js
+++ b/packages/react-native/Libraries/Components/TextInput/AndroidTextInputNativeComponent.js
@@ -485,6 +485,11 @@ export type NativeProps = $ReadOnly<{|
*/
selectionColor?: ?ColorValue,
+ /**
+ * The text selection handle color.
+ */
+ selectionHandleColor?: ?ColorValue,
+
/**
* The start and end of the text input's selection. Set start and end to
* the same value to position the cursor.
@@ -692,6 +697,9 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = {
fontStyle: true,
textShadowOffset: true,
selectionColor: {process: require('../../StyleSheet/processColor').default},
+ selectionHandleColor: {
+ process: require('../../StyleSheet/processColor').default,
+ },
placeholderTextColor: {
process: require('../../StyleSheet/processColor').default,
},
diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts b/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts
index 80db8f0a652..7234cbb3c32 100644
--- a/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts
+++ b/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts
@@ -336,6 +336,14 @@ export interface TextInputAndroidProps {
*/
cursorColor?: ColorValue | null | undefined;
+ /**
+ * When provided it will set the color of the selection handles when highlighting text.
+ * Unlike the behavior of `selectionColor` the handle color will be set independently
+ * from the color of the text selection box.
+ * @platform android
+ */
+ selectionHandleColor?: ColorValue | null | undefined;
+
/**
* Determines whether the individual fields in your app should be included in a
* view structure for autofill purposes on Android API Level 26+. Defaults to auto.
diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js b/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js
index 0eb8f578d65..638acd7c792 100644
--- a/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js
+++ b/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js
@@ -332,6 +332,14 @@ type AndroidProps = $ReadOnly<{|
*/
cursorColor?: ?ColorValue,
+ /**
+ * When provided it will set the color of the selection handles when highlighting text.
+ * Unlike the behavior of `selectionColor` the handle color will be set independently
+ * from the color of the text selection box.
+ * @platform android
+ */
+ selectionHandleColor?: ?ColorValue,
+
/**
* When `false`, if there is a small amount of space available around a text input
* (e.g. landscape orientation on a phone), the OS may choose to have the user edit
diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.js b/packages/react-native/Libraries/Components/TextInput/TextInput.js
index 3e0d8bf7681..0162f2bb007 100644
--- a/packages/react-native/Libraries/Components/TextInput/TextInput.js
+++ b/packages/react-native/Libraries/Components/TextInput/TextInput.js
@@ -917,6 +917,12 @@ export type Props = $ReadOnly<{|
*/
selectionColor?: ?ColorValue,
+ /**
+ * The text selection handle color.
+ * @platform android
+ */
+ selectionHandleColor?: ?ColorValue,
+
/**
* If `true`, all text will automatically be selected on focus.
*/
@@ -1111,6 +1117,9 @@ function InternalTextInput(props: Props): React.Node {
id,
tabIndex,
selection: propsSelection,
+ selectionColor,
+ selectionHandleColor,
+ cursorColor,
...otherProps
} = props;
@@ -1506,7 +1515,15 @@ function InternalTextInput(props: Props): React.Node {
if (childCount > 1) {
children = {children};
}
-
+ // For consistency with iOS set cursor/selectionHandle color as selectionColor
+ const colorProps = {
+ selectionColor,
+ selectionHandleColor:
+ selectionHandleColor === undefined
+ ? selectionColor
+ : selectionHandleColor,
+ cursorColor: cursorColor === undefined ? selectionColor : cursorColor,
+ };
textInput = (
/* $FlowFixMe[prop-missing] the types for AndroidTextInput don't match up
* exactly with the props for TextInput. This will need to get fixed */
@@ -1520,6 +1537,7 @@ function InternalTextInput(props: Props): React.Node {
// $FlowFixMe[incompatible-type] - Figure out imperative + forward refs.
ref={ref}
{...otherProps}
+ {...colorProps}
{...eventHandlers}
accessibilityState={_accessibilityState}
accessibilityLabelledBy={_accessibilityLabelledBy}
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
index 8496a7d059e..0816eeb102e 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java
@@ -168,14 +168,11 @@ public class ReactTextInputManager extends BaseViewManager= Build.VERSION_CODES.Q) {
+ Drawable drawableCenter = view.getTextSelectHandle().mutate();
+ Drawable drawableLeft = view.getTextSelectHandleLeft().mutate();
+ Drawable drawableRight = view.getTextSelectHandleRight().mutate();
+ if (color != null) {
+ BlendModeColorFilter filter = new BlendModeColorFilter(color, BlendMode.SRC_IN);
+ drawableCenter.setColorFilter(filter);
+ drawableLeft.setColorFilter(filter);
+ drawableRight.setColorFilter(filter);
+ } else {
+ drawableCenter.clearColorFilter();
+ drawableLeft.clearColorFilter();
+ drawableRight.clearColorFilter();
+ }
+ view.setTextSelectHandle(drawableCenter);
+ view.setTextSelectHandleLeft(drawableLeft);
+ view.setTextSelectHandleRight(drawableRight);
+ return;
+ }
+
+ // Based on https://github.com/facebook/react-native/pull/31007
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) {
+ return;
+ }
+
+ // The following code uses reflection to change handles color on Android 8.1 and below.
+ for (int i = 0; i < DRAWABLE_HANDLE_RESOURCES.length; i++) {
+ try {
+ Field drawableResourceField =
+ view.getClass().getDeclaredField(DRAWABLE_HANDLE_RESOURCES[i]);
+ drawableResourceField.setAccessible(true);
+ int resourceId = drawableResourceField.getInt(view);
+
+ // The view has no handle drawable.
+ if (resourceId == 0) {
+ return;
+ }
+
+ Drawable drawable = ContextCompat.getDrawable(view.getContext(), resourceId).mutate();
+ if (color != null) {
+ drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ } else {
+ drawable.clearColorFilter();
+ }
+
+ Field editorField = TextView.class.getDeclaredField("mEditor");
+ editorField.setAccessible(true);
+ Object editor = editorField.get(view);
+
+ Field cursorDrawableField = editor.getClass().getDeclaredField(DRAWABLE_HANDLE_FIELDS[i]);
+ cursorDrawableField.setAccessible(true);
+ cursorDrawableField.set(editor, drawable);
+ } catch (NoSuchFieldException ex) {
+ } catch (IllegalAccessException ex) {
+ }
+ }
}
@ReactProp(name = "cursorColor", customType = "Color")
public void setCursorColor(ReactEditText view, @Nullable Integer color) {
- if (color == null) {
- return;
- }
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Drawable cursorDrawable = view.getTextCursorDrawable();
if (cursorDrawable != null) {
- cursorDrawable.setColorFilter(new BlendModeColorFilter(color, BlendMode.SRC_IN));
+ if (color != null) {
+ cursorDrawable.setColorFilter(new BlendModeColorFilter(color, BlendMode.SRC_IN));
+ } else {
+ cursorDrawable.clearColorFilter();
+ }
view.setTextCursorDrawable(cursorDrawable);
}
return;
@@ -552,39 +607,35 @@ public class ReactTextInputManager extends BaseViewManager = [
+
+
);
},
@@ -470,7 +480,7 @@ const examples: Array = [
'next',
];
const returnKeyLabels = ['Compile', 'React Native'];
- const examples = returnKeyTypes.map(type => {
+ const returnKeyExamples = returnKeyTypes.map(type => {
return (
= [
});
return (
- {examples}
+ {returnKeyExamples}
{types}
);