TextWatcher: چیست، رابط TextWatcher و پیاده‌سازی در اندروید

نویسنده: IT Sectr منتشر شده: 2026-07-08 زمان مطالعه: 6 دقیقه

TextWatcher یک رابط اندروید است که به شما امکان می‌دهد تغییرات متن را در EditText و سایر TextViewها در زمان واقعی ردیابی کنید. توسعه‌دهنده در سه مرحله اعلان دریافت می‌کند: قبل از تغییر، در حین تغییر و پس از تغییر محتوای متنی. طبق Android Developers, 2026، TextWatcher در اکثر برنامه‌ها برای اعتبارسنجی ورودی، شمارش کاراکترها، پیاده‌سازی جستجو با تکمیل خودکار و قالب‌بندی پویای متن استفاده می‌شود. این رابط در فرم‌هایی که نیاز به واکنش فوری به هر فشردن کلید دارند ضروری است.

نکات اصلی

  • TextWatcher — رابط داخلی Android SDK برای گوش دادن به تغییرات متن در TextView و EditText.
  • رابط شامل سه متد است: beforeTextChanged، onTextChanged و afterTextChanged، هر کدام مسئول مرحله خود از تغییر هستند.
  • متد afterTextChanged برای اعتبارسنجی فیلد پس از اتمام ورود کاربر مناسب‌ترین است.
  • فراخوانی بازگشتی — خطای رایج: تغییر متن در داخل TextWatcher منجر به حلقه بی‌نهایت می‌شود.
  • TextWatcher در فیلدهای جستجو، اعتبارسنجی فرم‌ها، شمارش کاراکترها و قالب‌بندی خودکار شماره تلفن استفاده می‌شود.

TextWatcher چیست و به چه دردی می‌خورد؟

TextWatcher — یک رابط از بسته android.text است که برنامه را از تغییرات متن در اشیاء Editable مطلع می‌کند. با هر بار ورود، حذف یا جایگزینی کاراکتر، TextWatcher به ترتیب سه متد را فراخوانی می‌کند و اطلاعات مربوط به موقعیت تغییرات را منتقل می‌کند. این به توسعه‌دهنده اجازه می‌دهد فوراً — بدون دکمه‌های اضافی یا محرک‌ها — به اقدامات کاربر واکنش نشان دهد.

سناریوهای اصلی استفاده شامل اعتبارسنجی فیلدها در زمان واقعی است: بررسی ایمیل با هر کاراکتر وارد شده، شمارش کاراکترهای باقی‌مانده در فیلد با محدودیت طول، پیاده‌سازی جستجو با ارسال تأخیری درخواست از طریق debounce. همچنین TextWatcher برای قالب‌بندی ورودی استفاده می‌شود — مثلاً قرار دادن خودکار فاصله در شماره تلفن یا افزودن ماسک برای تاریخ.

طبق Android Developers، TextWatcher در 70٪ از برنامه‌هایی که با فرم‌ها کار می‌کنند وجود دارد. کتابخانه‌هایی مانند Material Design Components و TextInputEditText به صورت داخلی از TextWatcher برای مدیریت حالت خطا و نمایش شمارنده‌ها استفاده می‌کنند. درک نحوه کار این رابط برای هر توسعه‌دهنده اندروید ضروری است.

رابط TextWatcher چگونه کار می‌کند

TextWatcher از طریق متد addTextChangedListener به هر شیء TextView یا EditText متصل می‌شود. هنگامی که کاربر کاراکتری وارد یا حذف می‌کند، اندروید ابتدا beforeTextChanged، سپس onTextChanged و در نهایت afterTextChanged را فراخوانی می‌کند. در پارامترهای هر متد داده‌هایی درباره محدوده در حال تغییر منتقل می‌شود: موقعیت شروع، تعداد کاراکترهای حذف شده و تعداد کاراکترهای اضافه شده.

