TextInputLayout hint overlap issue

Finally found the adequate explanation of the issue:

Well it turns out that there was a performance optimization added to the framework in Android 4.0 which allowed your view hierarchy only one single draw pass before the Activity animation was started. Once the Activity animation has ended, your view hierarchy is drawn every ~16ms as you expect.

Read more: https://medium.com/@chrisbanes

TLDR: it is platform limitation and this behavior will occur on older versions (Marshmallow and lower).

On Nougat animation will run as expected without the lag.


I came up with a cheap workaround for this and another bug.

Subclass the TextInputLayout
See code for addView()
If you have text set in the text view when it is inflated it will set the hint to collapsed and prevent an animation. This code performs a workaround that will temporarily set text until the state is set during setup. As a bonus there is code that makes sure the hint gets drawn just in case there is only one layout pass.

    public class TextInputLayout extends android.support.design.widget.TextInputLayout {

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

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

    @SuppressLint("DrawAllocation")
    @Override
    protected void onLayout(final boolean changed, final int left, final int top, final int right, final int bottom) {
        if (ViewCompat.isLaidOut(this)) {
            super.onLayout(changed, left, top, right, bottom);
        } else {
            // Workaround for this terrible logic where onLayout gets called before the view is flagged as laid out.
            // The normal TextInputLayout is depending on isLaidOut when onLayout is called and failing the check which prevents initial drawing
            // If there are multiple layout passes this doesn't get broken
            post(new Runnable() {
                @SuppressLint("WrongCall")
                @Override
                public void run() {
                    TextInputLayout.super.onLayout(changed, left, top, right, bottom);
                }
            });
        }
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if (child instanceof EditText) {
            EditText editText = (EditText) child;
            if (StringUtils.isEmpty(editText.getText().toString())) {
                editText.setText("  "); // Set filler text so the initial state of the floating title is to be collapsed
                super.addView(child, index, params);
                editText.setText(""); // Set back to blank to cause the hint to animate in just in case the user sets text
                // This prevents the hint from being drawn over text that is set programmatically before the state is determined
                return;
            }
        }
        super.addView(child, index, params);
    }
}

The workaround that worked for me was to update activity like this:

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    textInputLayout.setHintAnimationEnabled(false);
    textInput.setText("sample");
    textInputLayout.setHintAnimationEnabled(true);
    ...
}