【问题标题】:How to Change programmatically Edittext Cursor Color in android?如何在android中以编程方式更改Edittext光标颜色?
【发布时间】:2014-11-17 17:09:31
【问题描述】:

在android中我们可以通过以下方式改变光标颜色:

android:textCursorDrawable="@drawable/black_color_cursor"

我们如何动态地做到这一点?

在我的情况下,我已将可绘制光标设置为白色,但我需要更改为黑色怎么办?

    // Set an EditText view to get user input
    final EditText input = new EditText(nyactivity);
    input.setTextColor(getResources().getColor(R.color.black));

【问题讨论】:

标签: java android


【解决方案1】:

使用一些反射对我有用

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>

这是一种无需 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 方法,但:使用“mTextSelectHandleRes”而不是“mCursorDrawable”代替“mCursorDrawableRes”,如果您还需要在此之上进行选择,请使用“mSelectHandleCenter”,使用 mTextSelectHandleLeftRes、mTextSelectHandleRightRes 和 mSelectHandleLeft、mSelectHandleRight 相应地检查 TextView.java 和 Edit.java了解更多信息
  • 在 API 15 中不起作用,java.lang.NoSuchFieldException: mEditor,您能提供一些解决方案吗?
  • @PavelPavlov 自 Android P 起,反射 mDrawableForCursor 已在黑名单中,如果您仍想更改光标颜色,请使用 xml android:textCursorDrawable 选项。更多信息developer.android.com/distribute/best-practices/develop/…
  • 让我再次澄清一下。答案是没有 XML 就无法做到。自 Android P 以来,他们锁定了反射的某些用途。这意味着即使您可以在Editor 中看到mDrawableForCursor 属性,但不能像我们使用pre-P 那样访问它。这来自官方文档,不仅适用于 PIXEL XL,而且适用于所有 Android P 设备。
【解决方案2】:
android:textCursorDrawable="@null"

然后在应用程序中:

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

Get from here

【讨论】:

  • 对不起,但我没有任何 xml 怎么办.??
【解决方案3】:

这是 @Jared Rummler 函数的重写版本,有一些改进:

  • 支持 Android 4.0.x
  • API 22 及更高版本已弃用 getDrawable(int) 中的特殊 getDrawable(Context, int) 函数。
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);
    }
}

【讨论】:

  • 很好,但我认为用ContextCompat.getDrawable(context, resId) 替换您的自定义getDrawable(context, resId) 方法会更好——开箱即用的方法相同;)
【解决方案4】:

Kotlin 版本,适用于 api 14 到 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)
}

【讨论】:

  • ... 经过 8 年的 Android 开发,我花了几个小时来更改光标颜色。我只想哭。感谢您的回答!
  • @UneXp 尽管stackoverflow.com/a/59488928/1197590UneXp 更改手柄颜色更难
  • 像魅力一样工作!!
【解决方案5】:

我们设法做到了:

  1. 创建一个仅包含 EditText 和在 xml 中设置的光标颜色的布局文件。
  2. 给它充气
  3. 使用 EditText 就像使用以编程方式创建的一样

【讨论】:

    【解决方案6】:

    @Jared Rummler@Oleg Barinov 的启发,我精心设计了适用于 API 15 的解决方案 -

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

    【讨论】:

    • 有什么方法可以将此更改应用于我的应用程序中的所有光标可绘制对象?
    【解决方案7】:

    2019 年更新: 工作顺畅轻松 https://material.io/develop/android/docs/getting-started/

    如果您使用的是材质组件,只需使用带有颜色或自定义可绘制对象的textCursorDrawable

        <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>
    

    【讨论】:

      【解决方案8】:

      这是基于John's answer的Xamarin解决方案

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

      【讨论】:

        【解决方案9】:

        您应该更改“colorAccent”,并且为了不更改整个应用程序的此参数,您可以使用 ThemeOverlay。你可以在this article阅读更详细的内容,最后一节“光标和选择”

        【讨论】:

          【解决方案10】:
              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);
                  }
              }
          

          在 Q (29) 之前请参阅:https://stackoverflow.com/a/44352565/2255331

          【讨论】:

          • 你不能使用editText.setTextCursorDrawable(ContextCompat.getDrawable(context, R.color.white));直接地 ?因为您已经为 >= Android Q 包装了
          猜你喜欢
          • 2016-06-05
          • 2017-04-14
          • 2015-04-02
          • 2012-07-23
          • 1970-01-01
          • 1970-01-01
          • 2016-05-22
          • 2017-11-25
          • 1970-01-01
          相关资源
          最近更新 更多