【问题标题】:Change color edittext selection handles programmatically以编程方式更改颜色编辑文本选择句柄
【发布时间】:2017-12-28 17:39:34
【问题描述】:

我正在尝试以编程方式更改编辑文本的颜色。它可以工作,但正如您从所附图像中看到的那样,文本选择的图标仍然使用主题颜色重音而不是我设置的蓝色。我怎样才能改变它?我当前的代码是:

editText.setBackgroundTintList(new ColorStateList(new int[][]{StateSet.WILD_CARD}, new int[]{color}));
setCursorDrawableColor(editText, color);

private void setCursorDrawableColor(EditText editText, int color) {
    try {
        Field fCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes");
        fCursorDrawableRes.setAccessible(true);
        int mCursorDrawableRes = fCursorDrawableRes.getInt(editText);
        Field fEditor = TextView.class.getDeclaredField("mEditor");
        fEditor.setAccessible(true);
        Object editor = fEditor.get(editText);
        Class<?> clazz = editor.getClass();
        Field fCursorDrawable = clazz.getDeclaredField("mCursorDrawable");
        fCursorDrawable.setAccessible(true);

        Drawable[] drawables = new Drawable[2];
        Resources res = editText.getContext().getResources();
        drawables[0] = res.getDrawable(mCursorDrawableRes);
        drawables[1] = res.getDrawable(mCursorDrawableRes);
        drawables[0].setColorFilter(color, PorterDuff.Mode.SRC_IN);
        drawables[1].setColorFilter(color, PorterDuff.Mode.SRC_IN);
        fCursorDrawable.set(editor, drawables);
    } catch (final Throwable ignored) {
    }
}

【问题讨论】:

  • 您是否尝试过使用样式而不是反射?\

标签: android


【解决方案1】:

我更喜欢使用styles.xml 来设置样式。但是,以编程方式执行此操作可以如下完成:

1.高亮颜色

首先是高亮颜色。这可以通过以下方式设置:

editText.setHighlightColor(color);

2。左右标记

左右标记仍未被着色。继续你的反射方法,我们应该对这些选择标记做同样的事情:

// Left
Field fCursorDrawableLeftRes = TextView.class.getDeclaredField("mTextSelectHandleLeftRes");
fCursorDrawableLeftRes.setAccessible(true);
int mCursorDrawableLeftRes = fCursorDrawableLeftRes.getInt(editText);

// Right
Field fCursorDrawableRightRes = TextView.class.getDeclaredField("mTextSelectHandleRightRes");
fCursorDrawableRightRes.setAccessible(true);
int mCursorDrawableRightRes = fCursorDrawableRightRes.getInt(editText);

当然:将其添加到可绘制列表以更新它们(从您的源更新):

Drawable[] drawables = new Drawable[3];
Resources res = editText.getContext().getResources();
drawables[0] = res.getDrawable(mCursorDrawableRes);
drawables[1] = res.getDrawable(mCursorDrawableLeftRes);
drawables[2] = res.getDrawable(mCursorDrawableRightRes);
drawables[0].setColorFilter(color, PorterDuff.Mode.SRC_IN);
drawables[1].setColorFilter(color, PorterDuff.Mode.SRC_IN);
drawables[2].setColorFilter(color, PorterDuff.Mode.SRC_IN);

3.结果

这意味着您的方法将类似于:

private void setCursorDrawableColor(EditText editText, int color) {
    try {
        Field fCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes");
        fCursorDrawableRes.setAccessible(true);
        int mCursorDrawableRes = fCursorDrawableRes.getInt(editText);

        // Left
        Field fCursorDrawableLeftRes = TextView.class.getDeclaredField("mTextSelectHandleLeftRes");
        fCursorDrawableLeftRes.setAccessible(true);
        int mCursorDrawableLeftRes = fCursorDrawableLeftRes.getInt(editText);

        // Right
        Field fCursorDrawableRightRes = TextView.class.getDeclaredField("mTextSelectHandleRightRes");
        fCursorDrawableRightRes.setAccessible(true);
        int mCursorDrawableRightRes = fCursorDrawableRightRes.getInt(editText);

        Field fEditor = TextView.class.getDeclaredField("mEditor");
        fEditor.setAccessible(true);
        Object editor = fEditor.get(editText);
        Class<?> clazz = editor.getClass();
        Field fCursorDrawable = clazz.getDeclaredField("mCursorDrawable");
        fCursorDrawable.setAccessible(true);

        Drawable[] drawables = new Drawable[3];
        Resources res = editText.getContext().getResources();
        drawables[0] = res.getDrawable(mCursorDrawableRes);
        drawables[1] = res.getDrawable(mCursorDrawableLeftRes);
        drawables[2] = res.getDrawable(mCursorDrawableRightRes);
        drawables[0].setColorFilter(color, PorterDuff.Mode.SRC_IN);
        drawables[1].setColorFilter(color, PorterDuff.Mode.SRC_IN);
        drawables[2].setColorFilter(color, PorterDuff.Mode.SRC_IN);
        fCursorDrawable.set(editor, drawables);
    } catch (final Throwable ignored) {}
}

