以编程方式更改颜色EditText选择手柄

4

我正在尝试以编程方式更改EditText的颜色。它可以工作,但是如您所见,附加的图像中,文本选择的图标仍在使用主题颜色强调而不是我设置的蓝色。我该如何更改它?我的当前代码如下:

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

edit text with color accent


你试过使用样式而不是反射吗? - matrix
3个回答

3

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

import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.InsetDrawable
import android.graphics.drawable.RotateDrawable
import android.graphics.drawable.VectorDrawable
import android.os.Build
import android.util.TypedValue
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import java.lang.reflect.Field
import java.lang.Exception
import kotlin.math.sqrt

@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) {
            runCatching { Class.forName("android.widget.Editor") }.getOrNull() ?: editorField.javaClass
        } else {
            TextView::class.java
        }
        val handles = listOf(
            "mSelectHandleLeft" to "mTextSelectHandleLeftRes",
            "mSelectHandleRight" to "mTextSelectHandleRightRes",
            "mSelectHandleCenter" to "mTextSelectHandleRes"
        )
        for (i in 0 until handles.size) {
            editorClass.getFieldByName(handles[i].first)?.let { field: Field ->
                val drawable = field.get(editor) as? Drawable
                    ?: TextView::class.java.getFieldByName(handles[i].second)?.getInt(this)
                        ?.let { ContextCompat.getDrawable(context, it) }
                if (drawable != null) field.set(editor, drawable.tinted(color))
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

fun Class<*>.getFieldByName(name: String): Field? = runCatching {
    getDeclaredField(name).apply { isAccessible = true }
}.getOrNull()

fun Number.spToPx(context: Context? = null): Float =
    (context?.resources ?: Resources.getSystem()).displayMetrics
        .let { TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, this.toFloat(), it) }

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

1
值得一提的是,还需要调用 highlightColor = color 来设置正在突出显示的文本的颜色。 - Niklas
@Niklas 我已经查看了华为的 TextView 代码,似乎没有关于处理或着色的代码。 https://github.com/SivanLiu/HwFrameWorkSource/blob/master/P9_8_0_0/src/main/java/android/widget/TextView.java 如果你找到了解决方法,之后可以告诉我吗? - John
我找到的唯一东西是:https://github.com/SivanLiu/HwFrameWorkSource/blob/5b92ed0f1ccb4bafc0fdb08b6fc4d98447b754ad/P9_8_0_0/src/main/java/android/widget/Editor.java#L1974-L1980我们应该能够访问它:https://github.com/SivanLiu/HwFrameWorkSource/blob/5b92ed0f1ccb4bafc0fdb08b6fc4d98447b754ad/P9_8_0_0/src/main/java/android/widget/Editor.java#L197 - Niklas
更新的版本可以在华为上运行。 - Niklas
你在20小时前的编辑中修复了哪个问题? - Niklas
显示剩余2条评论

2

我希望使用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及以上版本。 - Alex
我只是再次确认是否正确应用了所有的样式...但原则上,气泡句柄的颜色没有改变。此外,我相信只需要使用 colorAccent 就足以改变它。 - Michel Feinstein
对于手柄和底线,这也可以工作。这些值只是更具体地针对控件。此外,使用 colorAccent 仍需要设置 android:textColorHighlight 来突出显示文本上方的高亮条。这不会受到强调颜色的影响。 - Alex
是的,那就是我设置的,但EditText似乎忽略了我的样式,这就是为什么它不起作用,我必须检查一下为什么... - Michel Feinstein
好的,我修复了。我不知道为什么,但是我的EditText忽略了我在styletextAppearance中设置的内容,但是如果我在android:theme中传递它,它就可以工作...我不知道为什么...你知道吗? - Michel Feinstein
可能有多种情况,可能是样式被另一个样式覆盖了,或者样式没有正确设置。考虑在StackOverflow上创建一个问题,并附上您要设置样式的代码。您的问题不在本帖的范围内。 - Alex

0

这里是针对基于 Xamarin 的 Jonh's 答案 的解决方案。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接