درک این نکته مهم است که پس از فراخوانی afterTextChanged، شیء Editable از قبل حاوی مقدار فعلی است. بنابراین بررسی متن نهایی فیلد در afterTextChanged راحت است. تا این لحظه داده‌ها هنوز به طور کامل به‌روز نشده‌اند. توسعه‌دهندگان اغلب کاربرد متدها را اشتباه می‌گیرند و از onTextChanged برای اعتبارسنجی نهایی استفاده می‌کنند، در حالی که انتخاب درست afterTextChanged است.

ویژگی‌های فراخوانی متدها

با هر بار درج، جایگزینی یا حذف کاراکتر، زنجیره فراخوانی تضمیناً به طور کامل اجرا می‌شود. با این حال، اگر در داخل afterTextChanged متن تغییر کند (از طریق clear، append، insert)، TextWatcher به صورت بازگشتی فعال می‌شود. این شایع‌ترین علت StackOverflowError در فرم‌های اندروید است. برای جلوگیری از بازگشت از پرچم مسدودکننده استفاده می‌شود.

سه متد TextWatcher: beforeTextChanged، onTextChanged، afterTextChanged

هر یک از سه متد نقش خود را در چرخه حیات تغییر متن ایفا می‌کند. متد beforeTextChanged(CharSequence s, int start, int count, int after) قبل از اعمال تغییرات فراخوانی می‌شود. این متد وضعیت فعلی رشته، موقعیت شروع تغییر، تعداد کاراکترهای در حال حذف و تعداد کاراکترهای در حال افزودن را منتقل می‌کند. در اینجا می‌توان مقدار قبلی را ذخیره کرد یا شرایط را قبل از تغییر بررسی نمود.

متد onTextChanged در حین تغییر فراخوانی می‌شود، زمانی که کاراکترها حذف شده‌اند اما کاراکترهای جدید هنوز وارد نشده‌اند. پارامترها: متن پس از حذف، موقعیت شروع، تعداد کاراکترهای حذف شده و تعداد کاراکترهای در حال افزودن. این متد برای انیمیشن یا لاگ‌گیری مناسب است، اما برای کار با متن نهایی فعلی مناسب نیست — هنوز تکمیل نشده است.

متد afterTextChanged — پرکاربردترین است. این متد شیء Editable را دریافت می‌کند و پس از اعمال کامل تغییرات فراخوانی می‌شود. در این متد می‌توان مقدار نهایی فیلد را خواند، اعتبارسنجی انجام داد، UI را به‌روز کرد و متن را تغییر داد (با احتیاط به دلیل بازگشت).

مثال پیاده‌سازی TextWatcher برای شمارش کاراکترها

مثال عملی — شمارنده کاراکترها برای فیلد ورودی که با هر تغییر متن به‌روز می‌شود. چنین عنصری اغلب در فرم‌های تماس، پست‌ها و پیام‌های با محدودیت طول دیده می‌شود. پیاده‌سازی از طریق TextWatcher چند خط کد نیاز دارد و به کتابخانه‌های شخص ثالث نیاز ندارد.

kotlin
val editText = findViewById<EditText>(R.id.edit_text)
val counterText = findViewById<TextView>(R.id.counter)

editText.addTextChangedListener(object : TextWatcher {
    override fun beforeTextChanged(
        s: CharSequence?, start: Int,
        count: Int, after: Int
    ) {}

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

    override fun afterTextChanged(s: Editable?) {
        val len = s?.length ?: 0
        counterText.text = "$len / 200"
    }
})

در مثال، متد afterTextChanged محتوای فعلی فیلد را از طریق پارامتر s از نوع Editable دریافت می‌کند. طول متن در یک TextView جداگانه به‌روز می‌شود. برای جلوگیری از بازگشت در این مورد، فقط counterText تغییر می‌کند، نه خود EditText، بنابراین حلقه ایجاد نمی‌شود. با محدودیت 200 کاراکتر، می‌توان پس از تجاوز از حد، ورود را مسدود کرد.

متدهای beforeTextChanged و onTextChanged خالی می‌مانند، زیرا برای شمارش طول، وضعیت نهایی کافی است. اگر نیاز به لاگ‌گیری هر تغییر باشد، می‌توان کد را به onTextChanged اضافه کرد. چنین انعطاف‌پذیری TextWatcher را به ابزاری جهانی برای هر سناریوی کار با ورود متن تبدیل می‌کند.