//其他方法(使用styles.xml

正如我所说,我更喜欢使用styles.xml 来实现这种行为。在这种情况下,仅使用以下三个属性将导致想要的行为:

<item name="colorControlNormal">@android:color/holo_green_dark</item>
<item name="colorControlActivated">@android:color/holo_green_dark</item>
<item name="colorControlHighlight">@android:color/holo_green_dark</item>

(当然还有 textColorHighlight 用于突出显示)

【讨论】:

  • @mFeinstein 哪一部分不适合你?在你的情况下什么没有被着色?这应该至少适用于 API 21 及更高版本
  • 我只是在仔细检查我是否正确应用了所有样式......但原则上气泡手柄没有改变颜色。另外,我相信只需colorAccent 就足以改变它
  • 对于手柄和底线,这也行得通。这些值只是更特定于控件。此外,使用colorAccent 仍然需要为文本顶部的突出显示栏设置android:textColorHighlight。这不是由强调色着色的。
  • 是的,这就是我设置的,但是 EditText 似乎忽略了我的风格,这就是为什么它不起作用,我必须检查它为什么......
  • 好的,我修好了,我不知道为什么,但是我的EditText 忽略了我在styletextAppearance 中设置的内容,但是如果我在android:theme 中传递它就可以了。 ...我不知道为什么...你呢?
【解决方案2】:

Kotlin 版本,适用于 api 14 到 api 32

@SuppressLint("PrivateApi")
fun TextView.setHandlesColor(@ColorInt color: Int) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val size = 22.spToPx(context).toInt()
        val corner = size.toFloat() / 2
        val inset = 10.spToPx(context).toInt()

        //left drawable
        val drLeft = GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, intArrayOf(color, color))
        drLeft.setSize(size, size)
        drLeft.cornerRadii = floatArrayOf(corner, corner, 0f, 0f, corner, corner, corner, corner)
        setTextSelectHandleLeft(InsetDrawable(drLeft, inset, 0, inset, inset))

        //right drawable
        val drRight = GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, intArrayOf(color, color))
        drRight.setSize(size, size)
        drRight.cornerRadii = floatArrayOf(0f, 0f, corner, corner, corner, corner, corner, corner)
        setTextSelectHandleRight(InsetDrawable(drRight, inset, 0, inset, inset))

        //middle drawable
        val drMiddle = GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, intArrayOf(color, color))
        drMiddle.setSize(size, size)
        drMiddle.cornerRadii = floatArrayOf(0f, 0f, corner, corner, corner, corner, corner, corner)
        val mInset = (sqrt(2f) * corner - corner).toInt()
        val insetDrawable = InsetDrawable(drMiddle, mInset, mInset, mInset, mInset)
        val rotateDrawable = RotateDrawable()
        rotateDrawable.drawable = insetDrawable
        rotateDrawable.toDegrees = 45f
        rotateDrawable.level = 10000
        setTextSelectHandle(rotateDrawable)
        return
    }

    try {
        val editorField = TextView::class.java.getFieldByName("mEditor")
        val editor = editorField?.get(this) ?: this
        val editorClass: Class<*> = if (editorField != null) Class.forName("android.widget.Editor") else TextView::class.java
        val handles = androidx.collection.ArrayMap<String, String>(3).apply {
            put("mSelectHandleLeft", "mTextSelectHandleLeftRes")
            put("mSelectHandleRight", "mTextSelectHandleRightRes")
            put("mSelectHandleCenter", "mTextSelectHandleRes")
        }
        for (i in 0 until handles.size) {
            editorClass.getFieldByName(handles.keyAt(i))?.let { field: Field ->
                val drawable = field.get(editor) as? Drawable
                    ?: TextView::class.java.getFieldByName(handles.valueAt(i))
                        ?.getInt(this)
                        ?.let { ContextCompat.getDrawable(context, it) }

                if(drawable != null) field.set(editor, drawable.tinted(color))
            }
        }
    } catch (e: java.lang.Exception) {
        e.printStackTrace()
    }
}

fun Class<*>.getFieldByName(name: String): Field? = try {
    this.getDeclaredField(name).apply { isAccessible = true }
} catch (t: Throwable) {
    null
}

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

【讨论】:

【解决方案3】:

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

    public static void SetHandlesColor(EditText editText, Color color)
        {

            try
            {
                if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
                {
                    var size = SpToPx(22, editText.Context);
                    var corner = size / 2f;
                    var inset = SpToPx(10, editText.Context);

                    //left drawable
                    var drLeft = new GradientDrawable(GradientDrawable.Orientation.BottomTop, new[] { (int)color, color });
                    drLeft.SetSize(size, size);
                    drLeft.SetCornerRadii(new[] { corner, corner, 0f, 0f, corner, corner, corner, corner });
                    editText.TextSelectHandleLeft = new InsetDrawable(drLeft, inset, 0, inset, inset);

                    //right drawable
                    var drRight = new GradientDrawable(GradientDrawable.Orientation.BottomTop, new[] { (int)color, color });
                    drRight.SetSize(size, size);
                    drRight.SetCornerRadii(new[] { 0f, 0f, corner, corner, corner, corner, corner, corner });
                    editText.TextSelectHandleRight = new InsetDrawable(drRight, inset, 0, inset, inset);

                    //middle drawable
                    var drMiddle = new GradientDrawable(GradientDrawable.Orientation.BottomTop, new[] { (int)color, color });
                    drMiddle.SetSize(size, size);
                    drMiddle.SetCornerRadii(new[] { 0f, 0f, corner, corner, corner, corner, corner, corner });
                    var mInset = (int)(System.Math.Sqrt(2f) * corner - corner);
                    var insetDrawable = new InsetDrawable(drMiddle, mInset, mInset, mInset, mInset);
                    var rotateDrawable = new RotateDrawable();
                    rotateDrawable.Drawable = insetDrawable;
                    rotateDrawable.ToDegrees = 45f;
                    rotateDrawable.SetLevel(10000);
                    editText.TextSelectHandle = rotateDrawable;
                    return;
                }

                var editorField = Class.FromType(typeof(TextView)).GetDeclaredField("mEditor");
                if (!editorField.Accessible)
                    editorField.Accessible = true;

                var editor = editorField.Get(editText);
                var editorClass = editor.Class;
                string[] handleNames = { "mSelectHandleLeft", "mSelectHandleRight", "mSelectHandleCenter" };
                string[] resNames = { "mTextSelectHandleLeftRes", "mTextSelectHandleRightRes", "mTextSelectHandleRes" };

                for (int i = 0; i < handleNames.Length; i++)
                {
                    var handleField = editorClass.GetDeclaredField(handleNames[i]);
                    if (!handleField.Accessible)
                    {
                        handleField.Accessible = true;
                    }
                    Drawable handleDrawable = (Drawable)handleField.Get(editor);

                    if (handleDrawable == null)
                    {
                        var resField = Class.FromType(typeof(TextView)).GetDeclaredField(resNames[i]);
                        if (!resField.Accessible)
                        {
                            resField.Accessible = true;

                        }
                        int resId = resField.GetInt(editText);
                        handleDrawable = ContextCompat.GetDrawable(editText.Context, resId);

                    }

                    if (handleDrawable != null)
                    {
                        Drawable drawable = handleDrawable.Mutate();
                        drawable.SetColorFilter(color, PorterDuff.Mode.SrcIn);
                        handleField.Set(editor, drawable);
                    }
                }
            }
            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);
        }

【讨论】:

    猜你喜欢
    • 2018-07-14
    • 2011-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-24
    • 2015-12-06
    • 2021-03-31
    • 1970-01-01
    相关资源
    最近更新 更多