Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TextInputLayout: Different color for hint label when not focused

What I want to do:

When using an EditText embedded in a TextInputLayout I want to ...

  1. Set the Color of the label to GREEN when it's de-focused and floating above the EditText because the user has already entered some value
  2. Set the Color of the label to RED when it's de-focused and located inside the EditText, because the user has not yet entered a value
  3. I do not want to change the hint text color of all my EditTexts to RED, but only when they're wrapped in a TextInputLayout (I don't need a general approach - a specific approach like setting a theme/style for each TextInputLayout in the layout XML would be fine)
  4. Preserve (i.e. don't change) the accent color (YELLOW) used to color the floating label when the user has focused the field.

What I have tried:

Setting the below as a theme/style on the TextInputLayout does satisfy 1. but not 2.

<style name="FloatingLabel" parent="Widget.Design.TextInputLayout">
    <item name="android:textColorHint">@color/red</item>
</style>

Setting a specific color on my embedded EditText that changes the hint text to another color:

 android:textColorHint="@color/text_placeholder_gray"

actually causes an overlap of hint texts when the label is moved from it's floating position back into the Edittext as a hint (i.e. no text).

Setting this:

<style name="TextAppearence.App.TextInputLayout" parent="@android:style/TextAppearance">
<item name="android:textColor">@color/main_color</item>

on the TextInputLayout:

 <android.support.design.widget.TextInputLayout
  ...
   app:hintTextAppearance="@style/TextAppearence.App.TextInputLayout" >

Changes the hint label color but it also does so for the focused state - which means 4 is not satisfied.

And since a picture says more than a tousand words (all fields are in non-focused state):

enter image description here

How to achieve a setup that satisfies criteria 1-4 ?

like image 214
AgentKnopf Avatar asked Jul 12 '16 11:07

AgentKnopf


People also ask

How do you set different gravity for TextInputLayout hint and text in Android?

To set a different gravity for the EditText , don't set any gravity attributes in the layout, then set the EditText 's gravity programmatically, in your code. That is, after setContentView() , use findViewById() to get the EditText , then call setGravity(Gravity. CENTER_HORIZONTAL) on it.

How do I turn off hint in TextInputLayout?

There may be three ways to go about achieving this: 1 Set android:hint on TextInputLayout to a space _ character, and keep android:hint="This is my cool hint" set on the EditText . By setting android:hint=" " , if (TextUtils. isEmpty(mHint)) evaluates to false , and the EditText retains its hint.


1 Answers

I had a similar problem: I needed to implement a text input layout in which the label has different colours for empty (when there is no text entered in the edit text), "filled" and focused state. My main problem was how to differentiate between the empty and the filled state as setting a different colour for the focused state was already easy using selectors. In the end I decided to define a custom "empty text" state and implement my custom text input layout (which extends the normal text input layout).

Here is some code:

In res/values/attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

...

    <!-- Custom state for the text input layout to determine whether the label is shown above some text or not -->
    <declare-styleable name="EmptyTextState">
        <attr name="state_empty_text" format="boolean"/>
    </declare-styleable>

</resources>

The custom text input layout:

public class EmptyStateTextInputLayout extends TextInputLayout {
    private boolean emptyText = true;
    private static final int[] EMPTY_TEXT_STATE = new int[]{R.attr.state_empty_text};

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

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

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

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        int[] state = super.onCreateDrawableState(extraSpace + 1);
        if (emptyText) {
            mergeDrawableStates(state, EMPTY_TEXT_STATE);
        }
        return state;
    }

    public void setEmptyTextState(boolean emptyTextState) {
        this.emptyText = emptyTextState;
        refreshDrawableState();
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if (child instanceof EditText) {
            EditText editText = (EditText) child;
            if (!TextUtils.isEmpty(editText.getText())) {
                setEmptyTextState(false);
            }
            editText.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                }

                @Override
                public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                }

                @Override
                public void afterTextChanged(Editable editable) {
                    if (!TextUtils.isEmpty(editable)) {
                        setEmptyTextState(false);
                    } else {
                        setEmptyTextState(true);
                    }
                }
            });
        }
        super.addView(child, index, params);
    }
}

XML selector to set the colour of label in different states (res/color/input_field_floating_label.xml):

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
    <item android:color="@color/focused_text_color" android:state_focused="true" />
    <item android:color="@color/placeholder_color" app:state_empty_text="true"/>
    <item android:color="@color/primary_text_color"/> <!-- default color -->
</selector>

Style for the input text layout (in res/values/styles.xml):

<style name="EditTextLayout">
    ...
    <item name="android:textColorHint">@color/input_field_floating_label</item>
</style>

Theme and style for the edit text (still in res/values/styles.xml):

<style name="EditTextTheme">
    ...
    <item name="android:textColorHint">@color/input_field_floating_label</item>
</style>

<style name="EditText">
    <item name="android:theme">@style/EditTextTheme</item>
    ...
</style>

Usage:

<com.package.path.widget.EmptyStateTextInputLayout
            style="@style/DarkEditTextLayout"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            ...
            >

            <EditText
                style="@style/EditText"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
</com.package.path.widget.EmptyStateTextInputLayout>

I recommend this blog post to get an idea of working with custom states: http://code.neenbedankt.com/example-of-custom-states-in-android-components/

like image 78
hildegard Avatar answered Sep 22 '22 06:09

hildegard