james
james

Reputation: 5226

Android TextInputLayout icon in error (or hint) text

I am using a TextInputLayoutHelper widget in order to follow the material guidelines for floating label inputs. It currently looks like this:

floating input label with error message

My code

In my activities onCreate function, I have:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val passwordInputLayout = this.findViewById<TextInputLayoutHelper>(R.id.input_layout_password)
    passwordInputLayout.error = "8+ characters and at least one uppercase letter, a number, and a special character (\$, #, !)"
    passwordInputLayout.isErrorEnabled = true
}

and my widget in my xml looks like...

<TextInputLayout
    android:id="@+id/input_layout_password"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/EditTextTheme"
    app:errorEnabled="true"
    app:errorTextAppearance="@style/ErrorAppearance"
    app:passwordToggleDrawable="@drawable/asl_password_visibility"
    app:passwordToggleEnabled="true"
    app:passwordToggleTint="?colorControlNormal">

    <EditText
        android:id="@+id/password_edit_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="@string/set_a_password"
        android:inputType="textPassword"
        android:singleLine="true" />

</TextInputLayout>

What I want to do

I want to put an icon in the error/hint text (the exclamation triangle) to the right of the error text.

mock up of input with error icon

My Attempt

Attempt 1

I found an implementation which uses setError(text, drawable) but I am using Kotlin to setError is not available.

So I tried:

val warningIcon = ResourcesCompat.getDrawable(resources, R.drawable.ic_warning_black_24dp, null)
warningIcon?.setBounds(0, 0, warningIcon.intrinsicWidth, warningIcon.intrinsicHeight)

passwordInputLayout.error = "8+ characters and at least one uppercase letter, a number, and a special character (\$, #, !) $warningIcon"

but that does not render the drawable, only a string of the resource path.

Attempt 2

I found another one that overrides the TextInputLayoutHelper in order to set a drawable next to the text. As you can see, setError only contains the interface override fun setError(error: CharSequence?) which does not have a parameter for drawable.

override fun setError(error: CharSequence?) {
    super.setError(error)

    val warningIcon = ResourcesCompat.getDrawable(resources, R.drawable.ic_warning_black_24dp, null)
    warningIcon?.setBounds(0, 0, warningIcon.intrinsicWidth, warningIcon.intrinsicHeight)
    // mHelperView is a TextView used in my custom `TextInputLayout` override widget
    mHelperView!!.setCompoundDrawables(null, null, warningIcon, null)
}

Is there an override or built in "Android way" to add this icon next to the error/hint text?

Upvotes: 12

Views: 9057

Answers (3)

dilix
dilix

Reputation: 3893

You want to place an icon inside the error container of TextInputLayout

If you check the real layout

enter image description here

You'll find that your textview is even wrap content.

Also in SDK sources

if (enabled) {
    mErrorView = new AppCompatTextView(getContext());
    mErrorView.setId(R.id.textinput_error);
    ...

So there is no place to insert an icon on the right AS IS.

  • One possible solution is to find this view programatically inside your layout, change width and apply drawable.
  • Second (and probably easiest) solution is to implement your own simple error layout and not to set error for TextInputLayout but manage the error state by yourself

Upvotes: 0

Hesam
Hesam

Reputation: 53600

This might help others who've fallen in to similar trouble. I looked at source codes. The thing is setError(text, drawable) has defined in EditText class not TextInputLayout as your layout is based on.

I have similar code and issue like you and since TextInputLayout doesn't have any method to replace Drawable then we have to create a TextView below TextInputLayout in order to simulate error message.

Upvotes: 0

Cassian
Cassian

Reputation: 121

You can use SpannableString with ImageSpan like this

val errorDrawable = ContextCompat.getDrawable(context!!, R.drawable.ic_error)
your_text_input_layout.error = SpannableString("your string").apply {
                setSpan(ImageSpan(errorDrawable, ImageSpan.ALIGN_BASELINE), 0, 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE)

Upvotes: 3

Related Questions