28

In android we can change the cursor color via:

android:textCursorDrawable="@drawable/black_color_cursor".

How can we do this dynamically?

In my case I have set cursor drawable to white, but i need to change black How to do ?

    // Set an EditText view to get user input
    final EditText input = new EditText(nyactivity);
    input.setTextColor(getResources().getColor(R.color.black));
Cœur
  • 32,421
  • 21
  • 173
  • 232
Amit Prajapati
  • 9,444
  • 7
  • 42
  • 71
  • follow [this link](https://stackoverflow.com/questions/7238450/set-edittext-cursor-color), the best to go for. – Krantiz Sep 10 '17 at 06:12

10 Answers10

87

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) {
  }
}
Jared Rummler
  • 35,743
  • 18
  • 127
  • 142
  • 2
    Thanks, you're a lifesaver. – Baggers Apr 15 '15 at 10:44
  • 1
    If anyone wants to change cursor pointer color. Use Jared Rummler approach but: instead of "mCursorDrawableRes" use "mTextSelectHandleRes" instead of "mCursorDrawable" use "mSelectHandleCenter" if you also need selection on top of that use mTextSelectHandleLeftRes, mTextSelectHandleRightRes and mSelectHandleLeft, mSelectHandleRight accordingly Checkout TextView.java and Edit.java for more information – Ivanov Maksim Mar 29 '18 at 12:24
  • 1
    Not working in API 15, java.lang.NoSuchFieldException: mEditor, Can you please provide some solution? – Hrishikesh Kadam Jul 25 '18 at 14:53
  • Has anyone tried to change the color of the caret on PIXEL XL phone updated to API 28? In my case the reflection cannot find the "mCursorDrawable" of the "mEditor". I found some posts online suggesting to use "mDrawableForCursor" instead, but the result is the same. Can anyone suggest approach which does not use any XML and works for all devices? – Pavel Pavlov Apr 09 '19 at 11:25
  • Also the first approach will change the caret for all instances of EditText in the application. How should one change the color only of a specific instance of EditText ? – Pavel Pavlov Apr 09 '19 at 11:47
  • it is showing me an error like "No field mCursorDrawable in class Landroid/widget/Editor; (declaration of 'android.widget.Editor' appears in /system/framework/framework.jar!classes2.dex)" – Pooja Shukla May 22 '19 at 06:21
  • 2
    @PavelPavlov since Android P, reflection `mDrawableForCursor` is in blacklist, use xml `android:textCursorDrawable` option if you still want to change cursor color. More info https://developer.android.com/distribute/best-practices/develop/restrictions-non-sdk-interfaces – cuasodayleo Jun 11 '19 at 09:18
  • @cuasodayleo the question I asked was to implement the same behavior entirely in Java (with no XML). Here is my original post (3 replies above): "Has anyone tried to change the color of the caret on PIXEL XL phone updated to API 28? In my case the reflection cannot find the "mCursorDrawable" of the "mEditor". I found some posts online suggesting to use "mDrawableForCursor" instead, but the result is the same. Can anyone suggest approach which does not use any XML and works for all devices?" – Pavel Pavlov Jun 12 '19 at 10:11
  • 1
    Let me clarify it again. The answer is you can't do it without XML. Since Android P they locking down on certain uses of reflection. It means even you can see `mDrawableForCursor` property in `Editor` but can't access it as we did with pre-P. This is from the official document and happens for all Android P devices not only PIXEL XL. – cuasodayleo Jun 12 '19 at 10:57
17
android:textCursorDrawable="@null"

Then in the application:

final EditText input = new EditText(nyactivity);
input.setTextColor(getResources().getColor(R.color.black));

Get from here

Community
  • 1
  • 1
Orest Savchak
  • 4,303
  • 1
  • 15
  • 26
7

This is a rewritten version of the function from @Jared Rummler with a couple of improvements:

  • Support for Android 4.0.x
  • The special getDrawable(Context, int) function sience the getDrawable(int) is deprecated for API 22 and above.
private static final Field
        sEditorField,
        sCursorDrawableField,
        sCursorDrawableResourceField;

