9

Is it possible to make the TextInputLayout label to show above the left drawable of an EditText perpendicularly when user focuses or types in the EditText.

Here is the xml of the EditText:

 <android.support.design.widget.TextInputLayout
                android:id="@+id/completion_date_layout2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:layout_marginRight="5dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <EditText
                    android:id="@+id/etTaskDate"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:hint="@string/title_completion_date"
                    android:inputType="text"
                    android:drawableLeft="@drawable/ic_date"
                    android:paddingBottom="15dp"
                    android:drawablePadding="5dp"
                    android:textSize="@dimen/fields_text_size"/>
            </android.support.design.widget.TextInputLayout>

Here is the desired output:

enter image description here

Here is the output that I am getting:

enter image description here

icaneatclouds
  • 1,170
  • 9
  • 18

4 Answers4

8

Thanks to Java's member access model and Google's developers who left a small loophole it could be achieved with a simple subclassing which repeats a minimum of the original code:

package android.support.design.widget;

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;

public final class TextInputLayoutEx extends TextInputLayout {

    private final int mDefaultPadding = __4DP__;
    private final Rect mTmpRect = new Rect();

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

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (isHintEnabled() && mEditText != null) {
            final Rect rect = mTmpRect;
            ViewGroupUtils.getDescendantRect(this, mEditText, rect);
            mCollapsingTextHelper.setCollapsedBounds(
                rect.left + mDefaultPadding, getPaddingTop(),
                rect.right - mDefaultPadding, bottom - top - getPaddingBottom());
            mCollapsingTextHelper.recalculate();
        }
    }

}

Here we put a new class to the same package which opens an access to the mCollapsingTextHelper with a package-level visibility and then repeat part of the code from the original onLayout method which manages field name positioning. The __4DP__ value is 4dp value converted to pixels, I'm pretty sure everyone has an utility method for this.

In your xml layout just switch from the android.support.design.widget.TextInputLayout to android.support.design.widget.TextInputLayoutEx so your layout looks like this:

<android.support.design.widget.TextInputLayoutEx
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="Mobile">
    <android.support.design.widget.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:drawableLeft="@drawable/ic_phone_black_24dp"
        android:drawablePadding="4dp"/>
</android.support.design.widget.TextInputLayoutEx>

And the result is

Collapsed and expanded state

At the moment it works for com.android.support:design:25.3.1

Mikhail
  • 568
  • 1
  • 6
  • 22
  • Will give this a try on another project. Thanks – icaneatclouds Aug 07 '17 at 01:55
  • EPIC, worked like a charm. @icaneatclouds consider to mark this code as `the answer`. – MiguelCatalan Dec 22 '17 at 13:37
  • Very smart solution! The only problem is that in some cases the cutout is not correct. For example if the label is in correct position and then we open the keyboard for another edittext. The workaround is to add as a first line in the onLayout method the following ``if (!changed) return`` but obviously this has consequences – Vaios Jun 15 '19 at 15:59
  • what is mCollapsingTextHelper and mEditText in the above example? – Auxano Services Nov 05 '19 at 10:02
  • 2
    @AuxanoServices they are package private fields from the base class. With migration to AndroidX they've changed their names to `collapsingTextHelper` and `editText`. – Mikhail Nov 06 '19 at 13:18
0

I have made a workaround regarding this issue. It may be a bit unreliable but it works on my end. Here is the code:

String spacer="        ";
EditText myEditText= (EditText)findViewById(R.id.myEditText);
myEditText.setText(spacer + "Today");
myEditText.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_calendar, 0, 0, 0);

What I did here was placing a spacer before the text inside the editText, then add the drawable left programatically.

But make sure to remove those spaces before fetching the contents of the editText:

String title = myEditText.getText().toString().substring(8);

This means i cropped the 8 spaces before the word "Today".

icaneatclouds
  • 1,170
  • 9
  • 18
0

You can use animation and frame_layout to animate the left icon, try this link, may be helpful for you.

Community
  • 1
  • 1
Bajrang Hudda
  • 3,028
  • 1
  • 36
  • 63
0

You can try this custom class: find here

and then just change in xml

com.mycompany.myapp.CustomTextInputLayout

import android.content.Context;
import android.graphics.Rect;
import android.support.design.widget.TextInputLayout;

import android.util.AttributeSet;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class CustomTextInputLayout extends TextInputLayout {
    private Object collapsingTextHelper;
    private Rect bounds;
    private Method recalculateMethod;

    public CustomTextInputLayout(Context context) {
        this(context, null);
    }

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

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

        init();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        adjustBounds();
    }

    private void init() {
        try {
            Field cthField = TextInputLayout.class.getDeclaredField("mCollapsingTextHelper");
            cthField.setAccessible(true);
            collapsingTextHelper = cthField.get(this);

            Field boundsField = collapsingTextHelper.getClass().getDeclaredField("mCollapsedBounds");
            boundsField.setAccessible(true);
            bounds = (Rect) boundsField.get(collapsingTextHelper);

            recalculateMethod = collapsingTextHelper.getClass().getDeclaredMethod("recalculate");
        }
        catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
            collapsingTextHelper = null;
            bounds = null;
            recalculateMethod = null;
            e.printStackTrace();
        }
    }

    private void adjustBounds() {
        if (collapsingTextHelper == null) {
            return;
        }

        try {
            bounds.left = getEditText().getLeft() + getEditText().getPaddingLeft();
            recalculateMethod.invoke(collapsingTextHelper);
        }
        catch (InvocationTargetException | IllegalAccessException | IllegalArgumentException e) {
            e.printStackTrace();
        }
    }
}
Ritesh Bhavsar
  • 1,343
  • 1
  • 9
  • 19
  • This works, but only without proguard, any ideas on how to exclude this from proguard? – Tim Oct 03 '18 at 14:36
  • 2
    Please don't just copy someone else's answer. If you believe a question has already been solved on another post, then please flag the current question as a duplicate, or leave a link in a comment. – Mike M. Dec 13 '18 at 10:04