I worked out a working solution to this problem after 2 days of struggle, below solution is perfect for them who want to change few edit text only, change/toggle color through java code, and want to overcome the problems of different behavior on OS versions due to use setColorFilter() method.
import android.content.Context; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.support.v4.content.ContextCompat; import android.support.v7.widget.AppCompatDrawableManager; import android.support.v7.widget.AppCompatEditText; import android.util.AttributeSet; import com.newco.cooltv.R; public class RqubeErrorEditText extends AppCompatEditText { private int errorUnderlineColor; private boolean isErrorStateEnabled; private boolean mHasReconstructedEditTextBackground; public RqubeErrorEditText(Context context) { super(context); initColors(); } public RqubeErrorEditText(Context context, AttributeSet attrs) { super(context, attrs); initColors(); } public RqubeErrorEditText(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initColors(); } private void initColors() { errorUnderlineColor = R.color.et_error_color_rule; } public void setErrorColor() { ensureBackgroundDrawableStateWorkaround(); getBackground().setColorFilter(AppCompatDrawableManager.getPorterDuffColorFilter( ContextCompat.getColor(getContext(), errorUnderlineColor), PorterDuff.Mode.SRC_IN)); } private void ensureBackgroundDrawableStateWorkaround() { final Drawable bg = getBackground(); if (bg == null) { return; } if (!mHasReconstructedEditTextBackground) { // This is gross. There is an issue in the platform which affects container Drawables // where the first drawable retrieved from resources will propogate any changes // (like color filter) to all instances from the cache. We'll try to workaround it... final Drawable newBg = bg.getConstantState().newDrawable(); //if (bg instanceof DrawableContainer) { // // If we have a Drawable container, we can try and set it's constant state via // // reflection from the new Drawable // mHasReconstructedEditTextBackground = // DrawableUtils.setContainerConstantState( // (DrawableContainer) bg, newBg.getConstantState()); //} if (!mHasReconstructedEditTextBackground) { // If we reach here then we just need to set a brand new instance of the Drawable // as the background. This has the unfortunate side-effect of wiping out any // user set padding, but I'd hope that use of custom padding on an EditText // is limited. setBackgroundDrawable(newBg); mHasReconstructedEditTextBackground = true; } } } public boolean isErrorStateEnabled() { return isErrorStateEnabled; } public void setErrorState(boolean isErrorStateEnabled) { this.isErrorStateEnabled = isErrorStateEnabled; if (isErrorStateEnabled) { setErrorColor(); invalidate(); } else { getBackground().mutate().clearColorFilter(); invalidate(); } } }
Uses in xml
<com.rqube.ui.widget.RqubeErrorEditText android:id="@+id/f_signup_et_referral_code" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_toEndOf="@+id/referral_iv" android:layout_toRightOf="@+id/referral_iv" android:ems="10" android:hint="@string/lbl_referral_code" android:imeOptions="actionNext" android:inputType="textEmailAddress" android:textSize="@dimen/text_size_sp_16" android:theme="@style/EditTextStyle"/>
Add lines in style
<style name="EditTextStyle" parent="android:Widget.EditText"> <item name="android:textColor">@color/txt_color_change</item> <item name="android:textColorHint">@color/et_default_color_text</item> <item name="colorControlNormal">@color/et_default_color_rule</item> <item name="colorControlActivated">@color/et_engagged_color_rule</item> </style>
java code to toggle color
myRqubeEditText.setErrorState(true); myRqubeEditText.setErrorState(false);
AppCompatEditText, apparently.