33

I am trying to change the tinting color of an EditText View programmatically during runtime. Basically i want to change what you would usually apply as ?attr/colorControlNormal like in the default background drawable.

Changing the background tint does not correctly apply by just setting a new ColorsStateList with one color:

editText.setBackgroundTintList( ColorStateList.valueOf( color ) );

For one the result is applied to all EditText although the tint list is applied and internally mutates the drawable. Also the alpha as specified in the default background 1 is visible at the beginning.

Here is the outcome of setting the tint color on just the first EditText:

outcome of setting the tint color on just the first EditText

So my question would be: How can I properly apply the tint programmatically to an EditText?

k0pernikus
  • 60,309
  • 67
  • 216
  • 347
Moritz
  • 10,124
  • 7
  • 51
  • 61

6 Answers6

47

This works for me:

editText.getBackground().setColorFilter(getResources().getColor(R.color.your_color),
                                        PorterDuff.Mode.SRC_ATOP);

Source: Changing EditText bottom line color with appcompat v7

Pang
  • 9,564
  • 146
  • 81
  • 122
ingyesid
  • 2,864
  • 2
  • 23
  • 21
  • If you want to only temporarily change the color of a single `EditText` (e.g. for marking an error), you have to call `mutate()` too (so: `editText.getBackground().mutate()....`). Otherwise the color change is shared across multiple `EditTexts` and resetting (having saved the old `Background` first) makes the old and the new color mix in a weird way. – Neph Mar 10 '20 at 14:43
23

With the newly introduced android.support.v4.graphics.drawable.DrawableCompat#setTint setting the color is now possible.

Moritz
  • 10,124
  • 7
  • 51
  • 61
  • 1
    Show me a example? – Lucas B. Sep 12 '18 at 19:34
  • 9
    Example: `DrawableCompat.setTint(editText.getBackground(), ContextCompat.getColor(context, R.color.xxx));` – Wess Nov 08 '18 at 12:20
  • How do you reset the background color to default with this? If I use the same code again but with the original color (e.g. `ColorAccent`), the text field uses that color even when it's inactive (usually it would be grey then). If I call `editText.setBackground(oldBackground)`, the `EditText` ends up using the old color when it's inactive and the new one when it's active, so a mix between both. – Neph Mar 10 '20 at 14:46
14

Try to create a custom EditText and add this.setBackgroundTintList( ColorStateList.valueOf( color ) ); into constructor.

Lennon Spirlandelli
  • 3,131
  • 5
  • 26
  • 51
  • 1
    Changing the tint during creation time is not sufficient for my use case. I need to change it on the fly during runtime. – Moritz Feb 03 '15 at 16:19
  • So you can create a listener into the custom `EditText` and call this method whenever you desire. – Lennon Spirlandelli Feb 03 '15 at 18:46
  • That would create a new View each time i change the color which would also require to port over all the state the TextView currently has. That sounds like a lot of overhead. – Moritz Feb 04 '15 at 09:50
  • No. That wouldn't create a new View. You must create a custom `EditText` with a listener to change its own color. After that, when you want to change the color, you just have to call the listener and every single `EditText` will response this listener. – Lennon Spirlandelli Feb 04 '15 at 12:10
  • I am not following your train of thought. When you specify to set something in the constructor, the only way to call this code again is via instantiation. – Moritz Feb 04 '15 at 15:57
  • Yes, I said that. But you said that the `EditText` would change their color during creation time. Because of that, I told you to create a listener, but the listener cannot be created into constructor. In this case, you must create it into your class such as usual method. – Lennon Spirlandelli Feb 04 '15 at 18:10
6

setColorFilter not working for me. I used:

Drawable wrappedDrawable = DrawableCompat.wrap(mView.getBackground());
DrawableCompat.setTint(wrappedDrawable, getResources().getColor(R.color.red));
mView.setBackgroundDrawable(wrappedDrawable);

or

DrawableCompat.setTint(mView.getBackground(), ContextCompat.getColor(this, R.color.red));

Let's try.

t0m
  • 3,004
  • 31
  • 53
2

for Kotlin

editText.backgroundTintList = ColorStateList.valueOf(R.color.colorLightGray )
-3

I wrote a small component to achieve this behavior.

Few important notes:

  • Old school setColorFilter method is used
  • To make tint work, first switch focus to other view, then tint EditText background drawable

Usage

ErrorLabelLayout layoutPassError = (ErrorLabelLayout) findViewById(R.id.layoutPasswordError)
layoutPassError.setError("Password_is_wrong");
// when you want to clear error e.g. in on text changed method
layoutPassError.clearError();

XML

<com.view.material.ErrorLabelLayout
    android:id="@+id/layoutPasswordError"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:focusable="false">

    <EditText
        android:id="@+id/editPassword"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword"
        android:hint="Enter your password"/>
</com.view.material.ErrorLabelLayout>

Source

public class ErrorLabelLayout extends LinearLayout implements ViewGroup.OnHierarchyChangeListener {

    private static final int ERROR_LABEL_TEXT_SIZE = 12;
    private static final int ERROR_LABEL_PADDING = 4;

    private TextView mErrorLabel;
    private Drawable mDrawable;
    private int mErrorColor;

    public ErrorLabelLayout(Context context) {
        super(context);
        initView();
    }

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

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

    private void initView() {
        setOnHierarchyChangeListener(this);
        setOrientation(VERTICAL);
        mErrorColor = Color.parseColor("#D32F2F");
        initErrorLabel();
    }

    private void initErrorLabel() {
        mErrorLabel = new TextView(getContext());
        mErrorLabel.setFocusable(true);
        mErrorLabel.setFocusableInTouchMode(true);
        mErrorLabel.setTextSize(ERROR_LABEL_TEXT_SIZE);
        mErrorLabel.setTextColor(mErrorColor);
        mErrorLabel.setPadding(dipsToPix(ERROR_LABEL_PADDING), 0, dipsToPix(ERROR_LABEL_PADDING), 0);
    }

    public void setErrorColor(int color) {
        mErrorColor = color;
        mErrorLabel.setTextColor(mErrorColor);
    }

    public void clearError() {
        mErrorLabel.setVisibility(INVISIBLE);
        mDrawable.clearColorFilter();
    }

    public void setError(String text) {
        mErrorLabel.setVisibility(VISIBLE);
        mErrorLabel.setText(text);
        // changing focus from EditText to error label, necessary for Android L only
        // EditText background Drawable is not tinted, until EditText remains focus
        mErrorLabel.requestFocus();
        // tint drawable
        mDrawable.setColorFilter(mErrorColor, PorterDuff.Mode.SRC_ATOP);
    }

    @Override
    public void onChildViewAdded(View parent, View child) {
        int childCount = getChildCount();
        if (childCount == 1) {
            mDrawable = getChildAt(0).getBackground();
            addView(mErrorLabel);
        }
    }

    @Override
    public void onChildViewRemoved(View parent, View child) {
    }

    private int dipsToPix(float dps) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dps, getResources().getDisplayMetrics());
    }
}

Tested on API 16 / 21 with com.android.support:appcompat-v7:22.1.1 library.

Dmytro Danylyk
  • 19,684
  • 11
  • 62
  • 68