Changing EditText bottom line color with appcompat

2019-01-01 03:22发布

问题:

I am using appcompat v7 to get the look consistent on Android 5 and less. It works rather well. However I cannot figure out how to change the bottom line color and the accent color for EditTexts. Is it possible?

I have tried to define a custom android:editTextStyle (cf. below) but I only succeeded to change the full background color or text color but not the bottom line nor the accent color. Is there a specific property value to use? do I have to use a custom drawable image through the android:background property? is it not possible to specify a color in hexa?

 <style name=\"Theme.App.Base\" parent=\"Theme.AppCompat.Light.DarkActionBar\">
     <item name=\"android:editTextStyle\">@style/Widget.App.EditText</item>
 </style>

 <style name=\"Widget.App.EditText\" parent=\"Widget.AppCompat.EditText\">
     ???
 </style>

According to android API 21 sources, EditTexts with material design seem to use colorControlActivated and colorControlNormal. Therefore, I have tried to override these properties in the previous style definition but it has no effect. Probably appcompat does not use it. Unfortunately, I cannot find the sources for the last version of appcompat with material design.

回答1:

Finally, I have found a solution. It simply consists of overriding the value for colorControlActivated, colorControlHighlight and colorControlNormal in your app theme definition and not your edittext style. Then, think to use this theme for whatever activity you desire. Below is an example:

<style name=\"Theme.App.Base\" parent=\"Theme.AppCompat.Light.DarkActionBar\">
    <item name=\"colorControlNormal\">#c5c5c5</item>
    <item name=\"colorControlActivated\">@color/accent</item>
    <item name=\"colorControlHighlight\">@color/accent</item>
</style>


回答2:

I felt like this needed an answer in case somebody wanted to change just a single edittext. I do it like this:

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


回答3:

While Laurents solution is correct, it comes with some drawbacks as described in the comments since not only the bottom line of the EditText gets tinted but the Back Button of the Toolbar, CheckBoxes etc. as well.

Luckily v22.1 of appcompat-v7 introduced some new possibilities. Now it\'s possible to assign a specific theme only to one view. Straight from the Changelog:

Deprecated use of app:theme for styling Toolbar. You can now use android:theme for toolbars on all API level 7 and higher devices and android:theme support for all widgets on API level 11 and higher devices.

So instead of setting the desired color in a global theme, we create a new one and assign it only to the EditText.

Example:

<style name=\"MyEditTextTheme\">
    <!-- Used for the bottom line when not selected / focused -->
    <item name=\"colorControlNormal\">#9e9e9e</item>
    <!-- colorControlActivated & colorControlHighlight use the colorAccent color by default -->
</style>

<EditText
    android:layout_width=\"match_parent\"
    android:layout_height=\"wrap_content\"
    android:theme=\"@style/MyEditTextTheme\"/>


回答4:

For Reference. This can be changed in xml by using:

android:backgroundTint=\"@color/blue\"


回答5:

Here is the solution for API < 21 and above

Drawable drawable = yourEditText.getBackground(); // get current EditText drawable 
drawable.setColorFilter(Color.GREEN, PorterDuff.Mode.SRC_ATOP); // change the drawable color

if(Build.VERSION.SDK_INT > 16) {
    yourEditText.setBackground(drawable); // set the new drawable to EditText
}else{
    yourEditText.setBackgroundDrawable(drawable); // use setBackgroundDrawable because setBackground required API 16
}

\"enter

Hope it help



回答6:

The accepted answer is a bit more per style basis thing, but the most efficient thing to do is to add the colorAccent attribute in your AppTheme style like this:

<style name=\"AppTheme.Base\" parent=\"Theme.AppCompat.Light.NoActionBar\">
        <item name=\"colorAccent\">@color/colorAccent</item>
        <item name=\"android:editTextStyle\">@style/EditTextStyle</item>
</style>

<style name=\"EditTextStyle\" parent=\"Widget.AppCompat.EditText\"/>

The colorAccent attribute is used for widget tinting throughout the app and thus should be used for consistency



回答7:

If you are using appcompat-v7:22.1.0+ you can use the DrawableCompat to tint your widgets

    public static void tintWidget(View view, int color) {
        Drawable wrappedDrawable = DrawableCompat.wrap(view.getBackground());
        DrawableCompat.setTint(wrappedDrawable.mutate(), getResources().getColor(color));
        view.setBackgroundDrawable(wrappedDrawable);
    }


回答8:

Use:

<EditText
    app:backgroundTint=\"@color/blue\"/>

This will support pre-Lollipop devices not only +21



回答9:

<!-- Base application theme. -->
<style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">
    <!-- Customize your theme here. -->
    <item name=\"colorPrimary\">@color/colorPrimary</item>
    <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>
    <item name=\"colorAccent\">@color/colorAccent</item>

    <item name=\"colorControlNormal\">@color/colorAccent</item>
    <item name=\"colorControlActivated\">@color/colorAccent</item>
    <item name=\"colorControlHighlight\">@color/colorAccent</item>

</style>



回答10:

One quick solution for your problem is to look in yourappspackage/build/intermediates/exploded-aar/com.android.support/appcompat-v7/res/drawable/ for abc_edit_text_material.xml and copy that xml file in your drawable folder. Then you can change the colour of the 9 patch files from inside this selector, in order to match your preferences.



回答11:

Here is a part of source code of TextInputLayout in support design library(UPDATED for version 23.2.0), which changes EditText\'s bottom line color in a simpler way:

private void updateEditTextBackground() {
    ensureBackgroundDrawableStateWorkaround();

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

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

It seems that all of above code become useless right now in 23.2.0 if you want to change the color programatically.

And if you want to support all platforms, here is my method:

/**
 * Set backgroundTint to {@link View} across all targeting platform level.
 * @param view the {@link View} to tint.
 * @param color color used to tint.
 */
public static void tintView(View view, int color) {
    final Drawable d = view.getBackground();
    final Drawable nd = d.getConstantState().newDrawable();
    nd.setColorFilter(AppCompatDrawableManager.getPorterDuffColorFilter(
            color, PorterDuff.Mode.SRC_IN));
    view.setBackground(nd);
}


回答12:

I too was stuck on this problem for too long.

I required a solution that worked for versions both above and below v21.

I finally discovered a very simple perhaps not ideal but effective solution: Simply set the background colour to transparent in the EditText properties.

<EditText
    android:background=\"@android:color/transparent\"/>

I hope this saves someone some time.



回答13:

You can set background of edittext to a rectangle with minus padding on left, right and top to achieve this. Here is the xml example:

<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">
    <item
        android:top=\"-1dp\"
        android:left=\"-1dp\"
        android:right=\"-1dp\"
        android:bottom=\"1dp\"
        >
        <shape android:shape=\"rectangle\">
            <stroke android:width=\"1dp\" android:color=\"#6A9A3A\"/>
        </shape>
    </item>
</layer-list>

Replace the shape with a selector if you want to provide different width and color for focused edittext.



回答14:

For me I modified both the AppTheme and a value colors.xml Both the colorControlNormal and the colorAccent helped me change the EditText border color. As well as the cursor, and the \"|\" when inside an EditText.

<style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">
    <!-- Customize your theme here. -->
    <item name=\"colorControlNormal\">@color/yellow</item>
    <item name=\"colorAccent\">@color/yellow</item>
</style>

Here is the colors.xml

<?xml version=\"1.0\" encoding=\"utf-8\"?>
<resources>
    <color name=\"yellow\">#B7EC2A</color>
</resources>

I took out the android:textCursorDrawable attribute to @null that I placed inside the editText style. When I tried using this, the colors would not change.



回答15:

It\'s very easy just add android:backgroundTint attribute in your EditText.

android:backgroundTint=\"@color/blue\"
android:backgroundTint=\"#ffffff\"
android:backgroundTint=\"@color/red\"


 <EditText
     android:layout_width=\"match_parent\"
     android:layout_height=\"wrap_content\"
     android:backgroundTint=\"#ffffff\"/>


回答16:

I use this method to change the color of the line with PorterDuff, with no other drawable.

public void changeBottomColorSearchView(int color) {
    int searchPlateId = mSearchView.getContext().getResources().getIdentifier(\"android:id/search_plate\", null, null);
    View searchPlate = mSearchView.findViewById(searchPlateId);
    searchPlate.getBackground().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}


回答17:

If you want change bottom line without using app colors, use these lines in your theme:

<item name=\"android:editTextStyle\">@android:style/Widget.EditText</item>
<item name=\"editTextStyle\">@android:style/Widget.EditText</item>

I don\'t know another solution.



回答18:

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


回答19:

In Activit.XML add the code

<EditText
        android:layout_width=\"match_parent\"
        android:layout_height=\"wrap_content\"
        android:inputType=\"textPersonName\"
        android:ems=\"10\"
        android:id=\"@+id/editText\"
        android:hint=\"Informe o usuário\"
        android:backgroundTint=\"@android:color/transparent\"/>

Where BackgroundTint=color for your desired colour



回答20:

I was absolutely baffled by this problem. I had tried everything in this thread, and in others, but no matter what I did I could not change the color of the underline to anything other than the default blue.

I finally figured out what was going on. I was (incorrectly) using android.widget.EditText when making a new instance (but the rest of my components were from the appcompat library). I should have used android.support.v7.widget.AppCompatEditText. I replaced new EditText(this) with new AppCompatEditText(this) and the problem was instantly solved. It turns out, if you are actually using AppCompatEditText, it will just respect the accentColor from your theme (as mentioned in several comments above) and no additional configuration is necessary.



回答21:

This is the easiest and most efficient/reusable/works on all APIs
Create a custom EditText class like so:

public class EditText extends android.widget.EditText {
    public EditText(Context context) {
        super(context);
        init();
    }

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

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

    private void init() {
        getBackground().mutate().setColorFilter(ContextCompat.getColor(getContext(), R.color.colorAccent), PorterDuff.Mode.SRC_ATOP);
    }
}

Then use it like this:

 <company.com.app.EditText
        android:layout_width=\"200dp\"
        android:layout_height=\"wrap_content\"/>


回答22:

To change the EditText background dynamically, you can use ColorStateList.

int[][] states = new int[][] {
    new int[] { android.R.attr.state_enabled}, // enabled
    new int[] {-android.R.attr.state_enabled}, // disabled
    new int[] {-android.R.attr.state_checked}, // unchecked
    new int[] { android.R.attr.state_pressed}  // pressed
};

int[] colors = new int[] {
    Color.BLACK,
    Color.RED,
    Color.GREEN,
    Color.BLUE
};

ColorStateList colorStateList = new ColorStateList(states, colors);

Credits: This SO answer about ColorStateList is awesome.



回答23:

Please modify this method according to your need. This worked for me!

  private boolean validateMobilenumber() {
        if (mobilenumber.getText().toString().trim().isEmpty() || mobilenumber.getText().toString().length() < 10) {
            input_layout_mobilenumber.setErrorEnabled(true);
            input_layout_mobilenumber.setError(getString(R.string.err_msg_mobilenumber));
           // requestFocus(mobilenumber);
            return false;
        } else {
            input_layout_mobilenumber.setError(null);
            input_layout_mobilenumber.setErrorEnabled(false);
            mobilenumber.setBackground(mobilenumber.getBackground().getConstantState().newDrawable());
        }