不要在错误时更改 TextInputLayout 背景

Do not change TextInputLayout background on error

我想要一个背景为 "normal" EditText 的 EditText,但带有 TextInputEditText 的错误处理(错误消息出现在底部,而不是“!”可绘制对象出现)。

我得到了这样的东西:

<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:setError="@{viewModel.error}">

    <android.support.design.widget.TextInputEditText

        android:layout_width="match_parent"
        android:layout_height="wrap_content"

        android:background="@drawable/simple_edit_text_background"
        android:ellipsize="end"
        android:inputType="textMultiLine|textNoSuggestions"
        android:text="@={viewModel.value}"

        style="@style/MyEditTextStyle" />

</android.support.design.widget.TextInputLayout>

但似乎当我在 TextInputLayout 上设置错误时,它会将背景可绘制对象(在正常的 TextInputEditText 中,下划线)更改为错误 TextView 的颜色。

这就是我的 EditText 的样子:

我们可以在以下方法中的TextInputLayout代码中看到它:

private void updateEditTextBackground() {
    if (mEditText == null) {
        return;
    }

    Drawable editTextBackground = mEditText.getBackground();
    if (editTextBackground == null) {
        return;
    }

    ensureBackgroundDrawableStateWorkaround();

    if (android.support.v7.widget.DrawableUtils.canSafelyMutateDrawable(editTextBackground)) {
        editTextBackground = editTextBackground.mutate();
    }

    if (mErrorShown && mErrorView != null) {
        // Set a color filter of the error color
        editTextBackground.setColorFilter(
                AppCompatDrawableManager.getPorterDuffColorFilter(
                        mErrorView.getCurrentTextColor(), PorterDuff.Mode.SRC_IN));
    } else if (mCounterOverflowed && mCounterView != null) {
        // Set a color filter of the counter color
        editTextBackground.setColorFilter(
                AppCompatDrawableManager.getPorterDuffColorFilter(
                        mCounterView.getCurrentTextColor(), PorterDuff.Mode.SRC_IN));
    } else {
        // Else reset the color filter and refresh the drawable state so that the
        // normal tint is used
        DrawableCompat.clearColorFilter(editTextBackground);
        mEditText.refreshDrawableState();
    }
}

更新背景颜色的代码块在这里:

if (mErrorShown && mErrorView != null) {
    // Set a color filter of the error color
    editTextBackground.setColorFilter(
            AppCompatDrawableManager.getPorterDuffColorFilter(
                    mErrorView.getCurrentTextColor(), PorterDuff.Mode.SRC_IN));
}

因为这个方法是私有的,所以我不能覆盖它,因为我仍然希望我的错误 TextView 的颜色是红色,所以到目前为止我看不到任何解决方案。有什么想法吗?

一个解决方案可能会在调用 setError 之后将背景颜色重置为其默认值,但是它们是否使用类似 onError 的方法进行任何回调,一旦出现错误就会触发设置为 TextView/EditText?

我通过像这样覆盖 TextInputLayout 自己解决了这个问题:

public class NoChangingBackgroundTextInputLayout extends TextInputLayout {
    public NoChangingBackgroundTextInputLayout(Context context) {
        super(context);
    }

    public NoChangingBackgroundTextInputLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public NoChangingBackgroundTextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setError(@Nullable CharSequence error) {
        ColorFilter defaultColorFilter = getBackgroundDefaultColorFilter();
        super.setError(error);
        //Reset EditText's background color to default.
        updateBackgroundColorFilter(defaultColorFilter);
    }

    @Override
    protected void drawableStateChanged() {
        ColorFilter defaultColorFilter = getBackgroundDefaultColorFilter();
        super.drawableStateChanged();
        //Reset EditText's background color to default.
        updateBackgroundColorFilter(defaultColorFilter);
    }

    private void updateBackgroundColorFilter(ColorFilter colorFilter) {
        if(getEditText() != null && getEditText().getBackground() != null)
            getEditText().getBackground().setColorFilter(colorFilter);
    }

    @Nullable
    private ColorFilter getBackgroundDefaultColorFilter() {
        ColorFilter defaultColorFilter = null;
        if(getEditText() != null && getEditText().getBackground() != null)
            defaultColorFilter = DrawableCompat.getColorFilter(getEditText().getBackground());
        return defaultColorFilter;
    }
}

正如我们所见,它在调用 setError 之后将 EditText 的背景重置为其默认颜色,但也在方法 drawableStateChanged() 中,因为在 losing/getting也关注有错误的 EditText。

我不认为这是最好的解决方案,但如果我没有得到任何更好的解决方案,我会同时将其标记为已解决。

用 and 安排接受的解决方案,我写了另一个class。当 EditText 具有特殊背景 (background_1) 时,它会在出错时更改为 background_2。当错误消失时,它再次 returns 到 background_1。没有执行红色填充。

public class YourTextInputLayout extends TextInputLayout {

    private Drawable drawable1; // Normal background.
    private Drawable drawable2; // Error background.

    public YourTextInputLayout(Context context) {
        super(context);
    }

    public YourTextInputLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public YourTextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();

