EditText 处理 28(P) 的颜色,以 29(Q) 作为目标 API

Ray*_*oix 5 customization android android-edittext programmatically

到目前为止,我一直在使用反射,因此更新了 editTexts 的选择句柄。在我更新到目标 api 29(Q) 之前,它一直工作正常。

似乎 Google 做了一些更新(或 Java,不完全确定),但我现在收到如下消息:

访问隐藏字段 Landroid/widget/Editor;->mDrawableForCursor:Landroid/graphics/drawable/Drawable; (暗灰名单,反射)

从好的方面来说,api 29(多年后)具有以编程方式设置手柄颜色的稳定方式。不幸的是,它并没有像我发现的那样向后兼容,而且它也破坏了 api 28。低于 api 28 的任何东西都可以通过反射正常工作。在我现在适用于除 api 28 之外的任何代码的代码下方

 /**
 * Sets the color for the cursor and handles on the {@link EditText editText}.
 *
 * @throws EditTextTintError if an error occurs while tinting the view.
 */
public void apply() throws EditTextTintError {
    try {
        // Get the editor
        Field field = TextView.class.getDeclaredField("mEditor");
        field.setAccessible(true);
        Object editor = field.get(editText);

        if (cursorColor != null) {
            editText.setHighlightColor(ColorUtils.setAlphaComponent(cursorColor, 40));

            // Get the cursor drawable, tint it, and set it on the TextView Editor
            // Get the cursor resource id
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                ColorFilter colorFilter = new BlendModeColorFilter(cursorColor, BlendMode.SRC_ATOP);
                editText.getTextCursorDrawable().mutate().setColorFilter(colorFilter);
                editText.getTextSelectHandle().mutate().setColorFilter(colorFilter);
                editText.getTextSelectHandleLeft().mutate().setColorFilter(colorFilter);
                editText.getTextSelectHandleRight().mutate().setColorFilter(colorFilter);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                try {
                    Field fieldP = TextView.class.getDeclaredField("mCursorDrawableRes");
                    fieldP.setAccessible(true);
                    int drawableResId = fieldP.getInt(editText);

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

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

                    // Set the drawables
                    fieldP = editorP.getClass().getDeclaredField("mDrawableForCursor");
                    fieldP.setAccessible(true);
                    fieldP.set(editorP, drawable);
                } catch (final Exception ignored) {
                    ignored.printStackTrace();
                }
            } else {
                String[] resFieldNames = {"mTextSelectHandleLeftRes", "mTextSelectHandleRightRes", "mTextSelectHandleRes"};
                String[] drawableFieldNames = {"mSelectHandleLeft", "mSelectHandleRight", "mSelectHandleCenter"};
                Integer[] colors = {selectHandleLeftColor, selectHandleRightColor, selectHandleMiddleColor};
                for (int i = 0; i < resFieldNames.length; i++) {
                    Integer color = colors[i];
                    if (color == null) {
                        continue;
                    }

                    String resFieldName = resFieldNames[i];
                    String drawableFieldName = drawableFieldNames[i];

                    field = TextView.class.getDeclaredField(resFieldName);
                    field.setAccessible(true);
                    int selectHandleRes = field.getInt(editText);

                    Drawable selectHandleDrawable = ContextCompat.getDrawable(editText.getContext(), selectHandleRes).mutate();
                    selectHandleDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);

                    field = editor.getClass().getDeclaredField(drawableFieldName);
                    field.setAccessible(true);
                    field.set(editor, selectHandleDrawable);
                }
            }
        }
    } catch (Exception e) {
        throw new EditTextTintError("Error applying tint to " + editText, e);
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上Android P的情况被谷歌的最新更新破坏了:(

else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    // insert working code here :D
} 
Run Code Online (Sandbox Code Playgroud)

我的问题是,有没有人能够使用目标 api 29 以编程方式为在 api 28 上运行的设备设置句柄颜色?

为了清楚起见,这是指的句柄: Android EditText 句柄

hta*_*oya 0

你不能这样做吗textField.setTextSelectHandle(drawable)

对于 API < 29

// This avoids crash for api < 29: java.lang.NoSuchMethodError: No virtual method setTextSelectHandle
    private void setTextSelectHandle(@NonNull EditText editText, @NonNull Context context) {
        try {
            final Field fEditor = TextView.class.getDeclaredField("mEditor");
            fEditor.setAccessible(true);
            final Object editor = fEditor.get(editText);
            if (editor != null) {
                final Field fSelectHandleCenter = editor.getClass()
                        .getDeclaredField("mSelectHandleCenter");
                fSelectHandleCenter.setAccessible(true);
                fSelectHandleCenter.set(editor, context.getDrawable(R.drawable.disable_text_handle));
            }
        } catch (ReflectiveOperationException exception) {
            // no-op
        }
    }
Run Code Online (Sandbox Code Playgroud)