In android we can change the cursor color via:
android:textCursorDrawable=\"@drawable/black_color_cursor\"
.
How can we do this dynamically?
We managed to do it by:
Kotlin version, works from api 14 to api 29
fun setCursorDrawableColor(editText: TextView, @ColorInt color: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val gradientDrawable = GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, intArrayOf(color, color))
gradientDrawable.setSize(2.spToPx(editText.context).toInt(), editText.textSize.toInt())
editText.textCursorDrawable = gradientDrawable
return
}
try {
val editorField = try {
TextView::class.java.getDeclaredField("mEditor").apply { isAccessible = true }
} catch (t: Throwable) {
null
}
val editor = editorField?.get(editText) ?: editText
val editorClass: Class<*> = if (editorField == null) TextView::class.java else editor.javaClass
val tintedCursorDrawable = TextView::class.java.getDeclaredField("mCursorDrawableRes")
.apply { isAccessible = true }
.getInt(editText)
.let { ContextCompat.getDrawable(editText.context, it) ?: return }
.let { tintDrawable(it, color) }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
editorClass
.getDeclaredField("mDrawableForCursor")
.apply { isAccessible = true }
.run { set(editor, tintedCursorDrawable) }
} else {
editorClass
.getDeclaredField("mCursorDrawable")
.apply { isAccessible = true }
.run { set(editor, arrayOf(tintedCursorDrawable, tintedCursorDrawable)) }
}
} catch (t: Throwable) {
t.printStackTrace()
}
}
fun Number.spToPx(context: Context? = null): Float {
val res = context?.resources ?: android.content.res.Resources.getSystem()
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, this.toFloat(), res.displayMetrics)
}
fun tintDrawable(drawable: Drawable, @ColorInt color: Int): Drawable {
(drawable as? VectorDrawableCompat)
?.apply { setTintList(ColorStateList.valueOf(color)) }
?.let { return it }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
(drawable as? VectorDrawable)
?.apply { setTintList(ColorStateList.valueOf(color)) }
?.let { return it }
}
val wrappedDrawable = DrawableCompat.wrap(drawable)
DrawableCompat.setTint(wrappedDrawable, color)
return DrawableCompat.unwrap(wrappedDrawable)
}
Here is the solution for Xamarin based on John's answer
public static void SetCursorDrawableColor(EditText editText, Color color)
{
try
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
{
var gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.BottomTop, new[] { (int)color, color });
gradientDrawable.SetSize(SpToPx(2, editText.Context), (int)editText.TextSize);
editText.TextCursorDrawable = gradientDrawable;
return;
}
var fCursorDrawableRes =
Class.FromType(typeof(TextView)).GetDeclaredField("mCursorDrawableRes");
fCursorDrawableRes.Accessible = true;
int mCursorDrawableRes = fCursorDrawableRes.GetInt(editText);
var fEditor = Class.FromType(typeof(TextView)).GetDeclaredField("mEditor");
fEditor.Accessible = true;
Java.Lang.Object editor = fEditor.Get(editText);
Class clazz = editor.Class;
if (Build.VERSION.SdkInt >= BuildVersionCodes.P)
{
//TODO This solution no longer works in Android P because of reflection
// Get the drawable and set a color filter
Drawable drawable = ContextCompat.GetDrawable(editText.Context, mCursorDrawableRes);
drawable.SetColorFilter(color, PorterDuff.Mode.SrcIn);
var fCursorDrawable = clazz.GetDeclaredField("mDrawableForCursor");
fCursorDrawable.Accessible = true;
fCursorDrawable.Set(editor, drawable);
}
else
{
Drawable[] drawables = new Drawable[2];
drawables[0] = ContextCompat.GetDrawable(editText.Context, mCursorDrawableRes).Mutate();
drawables[1] = ContextCompat.GetDrawable(editText.Context, mCursorDrawableRes).Mutate();
drawables[0].SetColorFilter(color, PorterDuff.Mode.SrcIn);
drawables[1].SetColorFilter(color, PorterDuff.Mode.SrcIn);
var fCursorDrawable = clazz.GetDeclaredField("mCursorDrawable");
fCursorDrawable.Accessible = true;
fCursorDrawable.Set(editor, drawables);
}
}
catch (ReflectiveOperationException) { }
catch (Exception ex)
{
Crashes.TrackError(ex);
}
}
public static int SpToPx(float sp, Context context)
{
return (int)TypedValue.ApplyDimension(ComplexUnitType.Sp, sp, context.Resources.DisplayMetrics);
}
You should change "colorAccent" and in order not to change this parameter for the whole application you can use ThemeOverlay. You can read more detailed in this article, the last section "Cursor and Selection"
Using some reflection did the trick for me
Java:
// https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/TextView.java#L562-564
Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
f.setAccessible(true);
f.set(yourEditText, R.drawable.cursor);
XML:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#ff000000" />
<size android:width="1dp" />
</shape>
Here is a method that you can use that doesn't need an XML:
public static void setCursorColor(EditText view, @ColorInt int color) {
try {
// Get the cursor resource id
Field field = TextView.class.getDeclaredField("mCursorDrawableRes");
field.setAccessible(true);
int drawableResId = field.getInt(view);
// Get the editor
field = TextView.class.getDeclaredField("mEditor");
field.setAccessible(true);
Object editor = field.get(view);
// Get the drawable and set a color filter
Drawable drawable = ContextCompat.getDrawable(view.getContext(), drawableResId);
drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
Drawable[] drawables = {drawable, drawable};
// Set the drawables
field = editor.getClass().getDeclaredField("mCursorDrawable");
field.setAccessible(true);
field.set(editor, drawables);
} catch (Exception ignored) {
}
}
Inspired from @Jared Rummler and @Oleg Barinov I have crafted solution which works on API 15 also -
public static void setCursorColor(EditText editText, @ColorInt int color) {
try {
// Get the cursor resource id
Field field = TextView.class.getDeclaredField("mCursorDrawableRes");
field.setAccessible(true);
int drawableResId = field.getInt(editText);
// Get the drawable and set a color filter
Drawable drawable = ContextCompat.getDrawable(editText.getContext(), drawableResId);
drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
Drawable[] drawables = {drawable, drawable};
if (Build.VERSION.SDK_INT == 15) {
// Get the editor
Class<?> drawableFieldClass = TextView.class;
// Set the drawables
field = drawableFieldClass.getDeclaredField("mCursorDrawable");
field.setAccessible(true);
field.set(editText, drawables);
} else {
// Get the editor
field = TextView.class.getDeclaredField("mEditor");
field.setAccessible(true);
Object editor = field.get(editText);
// Set the drawables
field = editor.getClass().getDeclaredField("mCursorDrawable");
field.setAccessible(true);
field.set(editor, drawables);
}
} catch (Exception e) {
Log.e(LOG_TAG, "-> ", e);
}
}