        replaceBackground();
    }

    @Override
    public void setError(@Nullable final CharSequence error) {
        super.setError(error);

        replaceBackground();
    }

    public void setDrawable1(Drawable drawable) {
        this.drawable1 = drawable;
    }

    public void setDrawable2(Drawable drawable) {
        this.drawable2 = drawable;
    }

    private void replaceBackground() {
        EditText editText = getEditText();
        if (editText != null) {
            editText.setBackground(isErrorEnabled() ? drawable2 : drawable1);
            Drawable drawable = editText.getBackground();
            if (drawable != null) {
                drawable.clearColorFilter();
            }
        }
    }
}

在 onCreate() / onCreateView() 初始化后的 activity/fragment 调用中:

YourTextInputLayout inputLayout = ...;
inputLayout.setDrawable1(ContextCompat.getDrawable(getContext(), R.drawable.background_1));
inputLayout.setDrawable2(ContextCompat.getDrawable(getContext(), R.drawable.background_2));

在您的 XML 布局中调用它:

<com.example.package.YourTextInputLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <EditText
        ...

我遇到了同样的问题;搜索并点击 & 运行 后,我找到了解决这个问题的简单方法-

试试这个最简单的方法 -

               <android.support.design.widget.TextInputLayout
                    android:id="@+id/til_description"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="8dp"
                    app:errorText="@{feedbackViewModel.descError}"
                    >

                    <EditText
                        style="@style/TextInputLayoutEditText"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:background="@drawable/desc_field_selector"
                        **android:paddingTop="10dp"**
                        **android:paddingBottom="7dp"**
                        android:gravity="top|left"
                        android:hint="@string/description"
                        android:inputType="textMultiLine"
                        android:lines="4"
                        android:onTextChanged="@{(text, start, before, count) -> feedbackViewModel.onDescriptionTextChanged(text)}"
                        android:scrollHorizontally="false"
                        android:scrollbarStyle="insideInset"
                        android:scrollbars="vertical"
                        android:text="@={feedbackViewModel.description}"/>

                </android.support.design.widget.TextInputLayout>

android:background="@drawable/desc_field_selector" -

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/rectangle_blue_border_background"
          android:state_pressed="true"/>
    <item android:drawable="@drawable/rectangle_blue_border_background"
          android:state_enabled="true"
          android:state_focused="true"
          android:state_window_focused="true"/>
    <item android:drawable="@drawable/rectangle_black_border_background"/>
</selector>

现在原始形状 (@drawable/rectangle_black_border_background) 将像 -

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    **<item android:state_focused="false" android:top="5dp">**
        <shape>
            **<solid android:color="@android:color/transparent"/>**
            <stroke android:width="1dp" android:color="@android:color/secondary_text_light"/>
            <corners android:radius="5dp"/>
        </shape>
    </item>
</layer-list>

而原始形状 (@drawable/rectangle_blue_border_background) 将像 -

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    **<item android:state_focused="true" android:top="5dp">**
        <shape>
            **<solid android:color="@android:color/transparent"/>**
            <stroke android:width="@dimen/one_dip" android:color="@color/colorAccent"/>
            <corners android:radius="5dp"/>
        </shape>
    </item>
</layer-list>

注意- **行太重要了-

  1. paddingTop="10dp"android:paddingBottom="7dp" -> 将浮动标签向上移动边框形状够了,不然不好看
  2. android:top="5dp" - 对于移动浮动标签足够长的形状很重要。
  3. solid android:color="@android:color/transparent" - 这个纯色会是透明的所以当错误发生时你只会看到一种透明的颜色。看看 -

**注意 - **根据需要更改项目和纯色的填充值和顶部值(您也可以删除纯色线)。

就是这样 :-) 编码愉快 +1

我建议的更简单的解决方案是创建一个可绘制资源文件 (@drawable/rectangular_border) :-

<?xml version="1.0" encoding="utf-8"?>    
<shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
    
        <stroke
            android:width="2dp"
            android:color="@color/colorAccent"        />    
        <corners
            android:radius="10dp"
            />
        <solid
            android:color="@android:color/transparent"
            />
    
    </shape>

并在你的 XML 文件中用以下资源文件替换你的 TextInputEditText 背景,即 -

<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:setError="@{viewModel.error}">

    <android.support.design.widget.TextInputEditText

        android:layout_width="match_parent"
        android:layout_height="wrap_content"

        android:background="@drawable/rectangular_border"
        android:ellipsize="end"
        android:inputType="textMultiLine|textNoSuggestions"
        android:text="@={viewModel.value}"

        style="@style/MyEditTextStyle" />

</android.support.design.widget.TextInputLayout>

这样你的背景颜色不会完全改变,只有边框上的错误或计数器溢出。希望这个解决方案能解决您的问题:).

如果您的 edittext 视图有自定义背景,请在您的可绘制文件夹中创建一个 并将其 属性颜色设置为透明。作为参考,这里是名称为的形状:shape_input_layout_edittext.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:innerRadius="200dp"
    android:shape="rectangle">
    <stroke
        android:width="1dp"
        android:color="#e2e2e2"></stroke>
    <solid android:color="#00000000"></solid>
    <corners android:radius="2dp"></corners>


</shape>

现在,在布局 xml 文件中,像这样使用它:

<com.google.android.material.textfield.TextInputLayout
                android:id="@+id/inputLayout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:hintEnabled="false">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/edtText"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"                    
  android:background="@drawable/shape_input_layout_edittext" ..../>
</com.google.android.material.textfield.TextInputLayout>

就是这样,现在每当出现错误时,edittext 的背景将没有颜色。 编码愉快!