使用反射方法设置EditText光标颜色

时间:2018-09-29 01:27:41

标签: java android reflection android-edittext textview

我一直在使用反射方法以编程方式设置answer中我的EditText的光标颜色(我也尝试过此answer)。但是,在进行了一些最近的更新后,不记得确切的时间了,该方法不再起作用,我认为Android可能已经更改了TextView类中的某些内容。无论如何,有人可以帮我吗?现在是否有mCursorDrawableResmCursorDrawable的新字段名称,还是整个方法无效,并且现在需要以另一种方式实现?

更新:我刚刚发现此方法仅在Android P上停止工作,在以前的版本上仍然有效。

更新2:我自己解决了问题,请检查答案是否也卡住了。

2 个答案:

答案 0 :(得分:4)

好的,在深入研究Android Pie源代码之后,我发现Google已将mCursorDrawable更改为mDrawableForCursor,并将其类型从两个元素的Drawable数组更改为简单Drawable,所以我根据原始的反射方法进行了一些更改,现在它适用于Android P:

 public static void setEditTextCursorColor(EditText editText, int color) {
    try {
        // Get the cursor resource id
        if(Build.VERSION.SDK_INT >= 28){//set differently in Android P (API 28)
            Field field = TextView.class.getDeclaredField("mCursorDrawableRes");
            field.setAccessible(true);
            int drawableResId = field.getInt(editText);

            // Get the editor
            field = TextView.class.getDeclaredField("mEditor");
            field.setAccessible(true);
            Object editor = field.get(editText);

            // Get the drawable and set a color filter
            Drawable drawable = ContextCompat.getDrawable(editText.getContext(), drawableResId);
            drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);

            // Set the drawables
            field = editor.getClass().getDeclaredField("mDrawableForCursor");
            field.setAccessible(true);
            field.set(editor, drawable);
        }else {
            Field field = TextView.class.getDeclaredField("mCursorDrawableRes");
            field.setAccessible(true);
            int drawableResId = field.getInt(editText);

            // Get the editor
            field = TextView.class.getDeclaredField("mEditor");
            field.setAccessible(true);
            Object editor = field.get(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};

            // Set the drawables
            field = editor.getClass().getDeclaredField("mCursorDrawable");
            field.setAccessible(true);
            field.set(editor, drawables);
        }
        setEditTextHandleColor(editText, color);
    } catch (Exception ignored) {
    }
}

旁注,我真的希望Google可以添加诸如setCursorDrawable()之类的公共方法或类似的方法,这会容易得多。

答案 1 :(得分:1)

不幸的是,即使在兼容性库中,Google都没有将xml属性公开给着色或设置这些方法的可绘制对象的方法,因此,当前动态设置它们的唯一方法是通过反射进行设置。

但是,您可以在xml中设置可绘制对象,如果您只想着色现有的材质设计可绘制对象,则可以通过为文本选择手柄着色xml来完成,因为它们是位图可绘制对象,但是光标可绘制对象是一个插图可绘制的,因此必须从源代码中重新创建。

使用的可绘制对象为:

R.drawable.abc_text_select_handle_left_mtrl_light
R.drawable.abc_text_select_handle_middle_mtrl_light
R.drawable.abc_text_select_handle_right_mtrl_light
R.drawable.abc_text_cursor_material

您可以像这样创建文本选择柄可绘制对象的着色版本:

<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/abc_text_select_handle_left_mtrl_light"
    android:tint="@color/my_text_select_handle_color" />

<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/abc_text_select_handle_middle_mtrl_light"
    android:tint="@color/my_text_select_handle_color" />

<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/abc_text_select_handle_right_mtrl_light"
    android:tint="@color/my_text_select_handle_color" />

可以从源代码重新创建光标可绘制对象,如下所示:

<inset xmlns:android="http://schemas.android.com/apk/res/android"
       android:inset="2dp">
  <shape
      android:tint="@color/my_text_cursor_color"
      android:shape="rectangle">
    <size
        android:height="2dp"
        android:width="2dp" />
    <solid
        android:color="@color/white" />
  </shape>
</inset>

将它们放置在drawables文件夹中,并使用以下方法在您的AppCompatEditText xml定义中引用它们:

android:textCursorDrawable
android:textSelectHandle
android:textSelectHandleLeft
android:textSelectHandleRight

和瞧,自定义颜色的光标,并选择与默认材质设计版本完全匹配的手柄,避免反射,因此不会引起警告或错误。