'How to Change programmatically Edittext Cursor Color in android?
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));
Solution 1:[1]
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) {
}
}
Solution 2:[2]
android:textCursorDrawable="@null"
Then in the application:
final EditText input = new EditText(nyactivity);
input.setTextColor(getResources().getColor(R.color.black));
Solution 3:[3]
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 thegetDrawable(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);
}
}
Solution 4:[4]
Kotlin version, works from api 14 to api 32
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.VectorDrawable
import android.os.Build
import android.util.TypedValue
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import java.lang.reflect.Field
fun TextView.setCursorDrawableColor(@ColorInt color: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
textCursorDrawable = GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, intArrayOf(color, color))
.apply { setSize(2.spToPx(context).toInt(), textSize.toInt()) }
return
}
try {
val editorField = TextView::class.java.getFieldByName("mEditor")
val editor = editorField?.get(this) ?: this
val editorClass: Class<*> = if (editorField != null) editor.javaClass else TextView::class.java
val cursorRes = TextView::class.java.getFieldByName("mCursorDrawableRes")?.get(this) as? Int ?: return
val tintedCursorDrawable = ContextCompat.getDrawable(context, cursorRes)?.tinted(color) ?: return
val cursorField = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
editorClass.getFieldByName("mDrawableForCursor")
} else {
null
}
if (cursorField != null) {
cursorField.set(editor, tintedCursorDrawable)
} else {
editorClass.getFieldByName("mCursorDrawable", "mDrawableForCursor")
?.set(editor, arrayOf(tintedCursorDrawable, tintedCursorDrawable))
}
} catch (t: Throwable) {
t.printStackTrace()
}
}
fun Class<*>.getFieldByName(vararg name: String): Field? {
name.forEach {
try{
return this.getDeclaredField(it).apply { isAccessible = true }
} catch (t: Throwable) { }
}
return null
}
fun Drawable.tinted(@ColorInt color: Int): Drawable = when {
this is VectorDrawableCompat -> {
this.apply { setTintList(ColorStateList.valueOf(color)) }
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && this is VectorDrawable -> {
this.apply { setTintList(ColorStateList.valueOf(color)) }
}
else -> {
DrawableCompat.wrap(this)
.also { DrawableCompat.setTint(it, color) }
.let { DrawableCompat.unwrap(it) }
}
}
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)
}
Solution 5:[5]
We managed to do it by:
- Creating a layout file with just an EditText and the cursor color set in xml on it.
- Inflating it
- Using the EditText as you would use a programmatically created one
Solution 6:[6]
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);
}
}
Solution 7:[7]
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
Solution 8:[8]
I wanted to build upon @John's answer. I found out that instead of using a GradientDrawable for Android >= Q you can just do:
textCursorDrawable?.tinted(color)
Therefore, the code becomes:
fun TextView.setCursorDrawableColor(@ColorInt color: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
textCursorDrawable?.tinted(color)
return
}
try {
val editorField = TextView::class.java.getFieldByName("mEditor")
val editor = editorField?.get(this) ?: this
val editorClass: Class<*> = if (editorField != null) editor.javaClass else TextView::class.java
val cursorRes = TextView::class.java.getFieldByName("mCursorDrawableRes")?.get(this) as? Int ?: return
val tintedCursorDrawable = ContextCompat.getDrawable(context, cursorRes)?.tinted(color) ?: return
val cursorField = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
editorClass.getFieldByName("mDrawableForCursor")
} else {
null
}
if (cursorField != null) {
cursorField.set(editor, tintedCursorDrawable)
} else {
editorClass.getFieldByName("mCursorDrawable", "mDrawableForCursor")
?.set(editor, arrayOf(tintedCursorDrawable, tintedCursorDrawable))
}
} catch (t: Throwable) {
t.printStackTrace()
}
}
fun Class<*>.getFieldByName(vararg name: String): Field? {
name.forEach {
try{
return this.getDeclaredField(it).apply { isAccessible = true }
} catch (t: Throwable) { }
}
return null
}
fun Drawable.tinted(@ColorInt color: Int): Drawable = when {
this is VectorDrawableCompat -> {
this.apply { setTintList(ColorStateList.valueOf(color)) }
}
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && this is VectorDrawable -> {
this.apply { setTintList(ColorStateList.valueOf(color)) }
}
else -> {
DrawableCompat.wrap(this)
.also { DrawableCompat.setTint(it, color) }
.let { DrawableCompat.unwrap(it) }
}
}
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)
}
Solution 9:[9]
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>
Solution 10:[10]
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);
}
Solution 11:[11]
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"
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow