Giới thiệu
InputFilter là cơ chế Android để lọc/chỉnh sửa text nhập vào EditText real-time. Phổ biến nhất là InputFilter.LengthFilter giới hạn độ dài, nhưng có thể tạo custom filter cho: chỉ cho phép số, chữ cái, regex, block ký tự đặc biệt, format số điện thoại...
Các bước thực hiện
Bước 1: Tạo dự án mới
Android Studio → File → New Project → Empty View Activity.
Bước 2: Thiết kế giao diện (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Giới hạn 15 ký tự (LengthFilter)"
android:textSize="16sp"
android:layout_marginBottom="8dp" />
<EditText
android:id="@+id/etLimited"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Tối đa 15 ký tự"
android:inputType="text"
android:padding="12dp"
android:background="@drawable/edittext_rounded" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="8dp"
android:text="Chỉ cho phép số (Custom Filter)"
android:textSize="16sp" />
<EditText
android:id="@+id/etNumbersOnly"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Chỉ nhập số"
android:inputType="number"
android:padding="12dp"
android:background="@drawable/edittext_rounded" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="8dp"
android:text="Block ký tự đặc biệt (Regex Filter)"
android:textSize="16sp" />
<EditText
android:id="@+id/etNoSpecialChars"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Không ký tự đặc biệt"
android:inputType="text"
android:padding="12dp"
android:background="@drawable/edittext_rounded" />
</LinearLayout>
Bước 3: Custom InputFilter classes
1. LengthFilter (có sẵn):
// Giới hạn 15 ký tự
editText.filters = arrayOf(InputFilter.LengthFilter(15))
2. Chỉ cho phép số (0-9):
object NumbersOnlyFilter : InputFilter {
override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
val input = source.subSequence(start, end).toString()
return if (input.matches(Regex("^[0-9]*$"))) null else ""
}
}
// Sử dụng
etNumbersOnly.filters = arrayOf(NumbersOnlyFilter, InputFilter.LengthFilter(10))
3. Block ký tự đặc biệt (chỉ cho chữ, số, space):
object NoSpecialCharsFilter : InputFilter {
private val allowedRegex = Regex("^[a-zA-Z0-9\\s]*$")
override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
val input = source.subSequence(start, end).toString()
return if (allowedRegex.matches(input)) null else ""
}
}
4. Chỉ cho phép chữ cái (không số):
object LettersOnlyFilter : InputFilter {
private val allowedRegex = Regex("^[a-zA-Z\\s]*$")
override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
val input = source.subSequence(start, end).toString()
return if (allowedRegex.matches(input)) null else ""
}
}
5. Format số điện thoại (ví dụ: 0xxx-xxx-xxx):
class PhoneNumberFilter : InputFilter {
override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
val currentText = dest.toString()
val newText = currentText.substring(0, dstart) + source.subSequence(start, end) + currentText.substring(dend)
// Chỉ cho phép số và tự động format
val digitsOnly = newText.replace(Regex("[^0-9]"), "")
// Logic format ở đây (ví dụ thêm dấu gạch ngang)
// Trả về null để cho phép, hoặc trả về string đã format
return null // Để EditText tự handle, hoặc xử lý phức tạp hơn
}
}
Bước 4: Áp dụng trong MainActivity.kt
package com.example.inputfilterdemo
import android.os.Bundle
import android.text.InputFilter
import android.text.Spanned
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.inputfilterdemo.databinding.ActivityMainBinding
import java.util.regex.Regex
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 1. LengthFilter - giới hạn 15 ký tự
binding.etLimited.filters = arrayOf(InputFilter.LengthFilter(15))
binding.etLimited.setOnKeyListener { _, _, _ ->
if (binding.etLimited.length() >= 15) {
Toast.makeText(this, "Đã đạt tối đa 15 ký tự", Toast.LENGTH_SHORT).show()
}
false
}
// 2. Chỉ số
binding.etNumbersOnly.filters = arrayOf(NumbersOnlyFilter, InputFilter.LengthFilter(10))
// 3. Không ký tự đặc biệt
binding.etNoSpecialChars.filters = arrayOf(NoSpecialCharsFilter, InputFilter.LengthFilter(50))
}
}
// Custom Filters
object NumbersOnlyFilter : InputFilter {
override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
val input = source.subSequence(start, end).toString()
return if (input.matches(Regex("^[0-9]*$"))) null else ""
}
}
object NoSpecialCharsFilter : InputFilter {
private val allowedRegex = Regex("^[a-zA-Z0-9\\sÀ-ỹà-ỹĐđ]*$") // Hỗ trợ tiếng Việt
override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
val input = source.subSequence(start, end).toString()
return if (allowedRegex.matches(input)) null else ""
}
}
InputFilter vs TextWatcher
| Đặc điểm | InputFilter | TextWatcher |
|---|---|---|
| Thời điểm chạy | Trước khi text thay đổi (pre-filter) | Sau khi text đã thay đổi |
| Có thể sửa text | Có (trả về CharSequence mới) | Có (nhưng dễ gây loop vô tận) |
| Performance | Tốt (chạy trên UI thread, nhanh) | Tốt |
| Dùng cho | Validation, format, block char | React to change, live search, count chars |
XML Attribute: android:maxLength
Đơn giản nhất cho giới hạn độ dài (không cần code):
<EditText
android:maxLength="15"
android:inputType="text" />
- Tự động enforce tại framework level.
- Không cho phép custom logic (chỉ số, regex...).
- Không hiển thị toast warning khi đạt giới hạn.
Lưu ý quan trọng
- InputFilter chạy trên **UI thread** - code trong filter phải **rất nhanh**, tránh regex phức tạp, I/O, network.
- Trả về
null= cho phép input gốc. Trả về""(empty) = block input. Trả về string khác = replace input. - Kết hợp nhiều filter:
editText.filters = arrayOf(filter1, filter2, LengthFilter(10))- chạy theo thứ tự. - Copy/Paste: InputFilter cũng filter nội dung paste vào.
- Emoji: Regex
\\skhông match emoji. Cần handle riêng nếu cho phép/block emoji. - Tiếng Việt: Regex
[a-zA-Z]không bao gồm chữ có dấu. Dùng[\\p{L}](Unicode letter) hoặc rangeÀ-ỹà-ỹĐđ.