17

I've already set the maxLength attribute for an EditText widget in the xml layout file, and it does limit the display of characters.

But it still allows the user to press more letters on the keyboard, and backspacing deletes these extra presses rather than the last letter on the display.

I believe this is not how it's supposed to be. Pressing keys after the limit has been reached should stop accepting more input even if it is not displayed.

Any fix on this?

Update

Based on the answer below, for multiline EditText, add the textMultiLine attribute too.

android:inputType="textFilter|textMultiLine"
Poly Bug
  • 1,514
  • 1
  • 14
  • 27
  • Did you get info on this, why is it happening and how other apps like Whatsapp handing this issue? – Anshul Dec 29 '14 at 05:35
  • I was just typing same question and StackOverflow suggested me yours. The good thing is when you get the value from EditText you get only the visible characters, no matters how much were entered after the limit. The bad thing is in backspacing which affects user experience. – Evgeniy Mishustin Dec 30 '15 at 14:31

4 Answers4

12
  1. Disable auto-suggestions(android:inputType="textFilter" or textNoSuggestions):
  2. Set maxLength

    <EditText
       android:id="@+id/editText"
       android:inputType="textFilter"
       android:maxLength="8"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
    

I hope, it helps

Konstantin Loginov
  • 15,802
  • 5
  • 58
  • 95
2

Unfortunately the following answer does not work. However, it helped us to find out that the bug mentioned by Poly Bug is not reproducible if the input begins with a number.


You can use a TextWatcher to listen for EditText changes and prevent the user from typing too many characters into the field. The following code works for me:

final int maxLength = 10;
EditText editText = (EditText) findViewById(R.id.my_edittext_id);
editText.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {}

    @Override
    public void afterTextChanged(Editable s) {
        if (s.length() >= maxLength) s.delete(maxLength, s.length());
    }
});

In afterTextChanged, the code compares the length of the inserted text with the maximal length maxLength and deletes all superfluous characters.


You might replace all standard EditText classes with the following class to use the listener in conjunction with the maxLength XML attribute:

public class MyMaxLengthEditText extends EditText {
    public static final String XML_NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android";
    private final int mMaxLength;

    public MyMaxLengthEditText(Context context) {
        super(context, null);
        mMaxLength = -1;
    }

    public MyMaxLengthEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        mMaxLength = attrs.getAttributeIntValue(XML_NAMESPACE_ANDROID, "maxLength", -1);
        addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {}

            @Override
            public void afterTextChanged(Editable s) {
                if (s.length() >= mMaxLength) s.delete(mMaxLength, s.length());
            }
        });
    }
}
jmeinke
  • 530
  • 6
  • 27
  • sorry, it does nothing, tried right now. First of all, Editable s doesn't get any values AFTER its maxLength. The problem is at least one level down, somewhere in keyboard buffer. Try to log every input in afterTextChanged and you will see – Evgeniy Mishustin Dec 30 '15 at 15:39
  • 1
    I've used numbers as input for my test case. When I type only numbers it works, e.g. 1234567890. However, if I mix in other characters, e.g. abcde12345 I get the same buggy behaviour you've observed. So yes, the problem seems to lie deeper. Sorry, I didn't test my answer more correctly. – jmeinke Dec 30 '15 at 15:48
  • 1
    OMG, nice catch! IF the input STARTS with a number, no matter what goes on it works as expected, but if it starts with a letter character - it is a bug – Evgeniy Mishustin Dec 30 '15 at 15:51
1

You can use InputFilter

InputFilters can be attached to Editables to constrain the changes that can be made to them.

Xml

    <EditText
   android:id="@+id/edit_Text"
   android:layout_width="match_parent"
   android:layout_height="wrap_content" />

Java Class

int maxLengthofYourEditText = 5;    // Set Your Limit 
edit_TextObj.setFilters(new InputFilter[] {new InputFilter.LengthFilter(maxLengthofYourEditText)});
IntelliJ Amiya
  • 74,896
  • 15
  • 165
  • 198
  • @KonstaninLoginov suggested this yesterday, but this wasn't working(if the input starts with a letter) – Evgeniy Mishustin Dec 31 '15 at 09:30
  • @LuciusHipan Have a look here http://stackoverflow.com/questions/30774123/android-edittext-maxlength-not-working – IntelliJ Amiya Dec 31 '15 at 09:32
  • 4
    maxLength is working. The problem is that if user continues to input characters after maxLength they are not visible but they arrive to some kind of software keyboard buffer and then when user presses backspace it deletes those invisible chars and it is affects user experience. The solution is in answer below. Unfortunately, I am not an author of the question so I cannot accept it – Evgeniy Mishustin Dec 31 '15 at 09:35
  • 2
    @EvgeniyMishustin, please, use links to solutions. If there were more than 3 answers, we couldn't find a working solution. :) I think you mean jmeinke solition. – CoolMind Jan 31 '19 at 09:21
0

I also bumped into this situation when had a series of fields containing 1 symbol (a usual pin-code).

My solution is based on @jmeinke answer.

Create a layout containing several EditTexts with android:maxLength="2" (we will enter 1 symbol in any of them and jump to another EditText. Strange, but on some phones the code below works even for maxLength=1).

class YourTextWatcher(
    private val prevEdit: EditText?,
    private val currEdit: EditText,
    private val nextEdit: EditText?,
    private val method: () -> Unit
) : TextWatcher {
    override fun afterTextChanged(s: Editable?) {
        if (s.isNullOrEmpty()) {
            prevEdit?.requestFocus()
        } else {
            if (s.length > 1) {
                if (currEdit.selectionEnd > 1) {
                    // If stand on second position of EditText and enter new symbol, 
                    // will move to next EditText copying second symbol.
                    val secondSymbol = s.substring(1, 2)
                    nextEdit?.setText(secondSymbol)
                }
                // Remove second symbol.
                s.delete(1, s.length)
            }
            nextEdit?.requestFocus()
            nextEdit?.setSelection(nextEdit.length(), nextEdit.length())
            method()
        }
    }

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
    }

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    }
}

Then for all EditTexts do following:

currEdit.addTextChangedListener(
    YourTextWatcher(prevEdit, currEdit, nextEdit) { yourMethodToValidateText() })

Instead of prevEdit, currEdit, nextEdit set your EditText controls. For instance, for the first:

edit1.addTextChangedListener(
    YourTextWatcher(null, edit1, edit2) { yourMethodToValidateText() })

yourMethodToValidateText() is a method that will check entered symbols. For instance, you can check if all EditTexts are filled.

This solution has a bug when you try to press BackSpace in empty EditText. Nothing happens, you won't go to a previous EditText. You can catch BackSpace in View.OnKeyListener, but it can intersect with TextWatcher. Also I didn't check a situation when copy & paste a text to EditText, forgot to check, what happens, when select a text in EditText and press Delete.

CoolMind
  • 26,736
  • 15
  • 188
  • 224