How to Change programmatically Edittext Cursor Color in android?

前端 未结 9 419
眼角桃花
眼角桃花 2020-12-08 14:23

In android we can change the cursor color via:

android:textCursorDrawable=\"@drawable/black_color_cursor\".

How can we do this dynamically?

相关标签:
9条回答
  • 2020-12-08 15:04

    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
    0 讨论(0)
  • 2020-12-08 15:05

    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)
    }
    
    0 讨论(0)
  • 2020-12-08 15:06

    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 讨论(0)
  • 2020-12-08 15:09

    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"

    0 讨论(0)
  • 2020-12-08 15:10

    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) {
      }
    }
    
    0 讨论(0)
  • 2020-12-08 15:10

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题