TextInputLayout Error right align
Asked Answered
D

6

16

I've an EditText enclosed within a TextInputLayout. I wish to display errors under the EditText, but aligned to the right end of the screen.

This is what I currently have: Text Input Layout

The error is displayed like this: Error right now

What I want it to look like: What I want

My XML is this:

        <android.support.design.widget.TextInputLayout
            android:id="@+id/text_input_email"
            style="@style/forgot_pass_text_inputlayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/tv_enter_email_message"
            android:layout_marginTop="@dimen/email_padding_top"
            android:hintTextAppearance="@{forgotPasswordVM.hintTextAppearance}"
            android:theme="@style/forgot_pass_til_state"
            app:error="@{forgotPasswordVM.email.textInputError}">

            <EditText
                android:id="@+id/actv_email"
                style="@style/forgot_pass_edit_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/email_address"
                android:imeActionId="@+id/btn_submit"
                android:imeOptions="actionSend"
                android:inputType="textEmailAddress"
                android:maxLines="1"
                android:onEditorAction="@{forgotPasswordVM.onEditorAction}"
                android:singleLine="true"
                app:binding="@{forgotPasswordVM.email.text}" />
        </android.support.design.widget.TextInputLayout>

I'm using data-binding.

I've tried setting android:gravity="right" and android:layout_gravity="right" on both the EditText and the TextInputLayout. Neither works the way I want it to.

I've tried setting right gravity in the theme as well as the style. Neither has any effect on the error.

I've tried right gravity on the style that is applied within app:errorTextAppearance="@style/right_error". Even this doesn't work.

I tried programmatically shifting the Error to the right by using a SpannableStringBuilder with an AlignmentSpan following this link. Even that doesn't work for me.

I'm not sure what else to try. Please suggest.

Dinsdale answered 3/3, 2017 at 10:1 Comment(3)
You might try using one of the options in my answer here to get the error TextView, and call setGravity(Gravity.RIGHT) on it. I've not tested it, but IIRC, the error TextView has a match_parent width, so it should align its text as you describe.Beekeeper
Good suggestion Mike M. but I tried both RIGHT and END to no avail. Had to go with the programmatic approach detailed below ...Livingstone
https://mcmap.net/q/749535/-how-to-make-error-message-in-textinputlayout-appear-in-centerMollie
D
12

So thanks to Mike's answer I was able to figure out the solution.

I created a custom class:

public class CustomTextInputLayout extends TextInputLayout {

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

    @Override
    public void setErrorEnabled(boolean enabled) {
        super.setErrorEnabled(enabled);

        if (!enabled) {
            return;
        }

        try {
            Field errorViewField = TextInputLayout.class.getDeclaredField("mErrorView");
            errorViewField.setAccessible(true);
            TextView errorView = (TextView) errorViewField.get(this);
            if (errorView != null) {
                errorView.setGravity(Gravity.RIGHT);
                LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
                params.gravity = Gravity.END;
                errorView.setLayoutParams(params);
            }
        }
        catch (Exception e) {
            // At least log what went wrong
            e.printStackTrace();
        }
    }
}

Now I simply replaced my TextInputLayout with the CustomTextInputLayout. Works like a charm. Thanks a lot Mike. I wish I could credit you more for this answer.

Dinsdale answered 3/3, 2017 at 12:15 Comment(4)
hi,im trying to show the error message ON the edittext and right aligned corner,any idea how i do that?Criticaster
@ViVekH, can you explain a little more as to what you want?Dinsdale
This doesn't appear to work any longer (or, certainly not on a 3.3.2 / Android 9 setup).Livingstone
That doesn't work for me. I've supportsRtl disabled it in the manifest. Is there any way to do this?Metropolitan
C
5

Here's a hack in Kotlin that doesn't depend on view hierarchy:

class CustomTextInputLayout @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : TextInputLayout(context, attrs, defStyleAttr) {

    override fun setError(errorText: CharSequence?) {
        super.setError(errorText)
        with(findViewById<TextView>(R.id.textinput_error)) {
            // this will work as long as errorView's layout width is
            // MATCH_PARENT -- it is, at least now in material:1.2.0-alpha06
            textAlignment = TextView.TEXT_ALIGNMENT_VIEW_END
        }
    }
}
Chemosh answered 22/5, 2020 at 6:17 Comment(1)
Thank you, it helped me a lot. If someone wants to use this with view binding, just do as follows: with(binding.root.findViewById<TextView>(R.id.textinput_error)) {textAlignment = TextView.TEXT_ALIGNMENT_CENTER}Lian
R
4

My custom class is in Kotlin

