invalidate() — متدی از کلاس View در Android است که ویو را به عنوان نیازمند ترسیم مجدد علامتگذاری میکند. فراخوانی invalidate() منجر به ترسیم مجدد ویو در نزدیکترین چرخه بهروزرسانی صفحه میشود و آن را به مکانیزم اصلی بهروزرسانی وضعیت بصری کامپوننتهای سفارشی تبدیل میکند. بر اساس Android Developers Documentation (2025)، invalidate() در 90% Viewهای سفارشی برای همگامسازی تغییرات دادهها با نمایش روی صفحه استفاده میشود. این متد به صورت ناهمگام کار میکند — فقط پرچم dirty را تنظیم کرده و بلافاصله کنترل را برمیگرداند.
نکات اصلی
invalidate() — متدی از کلاس android.view.View است که به سیستم Android اطلاع میدهد نمایش بصری ویو منسوخ شده است. پس از فراخوانی متد، سیستم ویو را به عنوان dirty علامتگذاری کرده و ترسیم مجدد آن را در نزدیکترین چرخه بهروزرسانی صفحه برنامهریزی میکند (معمولاً 16 ms برای 60 FPS).
متد invalidate() اشکال مختلفی دارد: بدون پارامتر (ترسیم مجدد کامل)، با پارامتر Rect (جزئی) و با پارامترهای ltrb (left, top, right, bottom). همه نسخهها به صورت ناهمگام کار میکنند و میتوانند از رشته UI فراخوانی شوند. برای فراخوانی از رشتههای پسزمینه postInvalidate() وجود دارد.
مکانیزم ترسیم مجدد در Android بر اساس ViewRootImpl است — کامپوننت داخلی که View Hierarchy را به Surface برای ترسیم متصل میکند. هنگامی که invalidate() فراخوانی میشود، ViewRootImpl ناحیه ویو را به عنوان dirty علامتگذاری کرده و از طریق Choreographer — سرویس سیستمی که ترسیم را با فرکانس بهروزرسانی صفحه همگام میکند — درخواست ترسیم مجدد ارسال میکند.
Choreographer سیگنال Vsync را دریافت کرده و یک گذر سهگانه را اجرا میکند: measure، layout، draw. با این حال invalidate() فقط روی فاز draw تأثیر میگذارد — فازهای measure و layout اجرا نمیشوند مگر اینکه requestLayout() فراخوانی شده باشد. این تفاوت کلیدی است: invalidate() سبکتر از requestLayout() است زیرا هندسه را دوباره محاسبه نمیکند.
class CustomChartView(context: Context, attrs: AttributeSet?)
: View(context, attrs) {
private var dataPoints: List<Float> = emptyList()
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
fun updateData(newPoints: List<Float>) {
dataPoints = newPoints
invalidate() // درخواست ترسیم مجدد
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
paint.color = Color.BLUE
paint.strokeWidth = 4f
paint.style = Paint.Style.STROKE
// رسم خط نمودار
val path = Path()
dataPoints.forEachIndexed { index, value ->
val x = index * width / max(dataPoints.size - 1, 1)
val y = height - value * height
if (index == 0) path.moveTo(x, y)
else path.lineTo(x, y)
}
canvas.drawPath(path, paint)
}
}
در این مثال، یک View سفارشی برای ترسیم نمودار هنگام بهروزرسانی دادهها invalidate() را فراخوانی میکند. سیستم فقط این View را دوباره ترسیم میکند و بر سایر عناصر سلسلهمراتب تأثیر نمیگذارد. onDraw() Canvas را برای ترسیم خطوط از طریق Path دریافت میکند.
تفاوت اصلی بین invalidate() و postInvalidate() در ایمنی رشته است. invalidate() باید فقط از رشته UI (main thread) فراخوانی شود. postInvalidate() را میتوان از هر رشتهای فراخوانی کرد — از طریق Handler درخواست ترسیم مجدد را به رشته UI ارسال میکند.
| ویژگی | invalidate() | postInvalidate() |
|---|---|---|
| رشته فراخوانی | رشته UI (رشته اصلی) | هر رشتهای |
| مکانیزم | بهروزرسانی مستقیم dirty flag | از طریق Handler.post() به رشته UI |
| تأخیر | حداقل، در چرخه جاری | تا چرخه بعدی رشته UI |
| عملکرد | بالا | سربار کوچک روی Handler |
| توصیه | برای رشته UI همیشه invalidate() | فقط برای رشتههای پسزمینه |
در عمل، postInvalidate() در سناریوهای بارگذاری داده از شبکه، پردازش نتایج سنسورها یا محاسبات پسزمینه استفاده میشود. اگر در رشته UI هستید — همیشه برای حداقل تأخیر از invalidate() استفاده کنید.
// فراخوانی شده از رشته UI
view.invalidate()
// فراخوانی شده از رشته پسزمینه
Thread {
// محاسبات سنگین
val result = performHeavyCalculation()
runOnUiThread {
updateUi(result)
}
}.start()
invalidate(Rect) و invalidate(int l, int t, int r, int b) امکان محدود کردن ناحیه ترسیم مجدد را فراهم میکنند. این برای عملکرد بسیار مهم است: هنگام بهروزرسانی فقط بخشی از View (مثلاً حرکت مکاننما، تغییر نشانگر) ترسیم مجدد کل ویو بیمعنی است.
سیستم مستطیل dirty مشخص شده را از طریق canvas.clipBounds به onDraw() منتقل میکند. در داخل onDraw() میتوان clipBounds را بررسی کرد و فقط در محدوده این ناحیه ترسیم کرد، اگرچه Android Canvas به طور خودکار ترسیم را خارج از مستطیل dirty محدود میکند.
// بهروزرسانی جزئی: فقط ناحیه مکاننما
private val cursorRect = Rect()
fun moveCursorTo(newX: Int, newY: Int) {
// موقعیت قدیمی را نامعتبر کن
invalidate(cursorRect)
cursorRect.set(newX - 5, newY - 5,
newX + 5, newY + 5)
// موقعیت جدید را نامعتبر کن
invalidate(cursorRect)
}
بدون ترسیم مجدد جزئی، هر حرکت مکاننما کل View را دوباره ترسیم میکرد که برای یک نمودار بزرگ به معنای ترسیم مجدد هزاران پیکسل به جای چند ده عدد است. invalidate(Rect) — یک تکنیک اجباری برای ویرایشگرها، بومهای نقاشی و کامپوننتهای متحرک است.
یکی از اشتباهات رایج فراخوانی requestLayout() جایی است که invalidate() کافی است، و برعکس. تفاوت اساسی است: invalidate() فقط روی فاز draw تأثیر میگذارد، در حالی که requestLayout() چرخه کامل measure → layout → draw را اجرا میکند.
| جنبه | invalidate() | requestLayout() |
|---|---|---|
| فازهای چرخه | فقط draw | measure + layout + draw |
| کی استفاده کنیم | فقط ترسیم تغییر میکند (رنگ، متن، گرافیک) | اندازه یا موقعیت ویو تغییر میکند |
| عملکرد | سبک — فقط ترسیم مجدد | سنگین — محاسبه مجدد سلسلهمراتب |
| تأثیر بر سلسلهمراتب | فقط ویو جاری | میتواند بر کانتینرهای والد تأثیر بگذارد |
اگر متن را در TextView تغییر میدهید — invalidate() کافی است، زیرا اندازه ویو تغییر نمیکند. اگر متن ممکن است به خط جدید منتقل شده و ارتفاع را افزایش دهد — requestLayout() نیاز است. Android Lint از طریق قوانین عملکرد به ردیابی چنین خطاهایی کمک میکند.
فراخوانیهای بیش از حد invalidate() — یکی از دلایل اصلی عملکرد پایین Viewهای سفارشی در Android است. تکنیکهای بهینهسازی را بررسی میکنیم.
اگر دادهها با فرکانس بالا بهروزرسانی میشوند (سنسورها، انیمیشن، ویدیو)، در هر تغییر invalidate() را فراخوانی نکنید. برای همگامسازی با فرکانس بهروزرسانی صفحه از ValueAnimator یا Choreographer.FrameCallback استفاده کنید. این تضمین میکند که invalidate() بیش از یک بار در هر فریم فراخوانی نمیشود.
از API 14 Android شتاب سختافزاری را از طریق GPU پشتیبانی میکند. اگر View سفارشی شما فقط از Canvas API (drawRect، drawCircle، drawPath) استفاده میکند، شتاب به صورت شفاف کار میکند. برای عملیات سازگار با DisplayList، invalidate() به طور قابل توجهی سریعتر پردازش میشود.
// استفاده از Choreographer برای همگامسازی Vsync
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
updateAnimation(frameTimeNanos)
invalidate()
Choreographer.getInstance().postFrameCallback(this)
}
fun startAnimation() {
Choreographer.getInstance().postFrameCallback(frameCallback)
}
برای بهروزرسانیهای نقطهای از invalidate(Rect) استفاده کنید، از فراخوانی invalidate() از داخل onDraw() خودداری کنید (حلقه بینهایت) و همیشه با GPU Profile Rendering روی دستگاه پروفایل بگیرید. این زمان دقیق ترسیم هر فریم را نشان داده و به شناسایی نقاط مشکلدار کمک میکند.
سوالات متداول
خیر، فراخوانی invalidate() در داخل onDraw() یک حلقه بینهایت ترسیم مجدد ایجاد میکند: onDraw() invalidate() را فراخوانی میکند که دوباره onDraw() را اجرا میکند. این منجر به 100% بار CPU و افت فریم میشود. از انیمیشنها از طریق ValueAnimator یا Choreographer استفاده کنید.
invalidate() فقط در رشته UI کار میکند و dirty flag را فوراً بهروزرسانی میکند. postInvalidate() درخواست را از طریق Handler به رشته UI ارسال میکند و میتواند از هر رشته پسزمینهای فراخوانی شود. اگر در رشته UI هستید — برای حداقل تأخیر از invalidate() استفاده کنید.
بله، در داخل TextView متد setText() پس از بهروزرسانی متن invalidate() را فراخوانی میکند. اگر متن ابعاد ویو را تغییر داده باشد، به طور اضافی requestLayout() فراخوانی میشود. توسعهدهنده نیازی به فراخوانی دستی invalidate() هنگام کار با ویجتهای استاندارد ندارد.
هر فراخوانی invalidate() ترسیم مجدد را در Vsync بعدی (هر 16 ms) برنامهریزی میکند. اگر onDraw() بیشتر از 16 ms طول بکشد، افت فریم رخ میدهد. onDraw() را بهینه کنید — Bitmap را کش کنید، از تخصیص حافظه خودداری کنید و برای ترسیم GPU از Hardware Acceleration استفاده کنید.
بله، پس از تغییر ویژگیهای Paint (رنگ، ضخامت، سبک) باید invalidate() فراخوانی شود، زیرا View تغییرات اشیاء Paint را به طور خودکار ردیابی نمیکند. سیستم نمیداند که Paint تغییر کرده است و بدون درخواست صریح onDraw() را فراخوانی نمیکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید