2

I'm trying to implement a layout where user will input their phone number. The phone number will be 11 digits. And so I wanted to build a text input field with a fixed width that can accommodate 11 characters.

NOTE: I don't want the input field to be wider than 11 characters. Let's say each character is 10px wide. So the input field should be 11*10px = 110px wide (I'm not taking the drawable icon into account here).

Here's what I've tried in XML:

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/textField"
                style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/margin_48"
                app:startIconDrawable="@drawable/ic_phone">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/phone"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:ems="11"
                    android:inputType="phone"
                    android:maxLength="11"
                    android:maxLines="1"
                    android:minEms="11"
                    android:text="01"
                    android:textSize="20sp"
                    android:typeface="monospace" />
            </com.google.android.material.textfield.TextInputLayout>

But this produces an output like the following:

Screenshot of UI

As you can see, despite using ems and minEms, the field is 2 characters wide (it's 0 character wide if I don't add android:text). But I want it to have a width of 11 characters (not more or less). Why is ems not effective here and what's the solution?

ganjaam
  • 1,030
  • 3
  • 17
  • 29
  • 1
    android:layout_width="wrap_content" set this to match parent – Heshan Sandeepa Mar 01 '22 at 07:42
  • @HeshanSandeepa sorry for the ambiguity, but I wanted to create an input field such that it it neither wider than nor smaller than 11 characters. After using the match parent, the TextInputEditText became wider than 11 characters. – ganjaam Mar 01 '22 at 08:30
  • Your own approach should work then. What about the parent view of 'TextInputLayout'. I think the problem is there. – Heshan Sandeepa Mar 01 '22 at 08:40
  • 1
    I think you're underestimating the difficulty of this. The TextView doesn't _necessarily_ know the effective measured size of a single glyph/character. Text measuring is an expensive computation and would greatly vary between fonts, styles, densities, etc. You'd have to [measure the text on a canvas like Android does](https://stackoverflow.com/questions/7549182/android-paint-measuretext-vs-gettextbounds) and then convert `px` to `dp` and resize your TextView accordingly. – Martin Marconcini Mar 01 '22 at 08:54
  • You can achieve it using ConstraintLayout. Add one more TextInputEditText outside the TextInputLayout and make it invisible. and align TextInputLayout on top of it. – Shubham Kathe Mar 01 '22 at 12:47
  • 1
    what you can do add a text view and on its foreground put edittext with width starting from that views start to end it will automatically fill up space – Atif AbbAsi Mar 11 '22 at 10:43

3 Answers3

2

For me the given answers didn't work. But this did:

<com.google.android.material.textfield.TextInputLayout
  ...
  android:maxEms="12"
  minEms="12">

    <com.google.android.material.textfield.TextInputEditText
    ...
    singleLine="true">
</com.google.android.material.textfield.TextInputLayout>

Hamburg is nice
  • 369
  • 4
  • 10
1

Just edit your layout width

from

  android:layout_width="wrap_content"

to

   android:layout_width="match_parent"

I just edited your code

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

         android:id="@+id/textField"
                    
            
            
   style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/margin_48"
                    app:startIconDrawable="@drawable/ic_phone">
    
                    <com.google.android.material.textfield.TextInputEditText
                        android:id="@+id/phone"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:ems="11"
                        android:inputType="phone"
                        android:maxLength="11"
                        android:maxLines="1"
                        android:minEms="11"
                        android:text="01"
                        android:textSize="20sp"
                        android:typeface="monospace" />
                </com.google.android.material.textfield.TextInputLayout>
Anand
  • 4,355
  • 2
  • 35
  • 45
  • pardon the ambiguity, but I actually wanted an input field which will not be larger, nor smaller than the total width of 11 characters. Because of using match parent, the field becomes wider than 11 characters. – ganjaam Mar 01 '22 at 08:32
1

You should set a 11 digits monospace text in your TextInputEditText widget (for example 12345678901), then define a ViewTreeObserver.OnGlobalLayoutListener in your fragment, that calculates the width when the layout is available to be measured.

You could instantiate the observer in onResume() of your fragment, and remove it in onPause(). You should also store the width during the rotation of the activity.

Here below a full working example:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    ConstraintLayout constraintLayout;
    TextInputEditText textInputEditText;
    int viewWidth;

    ViewTreeObserver.OnGlobalLayoutListener viewTreeObserver = new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                constraintLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            } else {
                constraintLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
            viewWidth = textInputEditText.getMeasuredWidth();
            textInputEditText.setWidth(viewWidth);
            textInputEditText.setText("");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState != null) {
            viewWidth = savedInstanceState.getInt("viewWidth", 0);
        }

        constraintLayout = findViewById(R.id.id_constraintlayout);
        textInputEditText = findViewById(R.id.phone);
    }

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putInt("viewWidth", viewWidth);
    }

    @Override
    public void onResume() {
        super.onResume();
        if (viewWidth == 0) {
            textInputEditText.setText("12345678901");
            ViewTreeObserver vto = constraintLayout.getViewTreeObserver();
            vto.addOnGlobalLayoutListener(viewTreeObserver);
        } else textInputEditText.setWidth(viewWidth);
    }

    @Override
    public void onPause() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
            constraintLayout.getViewTreeObserver().removeGlobalOnLayoutListener(viewTreeObserver);
        } else {
            constraintLayout.getViewTreeObserver().removeOnGlobalLayoutListener(viewTreeObserver);
        }
        super.onPause();
    }
}

activity_main.xml:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/id_constraintlayout"
    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"
    tools:context=".MainActivity">

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/textField"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:startIconDrawable="@drawable/ic_phone">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/phone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ems="11"
            android:inputType="phone"
            android:maxLength="11"
            android:maxLines="1"
            android:minEms="11"
            android:textSize="20sp"
            android:typeface="monospace" />
    </com.google.android.material.textfield.TextInputLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

I enclose an image of the result here below, where I already typed 6 digits:

TextInputEditText lenghted 11 digits

Graziano
  • 313
  • 1
  • 4
  • 11