class CustomTextInputLayout(context: Context, attrs: AttributeSet) :
    TextInputLayout(context, attrs) {

    override fun setErrorEnabled(enabled: Boolean) {
        super.setErrorEnabled(enabled)

        if (!enabled) {
            return
        }

        try {
            val layout = this
            val errorView : TextView = ((this.getChildAt(1) as ViewGroup).getChildAt(0) as ViewGroup).getChildAt(0) as TextView

            (layout.getChildAt(1) as ViewGroup).layoutParams.width = LayoutParams.MATCH_PARENT
            (layout.getChildAt(1) as ViewGroup).getChildAt(0).layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT

            errorView.gravity = Gravity.END

        } catch (e: Exception) {
            e.printStackTrace()
        }

    }
}
Reserpine answered 25/10, 2018 at 5:53 Comment(1)
This works for now (2019-02-18), but it will stop working when the view hierarchy changes again.Intelligible
P
3

With material-componets version 1.2.0-rc01, you can create a CustomTextInputLayout that inherits from TextInputLayout to change the text alignment to the end (or where you want it to be) like this:

class CustomTextInputLayout : TextInputLayout {

    constructor(context: Context) : super(context)

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttrs: Int
    ) : super(context, attrs, defStyleAttrs)

    override fun setErrorEnabled(enabled: Boolean) {
        super.setErrorEnabled(enabled)
        if (!enabled) {
            return
        }
        try {
            changeTextAlignment(
                com.google.android.material.R.id.textinput_error,
                View.TEXT_ALIGNMENT_VIEW_END
            )
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

   
    private fun changeTextAlignment(textViewId: Int, alignment: Int) {
        val textView = findViewById<TextView>(textViewId)
        textView.textAlignment = alignment
    }
}

The alignment is specified in the IndicatorViewController class to always be View.TEXT_ALIGNMENT_VIEW_START. This class is used by the TextInputLayout.

From the IndicatorViewController class

  void setErrorEnabled(boolean enabled) {
    if (errorEnabled == enabled) {
      return;
    }

    cancelCaptionAnimator();

    if (enabled) {
      errorView = new AppCompatTextView(context);
      errorView.setId(R.id.textinput_error);
      if (VERSION.SDK_INT >= 17) {
        errorView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
      }
//More code...
Piccoloist answered 4/8, 2020 at 16:24 Comment(0)
A
0

If selected answer not works. Another solution is below:

Create custom class as below:

public class CustomTextInputLayout extends TextInputLayout
{

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

@Override
public void setErrorEnabled(boolean enabled)
{
    super.setErrorEnabled(enabled);

    if (!enabled)
    {
        return;
    }

    try
    {
        TextView errorView = this.findViewById(R.id.textinput_error);
        FrameLayout errorViewParent = (FrameLayout) errorView.getParent();
        errorViewParent.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT));
        errorView.setGravity(Gravity.CENTER); // replace CENTER  with END or RIGHT
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}
}

Then create your custom TextInputLayout as:

 <CustomTextInputLayout
    android:id="@+id/til_first_name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="@dimen/activity_horizontal_margin"
    app:layout_constraintBottom_toTopOf="@+id/til_last_name">

    <EditText
        android:id="@+id/et_first_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/first_name"
        android:gravity="right"
        />

</CustomTextInputLayout>

After that initialize in your code as:

private CustomTextInputLayout tilFirstName;
tilFirstName = view.findViewById(R.id.til_first_name);

Congratulations! You have done what you have required.

Aerometry answered 19/7, 2019 at 10:41 Comment(4)
Thats basically textview in which error is shown of textinputlayout.Aerometry
I think the id should be com.google.android.material.R.id.textinput_error, otherwise it will complain R not foundLashondra
Brother I have above working code. Its working fine.Aerometry
Android Studio complains about your code on my end. Unless you import com.google.android.material.R, which you should mention it in the answer.Lashondra
E
0

Based on @Emmanuel Guerra's answer. I've applied it on helperText too:

class CustomTextInputLayout @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = com.google.android.material.R.attr.textInputStyle
) : TextInputLayout(context, attrs, defStyleAttr) {

    override fun setErrorEnabled(enabled: Boolean) {
        super.setErrorEnabled(enabled)
        if (!enabled) return
        centerMiniText(com.google.android.material.R.id.textinput_error)
    }

    override fun setHelperTextEnabled(enabled: Boolean) {
        super.setHelperTextEnabled(enabled)
        if (!enabled) return
        centerMiniText(com.google.android.material.R.id.textinput_helper_text)
    }

    private fun centerMiniText(textViewId: Int) {
        try {
            changeTextAlignment(textViewId, View.TEXT_ALIGNMENT_CENTER)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    @Suppress("SameParameterValue")
    private fun changeTextAlignment(textViewId: Int, alignment: Int) {
        findViewById<TextView>(textViewId)?.textAlignment = alignment
    }

}

Edmonds answered 18/5, 2021 at 7:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.