static {
    Field editorField = null;
    Field cursorDrawableField = null;
    Field cursorDrawableResourceField = null;
    boolean exceptionThrown = false;
    try {
        cursorDrawableResourceField = TextView.class.getDeclaredField("mCursorDrawableRes");
        cursorDrawableResourceField.setAccessible(true);
        final Class<?> drawableFieldClass;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
            drawableFieldClass = TextView.class;
        } else {
            editorField = TextView.class.getDeclaredField("mEditor");
            editorField.setAccessible(true);
            drawableFieldClass = editorField.getType();
        }
        cursorDrawableField = drawableFieldClass.getDeclaredField("mCursorDrawable");
        cursorDrawableField.setAccessible(true);
    } catch (Exception e) {
        exceptionThrown = true;
    }
    if (exceptionThrown) {
        sEditorField = null;
        sCursorDrawableField = null;
        sCursorDrawableResourceField = null;
    } else {
        sEditorField = editorField;
        sCursorDrawableField = cursorDrawableField;
        sCursorDrawableResourceField = cursorDrawableResourceField;
    }
}

public static void setCursorColor(EditText editText, int color) {
    if (sCursorDrawableField == null) {
        return;
    }
    try {
        final Drawable drawable = getDrawable(editText.getContext(), 
                sCursorDrawableResourceField.getInt(editText));
        drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
        sCursorDrawableField.set(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN
                ? editText : sEditorField.get(editText), new Drawable[] {drawable, drawable});
    } catch (Exception ignored) {
    }
}

private static Drawable getDrawable(Context context, int id) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        return context.getResources().getDrawable(id);
    } else {
        return context.getDrawable(id);
    }
}
Oleg Barinov
  • 171
  • 4
  • 4
  • great, but i think it'd be better to replace your custom `getDrawable(context, resId)` method with `ContextCompat.getDrawable(context, resId)` - just the same method out of the box ;) – Alexander Krol May 03 '17 at 13:27
6

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)
}
John
  • 1,153
  • 13
  • 10
3

We managed to do it by:

  1. Creating a layout file with just an EditText and the cursor color set in xml on it.
  2. Inflating it
  3. Using the EditText as you would use a programmatically created one
Rotem
  • 2,147
  • 2
  • 21
  • 38
1

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);
    }
}
Hrishikesh Kadam
  • 29,211
  • 3
  • 19
  • 29
0

2019 Updated: working smooth and easy https://material.io/develop/android/docs/getting-started/

If you are using material component just simply use textCursorDrawable with color or your custom drawable.

    <com.google.android.material.textfield.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="12dp">

        <com.google.android.material.textfield.TextInputEditText
            android:layout_width="match_parent"
            android:textCursorDrawable="@color/red"
            android:cursorVisible="true"
            android:layout_height="wrap_content" />

    </com.google.android.material.textfield.TextInputLayout>
Amit Prajapati
  • 9,444
  • 7
  • 42
  • 71
0

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);
        }
0

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"

Dharman
  • 21,838
  • 18
  • 57
  • 107
0
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        if (editText.getTextCursorDrawable() instanceof InsetDrawable) {
            InsetDrawable insetDrawable = (InsetDrawable) editText.getTextCursorDrawable();
            insetDrawable.setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP);
            editText.setTextCursorDrawable(insetDrawable);
        }

        if (editText.getTextSelectHandle() instanceof BitmapDrawable) {
            BitmapDrawable insetDrawable = (BitmapDrawable) editText.getTextSelectHandle();
            insetDrawable.setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP);
            editText.setTextSelectHandle(insetDrawable);
        }

        if (editText.getTextSelectHandleRight() instanceof BitmapDrawable) {
            BitmapDrawable insetDrawable = (BitmapDrawable) editText.getTextSelectHandleRight();
            insetDrawable.setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP);
            editText.setTextSelectHandleRight(insetDrawable);
        }

        if (editText.getTextSelectHandleLeft() instanceof BitmapDrawable) {
            BitmapDrawable insetDrawable = (BitmapDrawable) editText.getTextSelectHandleLeft();
            insetDrawable.setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP);
            editText.setTextSelectHandleLeft(insetDrawable);
        }
    }

Before Q (29) see: https://stackoverflow.com/a/44352565/2255331

maja89
  • 125
  • 5