TextInputLayout label interferring with entered text

Viewed 125

In my new Android app, the label of the TextInputLayout appears in the space where the user types the input. This causes the entered text to render on top of the label/hint:

When the user first loads the page: When the user first loads the page

When the user taps on the text input, the label shrinks towards the top left as expected: When the user taps on the text input

When the user types an entry, it types over the label: When the user types an entry

I have two questions:

  1. What is the appropriate behavior here following Material Design guidelines?
  2. How do I accomplish this behavior?

Here is my layout file:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:theme="@style/Theme.JobSearch"
    tools:context=".ui.JobsDetailsFragment">

    <TextView
        android:id="@+id/text_new_position"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/new_position"
        android:theme="@style/Theme.JobSearch"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/textInputLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text_new_position">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/text_title"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:hint="@string/title"
            android:theme="@style/Theme.JobSearch"
            app:layout_constraintTop_toTopOf="parent" />
    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.textfield.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/Theme.JobSearch"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textInputLayout">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/text_business_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/business_name" />
    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/position_save"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        android:contentDescription="@string/position_save"
        android:src="@drawable/done"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Update

I tried moving android:hint to the TextInputLayout and still see the same behavior:

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/title_input_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/title"
        android:theme="@style/Theme.JobSearch"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text_new_position">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/text_title"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintTop_toTopOf="parent" />
    </com.google.android.material.textfield.TextInputLayout>

For reference, here is themes.xml:

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.JobSearch" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/white</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>

    <style name="Theme.JobSearch.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

    <style name="Theme.JobSearch.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />

    <style name="Theme.JobSearch.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>
4 Answers

https://material.io/components/text-fields/android#filled-text-field

Note: The android:hint should always be set on the TextInputLayout instead of on the EditText in order to avoid unintended behaviors.

I think this is your problem.

Update: Try this code for the first box:

<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/title_input_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/title"
    android:theme="@style/Theme.JobSearch"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/text_new_position">

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

This removes the line app:layout_constraintTop_toTopOf="parent" and changes the height field to android:layout_height="wrap_content"

I have replicated your issue and the problem is because your using android:theme="@style/Theme.JobSearch" in your TextInputLayout or TextInputEditText. Remove all android:theme attributes from all TextInputLayout and TextInputEditText and use your own style instead which will be applied only to your TextInputLayout like style="@style/MyTextInputLayoutStyle".

I have modified your xml layout using a custom style for each TextInputLayout like below:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:theme="@style/Theme.JobSearch"
    tools:context=".ui.JobsDetailsFragment">

    <TextView
        android:id="@+id/text_new_position"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/new_position"
        android:theme="@style/Theme.JobSearch"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/textInputLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/title"
        style="@style/MyTextInputLayoutStyle"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text_new_position">

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

    <com.google.android.material.textfield.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/business_name"
        style="@style/MyTextInputLayoutStyle"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textInputLayout">

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

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/position_save"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        android:contentDescription="@string/position_save"
        android:src="@drawable/done"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

where style="@style/MyTextInputLayoutStyle" is your custom style for the TextInputLayout like below:

<style name="MyTextInputLayoutStyle" parent="Widget.MaterialComponents.TextInputLayout.FilledBox">
    <item name="boxStrokeColor">@color/text_input_box_stroke_color</item>
    <item name="boxBackgroundColor">@android:color/white</item>
</style>

and i have set a color selector for your boxStrokeColor (@color/text_input_box_stroke_color) to change the underline bottom stroke color based on the focused/default state like below:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/teal_200" android:state_focused="true" />
    <item android:color="@color/teal_200" android:state_hovered="true" />
    <item android:color="@android:color/darker_gray" />
</selector>

And the final result after the above changes will be like this:

textInputLayout

<com.google.android.material.textfield.TextInputEditText
    android:id="@+id/text_title"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:hint="@string/title"
    android:theme="@style/Theme.JobSearch"
    app:layout_constraintTop_toTopOf="parent" />

I am afraid that app:layout_constraintTop_toTopOf="parent" constraint could be removed as TextInputEditText is not a direct child of a ConstraintLayout or probably transferred to the surrounding TextInputLayout which is a direct child of the ConstraintLayout

Applying the latter to the layout, taking the sugguestion of @Jems into consideration

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:theme="@style/Theme.JobSearch"
    tools:context=".ui.JobsDetailsFragment">

    <TextView
        android:id="@+id/text_new_position"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/new_position"
        android:theme="@style/Theme.JobSearch"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/textInputLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" 
        android:hint="@string/title"
        app:layout_constraintTop_toBottomOf="@+id/text_new_position">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/text_title"
            android:layout_width="match_parent"
            android:layout_height="match_parent"

            android:theme="@style/Theme.JobSearch"/>
    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.textfield.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/Theme.JobSearch"
        android:hint="@string/business_name"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textInputLayout">

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

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/position_save"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        android:contentDescription="@string/position_save"
        android:src="@drawable/done"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Not much to say, it's quite obvious. You have to test it for yourself.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:theme="@style/Theme.JobSearch"
    tools:context=".ui.JobsDetailsFragment">

    <TextView
        android:id="@+id/text_new_position"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/new_position"
        android:theme="@style/Theme.JobSearch"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/textInputLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/text_new_position">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/text_title"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:hint="@string/title"
            android:theme="@style/Theme.JobSearch" />

    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.textfield.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/Theme.JobSearch"
        app:layout_constraintTop_toBottomOf="@+id/textInputLayout">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/text_business_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/business_name" />

    </com.google.android.material.textfield.TextInputLayout>

 
<com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/position_save"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        android:contentDescription="@string/position_save"
        android:src="@drawable/done"
        android:layout_gravity="right"
        app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
Related