TextWatcher برای اعتبارسنجی فیلدها در زمان واقعی

اعتبارسنجی در زمان واقعی به طور قابل توجهی UX را بهبود می‌بخشد: کاربر بلافاصله پس از وارد کردن مقدار نادرست خطا را می‌بیند، نه پس از فشار دادن دکمه ارسال. TextWatcher بررسی فوری ایمیل، رمز عبور، شماره تلفن و سایر فیلدها را امکان‌پذیر می‌کند. نتیجه از طریق setError در EditText یا از طریق یک TextView جداگانه با پیام خطا نمایش داده می‌شود.

kotlin
fun validateEmail(emailEditText: EditText) {
    emailEditText.addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {
            val email = s?.toString () ?: ""
            if (email.isNotBlank() &&
                !Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
                emailEditText.error = "Invalid email address"
            } else {
                emailEditText.error = null
            }
        }

        override fun beforeTextChanged(...) {}
        override fun onTextChanged(...) {}
    })
}

در مثال از Patterns.EMAIL_ADDRESS داخلی از Android SDK برای بررسی ایمیل استفاده شده است. اگر متن خالی نباشد و با الگو مطابقت نداشته باشد، از طریق ویژگی error خطا به فیلد تنظیم می‌شود. با ورود صحیح، خطا پاک می‌شود. مهم است که اعتبارسنجی را روی فیلد خالی اجرا نکنید — کاربر ممکن است هنوز وارد کردن را شروع نکرده باشد و پیام خطا زودهنگام خواهد بود.

برای رمزهای عبور و شماره تلفن‌ها از عبارات منظم سفارشی یا کتابخانه‌های تخصصی استفاده می‌شود. مثلاً برای بررسی پیچیدگی رمز عبور می‌توان تعداد ارقام، حروف بزرگ و کوچک را شمارش کرد. TextWatcher به‌روزرسانی نشانگر پیچیدگی رمز عبور را در زمان واقعی امکان‌پذیر می‌کند که بر نرخ تبدیل ثبت‌نام تأثیر مثبت دارد.

خطاهای معمول هنگام کار با TextWatcher

اولین و بحرانی‌ترین خطا — فراخوانی بازگشتی. اگر در داخل afterTextChanged متن همان EditText تغییر کند (از طریق s.clear()، s.append() یا s.insert())، TextWatcher دوباره فعال می‌شود. این یک حلقه بی‌نهایت ایجاد می‌کند که به StackOverflowError ختم می‌شود. راه‌حل — استفاده از پرچم مسدودکننده isUpdating یا بررسی اینکه آیا متن واقعاً تغییر کرده است.

دومین مشکل رایج — نشت حافظه. TextWatcher از طریق کلاس ناشناس یک ارجاع ضمنی به Activity یا Fragment دارد. اگر listener هنگام نابودی View حذف نشود، garbage collector نمی‌تواند حافظه را آزاد کند. راه‌حل — استفاده از کامپوننت‌های چرخه حیات یا فراخوانی صریح removeTextChangedListener در onDestroyView.

سومین خطا — استفاده از متد اشتباه. برخی توسعه‌دهندگان اعتبارسنجی نهایی را در onTextChanged انجام می‌دهند و منتظر afterTextChanged نمی‌مانند. در onTextChanged متن هنوز به طور کامل به‌روز نشده است و خواندن مقدار نهایی ممکن است داده‌های نادرست برگرداند. رویکرد درست — قرار دادن تمام منطق خواندن و بررسی متن نهایی در afterTextChanged.

متدزمان فراخوانیکاربردآیا می‌توان متن نهایی را خواند؟
beforeTextChangedقبل از تغییرذخیره وضعیت قبلیبله
onTextChangedدر حین تغییرلاگ‌گیری، انیمیشنخیر
afterTextChangedپس از تغییراعتبارسنجی، شمارش، به‌روزرسانی UIبله

