Android
EditText
Text Length
Android Development
Coding Tips

What's the best way to limit text length of EditText in Android

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If you want to limit how many characters an EditText accepts, the best default answer is to use an input filter, not a custom watcher that tries to repair the text after the fact. In Android, that usually means android:maxLength in XML for fixed limits or InputFilter.LengthFilter in code when the limit is dynamic.

The Best Default: android:maxLength

For a static character limit, use the built-in XML attribute. It is simple, clear, and enforced by the text field itself.

xml
1<EditText
2    android:id="@+id/usernameInput"
3    android:layout_width="match_parent"
4    android:layout_height="wrap_content"
5    android:hint="Username"
6    android:maxLength="20" />

This approach is usually best because:

  • it is declarative and easy to read
  • it works without extra code
  • it prevents excess input rather than reacting afterward

If the limit never changes, this should be your first choice.

Dynamic Limits: Use InputFilter.LengthFilter

If the maximum length depends on runtime conditions, apply a length filter programmatically.

kotlin
1import android.os.Bundle
2import android.text.InputFilter
3import android.widget.EditText
4import androidx.appcompat.app.AppCompatActivity
5
6class MainActivity : AppCompatActivity() {
7    override fun onCreate(savedInstanceState: Bundle?) {
8        super.onCreate(savedInstanceState)
9        setContentView(R.layout.activity_main)
10
11        val input = findViewById<EditText>(R.id.usernameInput)
12        val maxLength = 12
13        input.filters = arrayOf(InputFilter.LengthFilter(maxLength))
14    }
15}

This is still using the platform's intended mechanism. The only difference is that the limit is set from code instead of layout.

Why TextWatcher Is Not the Best Primary Tool

A TextWatcher can observe changes and show feedback, but it is not the ideal enforcement mechanism for length limits.

If you rely on a watcher alone, the user can briefly type past the limit and then have the text trimmed or rejected afterward. That creates a less clean experience and can be harder to maintain, especially when pasted text, selection replacement, and IME behavior are involved.

A watcher is useful for feedback such as:

  • showing remaining characters
  • displaying a helper message when the input is near the limit
  • updating a counter view

But the actual limit should usually still come from an input filter.

Example with Feedback

A good hybrid is filter for enforcement plus a watcher for UI feedback.

kotlin
1import android.os.Bundle
2import android.text.Editable
3import android.text.InputFilter
4import android.text.TextWatcher
5import android.widget.EditText
6import android.widget.TextView
7import androidx.appcompat.app.AppCompatActivity
8
9class MainActivity : AppCompatActivity() {
10    override fun onCreate(savedInstanceState: Bundle?) {
11        super.onCreate(savedInstanceState)
12        setContentView(R.layout.activity_main)
13
14        val input = findViewById<EditText>(R.id.usernameInput)
15        val counter = findViewById<TextView>(R.id.counterText)
16        val maxLength = 20
17
18        input.filters = arrayOf(InputFilter.LengthFilter(maxLength))
19        counter.text = "0 / $maxLength"
20
21        input.addTextChangedListener(object : TextWatcher {
22            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
23            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
24                counter.text = "${s?.length ?: 0} / $maxLength"
25            }
26            override fun afterTextChanged(s: Editable?) = Unit
27        })
28    }
29}

Here the filter enforces the limit, while the watcher keeps the UI informative.

Character Count Versus Byte Count

Be careful about what "length" means in your app. maxLength works on Java CharSequence length, which is usually what you want for user-facing text fields. If your backend limit is actually measured in bytes or storage encoding size, that is a different constraint and should be validated separately.

Do not force byte-oriented logic into the UI field limit unless you genuinely need it.

Common Pitfalls

Using only a TextWatcher to trim over-limit text is the most common mistake. It is reactive and less clean than an input filter.

Assuming the UI limit is enough for backend validation is another mistake. The server or persistence layer should still validate its own constraints.

Forgetting that pasted text also needs correct enforcement can expose bugs in homegrown logic. Built-in filters handle that better.

Finally, if the limit changes dynamically, remember to replace the filters array rather than assuming the old filter updates itself.

Summary

  • for a fixed limit, android:maxLength is the best default solution
  • for a dynamic limit, use InputFilter.LengthFilter
  • use TextWatcher for feedback, not as the main enforcement mechanism
  • keep backend validation separate from UI input limits
  • prefer built-in Android filtering over custom trimming logic whenever possible

Course illustration
Course illustration