چهارمین خطا — افزودن چندباره TextWatcher. اگر addTextChangedListener چندین بار برای یک EditText فراخوانی شده باشد، همه listenerها همان تغییر را پردازش خواهند کرد. در فرم‌هایی با افزودن پویای View، این منجر به تکراری شدن بررسی‌ها و رفتار غیرقابل پیش‌بینی می‌شود. همیشه بررسی کنید که آیا listener قبلاً اضافه شده است یا از یک نمونه واحد استفاده کنید.

سوالات متداول

تفاوت onTextChanged و afterTextChanged چیست؟

OnTextChanged در لحظه تغییر متن فراخوانی می‌شود، زمانی که کاراکترهای جدید هنوز اضافه نشده‌اند. این متد برای انیمیشن و لاگ‌گیری مناسب است. AfterTextChanged پس از اعمال کامل تغییرات فراخوانی می‌شود و از طریق پارامتر Editable به متن نهایی دسترسی می‌دهد. برای اعتبارسنجی و خواندن مقدار از afterTextChanged استفاده کنید.

چگونه از فراخوانی بازگشتی TextWatcher جلوگیری کنیم؟

از یک پرچم مسدودکننده از نوع Boolean استفاده کنید که قبل از تغییر متن در داخل afterTextChanged روی true تنظیم می‌شود. در ابتدای متد پرچم را بررسی کنید: اگر true است — خارج شوید. به طور جایگزین می‌توان مقدار قدیم و جدید را مقایسه کرد و متن را فقط در صورت تفاوت واقعی تغییر داد.

آیا باید TextWatcher را هنگام نابودی Activity حذف کرد؟

بله، حتماً. کلاس ناشناس TextWatcher از طریق closure یک ارجاع به Activity نگه می‌دارد. اگر listener حذف نشود، Activity نمی‌تواند توسط garbage collector جمع‌آوری شود. همیشه removeTextChangedListener را در onDestroyView برای Fragment یا onDestroy برای Activity فراخوانی کنید.

آیا می‌توان از TextWatcher در RecyclerView استفاده کرد؟

بله، اما با احتیاط. در RecyclerView، ViewHolderها دوباره استفاده می‌شوند و TextWatcher از موقعیت قبلی ممکن است فعال بماند. همیشه TextWatcher قدیمی را قبل از تنظیم TextWatcher جدید در متد onBindViewHolder حذف کنید. از tagها یا فیلدهای جداگانه ViewHolder برای ذخیره ارجاع به listener استفاده کنید.

کدام متد برای جستجو با تکمیل خودکار بهتر است؟

برای فیلد جستجو از afterTextChanged همراه با debounce (تأخیر) استفاده کنید. یک تایمر 300-500 میلی‌ثانیه‌ای پیاده‌سازی کنید که با هر تغییر جدید متن ریست می‌شود. این کار از ارسال درخواست به سرور با هر فشردن کلید جلوگیری می‌کند و بار API را کاهش می‌دهد.

خلاصه

  • TextWatcher — رابط اندروید برای ردیابی تغییرات متن در EditText و TextView که سه متد بازگشتی را پیاده‌سازی می‌کند.
  • متد afterTextChanged — انتخاب بهینه برای اعتبارسنجی و خواندن متن نهایی پس از تغییرات.
  • فراخوانی بازگشتی — خطر اصلی TextWatcher که با پرچم مسدودکننده جلوگیری می‌شود.
  • حذف listener برای جلوگیری از نشت حافظه هنگام نابودی Activity یا Fragment ضروری است.
  • اعتبارسنجی در زمان واقعی با TextWatcher UX را بهبود می‌بخشد و امکان نمایش فوری خطاها را فراهم می‌کند.
  • Debounce برای پیاده‌سازی جستجو و تکمیل خودکار جهت کاهش بار سرور لازم است.
  • انتخاب صحیح متد — کلید کار پایدار: beforeTextChanged برای ذخیره وضعیت، onTextChanged برای لاگ‌ها، afterTextChanged برای بررسی نهایی.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید