invalidate() là một phương thức của lớp View trong Android, đánh dấu một view là cần được vẽ lại. Gọi invalidate() sẽ kích hoạt việc vẽ lại view trong chu kỳ làm mới màn hình tiếp theo, khiến nó trở thành cơ chế chính để cập nhật trạng thái trực quan của các thành phần tùy chỉnh. Theo tài liệu Android Developers (2025), invalidate() được sử dụng trong 90% View tùy chỉnh để đồng bộ hóa thay đổi dữ liệu với hiển thị trên màn hình. Phương thức hoạt động không đồng bộ — nó chỉ đặt cờ dirty và trả lại quyền điều khiển ngay lập tức.
Những điểm chính
invalidate() là một phương thức của lớp android.view.View, thông báo cho hệ thống Android rằng biểu diễn trực quan của một view đã lỗi thời. Sau khi gọi phương thức, hệ thống đánh dấu view là dirty và lên lịch vẽ lại trong chu kỳ làm mới màn hình tiếp theo (thường là 16 ms cho 60 FPS).
Phương thức invalidate() có nhiều dạng: không tham số (vẽ lại toàn bộ), với tham số Rect (một phần) và với tham số ltrb (left, top, right, bottom). Tất cả các phiên bản đều hoạt động không đồng bộ và phải được gọi từ luồng UI. Để gọi từ luồng nền, có postInvalidate().
Cơ chế vẽ lại trong Android dựa trên ViewRootImpl — một thành phần nội bộ kết nối hệ phân cấp View với Surface để vẽ. Khi invalidate() được gọi, ViewRootImpl đánh dấu khu vực view là dirty và gửi yêu cầu vẽ lại qua Choreographer — một dịch vụ hệ thống đồng bộ hóa việc vẽ với tần số làm mới màn hình.
Choreographer nhận tín hiệu từ Vsync và bắt đầu quy trình ba bước: measure, layout, draw. Tuy nhiên, invalidate() chỉ ảnh hưởng đến giai đoạn draw — các giai đoạn measure và layout không được thực thi trừ khi requestLayout() được gọi. Đây là điểm khác biệt chính: invalidate() nhẹ hơn requestLayout() vì nó không tính toán lại hình học.
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() // Yêu cầu vẽ lại
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
paint.color = Color.BLUE
paint.strokeWidth = 4f
paint.style = Paint.Style.STROKE
// Đang vẽ đường biểu đồ
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)
}
}
Trong ví dụ này, một View tùy chỉnh để vẽ biểu đồ gọi invalidate() khi dữ liệu được cập nhật. Hệ thống chỉ vẽ lại View này mà không ảnh hưởng đến các phần tử khác trong hệ phân cấp. onDraw() nhận Canvas để vẽ các đường qua Path.
Sự khác biệt chính giữa invalidate() và postInvalidate() nằm ở tính an toàn luồng. invalidate() chỉ được gọi từ luồng UI (luồng chính). postInvalidate() có thể được gọi từ bất kỳ luồng nào — nó gửi yêu cầu vẽ lại đến luồng UI qua Handler.
| Đặc điểm | invalidate() | postInvalidate() |
|---|---|---|
| Luồng gọi | Luồng UI (luồng chính) | Bất kỳ luồng nào |
| Cơ chế | Cập nhật trực tiếp cờ dirty | Qua Handler.post() đến luồng UI |
| Độ trễ | Tối thiểu, trong chu kỳ hiện tại | Đến chu kỳ luồng UI tiếp theo |
| Hiệu suất | Cao | Chi phí Handler nhỏ |
| Khuyến nghị | Luôn dùng invalidate() cho luồng UI | Chỉ cho luồng nền |
Trong thực tế, postInvalidate() được sử dụng trong các kịch bản tải dữ liệu mạng, xử lý kết quả cảm biến hoặc tính toán nền. Nếu bạn đang ở trong luồng UI — hãy luôn sử dụng invalidate() để có độ trễ tối thiểu.
// Được gọi từ luồng UI
view.invalidate()
// Được gọi từ luồng nền
Thread {
// Tính toán nặng
val result = performHeavyCalculation()
runOnUiThread {
updateUi(result)
}
}.start()
invalidate(Rect) và invalidate(int l, int t, int r, int b) cho phép giới hạn khu vực vẽ lại. Điều này rất quan trọng cho hiệu suất: khi chỉ một phần của View thay đổi (ví dụ: di chuyển con trỏ, thay đổi chỉ báo), không cần vẽ lại toàn bộ view.
Hệ thống truyền hình chữ nhật dirty được chỉ định đến onDraw() qua canvas.clipBounds. Bên trong onDraw(), bạn có thể kiểm tra clipBounds và chỉ vẽ trong khu vực đó, mặc dù Android Canvas tự động cắt việc vẽ bên ngoài hình chữ nhật dirty.
// Cập nhật một phần: chỉ khu vực con trỏ
private val cursorRect = Rect()
fun moveCursorTo(newX: Int, newY: Int) {
// Vô hiệu hóa vị trí cũ
invalidate(cursorRect)
cursorRect.set(newX - 5, newY - 5,
newX + 5, newY + 5)
// Vô hiệu hóa vị trí mới
invalidate(cursorRect)
}
Nếu không có vẽ lại một phần, mỗi lần di chuyển con trỏ sẽ vẽ lại toàn bộ View, điều này đối với một biểu đồ lớn có nghĩa là vẽ lại hàng nghìn pixel thay vì vài chục. invalidate(Rect) là kỹ thuật thiết yếu cho trình chỉnh sửa, canvas vẽ và thành phần hoạt hình.
Một lỗi phổ biến là gọi requestLayout() ở nơi chỉ cần invalidate() và ngược lại. Sự khác biệt là cơ bản: invalidate() chỉ ảnh hưởng đến giai đoạn draw, trong khi requestLayout() kích hoạt một chu kỳ đầy đủ measure → layout → draw.
| Khía cạnh | invalidate() | requestLayout() |
|---|---|---|
| Các giai đoạn chu kỳ | Chỉ draw | measure + layout + draw |
| Khi nào sử dụng | Chỉ hiển thị thay đổi (màu, văn bản, đồ họa) | Kích thước hoặc vị trí view thay đổi |
| Hiệu suất | Nhẹ — chỉ vẽ lại | Nặng — tính toán lại hệ phân cấp |
| Tác động đến hệ phân cấp | Chỉ view hiện tại | Có thể ảnh hưởng đến vùng chứa cha |
Nếu bạn thay đổi văn bản trong TextView, chỉ cần invalidate() vì kích thước view không thay đổi. Nếu văn bản có thể xuống dòng mới và tăng chiều cao, cần requestLayout(). Android Lint giúp theo dõi các lỗi này qua các quy tắc hiệu suất.
Các lệnh gọi invalidate() quá mức là một trong những nguyên nhân chính gây hiệu suất kém cho View tùy chỉnh trong Android. Hãy xem các kỹ thuật tối ưu.
Nếu dữ liệu được cập nhật với tần suất cao (cảm biến, hoạt ảnh, video), đừng gọi invalidate() cho mỗi thay đổi. Sử dụng ValueAnimator hoặc Choreographer.FrameCallback để đồng bộ với tần số làm mới màn hình. Điều này đảm bảo invalidate() được gọi không quá một lần mỗi khung hình.
Từ API 14, Android hỗ trợ tăng tốc phần cứng qua GPU. Nếu View tùy chỉnh của bạn chỉ sử dụng Canvas API (drawRect, drawCircle, drawPath), tăng tốc hoạt động trong suốt. Đối với các hoạt động tương thích DisplayList, invalidate() được xử lý nhanh hơn đáng kể.
// Sử dụng Choreographer để đồng bộ Vsync
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
updateAnimation(frameTimeNanos)
invalidate()
Choreographer.getInstance().postFrameCallback(this)
}
fun startAnimation() {
Choreographer.getInstance().postFrameCallback(frameCallback)
}
Sử dụng invalidate(Rect) cho các cập nhật mục tiêu, tránh gọi invalidate() từ onDraw() (vòng lặp vô hạn), và luôn đo lường qua GPU Profile Rendering trên thiết bị. Điều này sẽ hiển thị thời gian kết xuất chính xác của mỗi khung hình và giúp xác định các khu vực có vấn đề.
Câu hỏi thường gặp
Không, gọi invalidate() bên trong onDraw() tạo ra một vòng lặp vô hạn vẽ lại: onDraw() gọi invalidate(), từ đó lại kích hoạt onDraw(). Điều này dẫn đến CPU hoạt động 100% và rớt khung hình. Hãy sử dụng hoạt ảnh qua ValueAnimator hoặc Choreographer.
invalidate() chỉ hoạt động trong luồng UI và cập nhật cờ dirty ngay lập tức. postInvalidate() gửi yêu cầu qua Handler đến luồng UI và có thể được gọi từ bất kỳ luồng nền nào. Nếu bạn ở trong luồng UI — hãy sử dụng invalidate() để có độ trễ tối thiểu.
Có, bên trong phương thức setText() của TextView gọi invalidate() sau khi cập nhật văn bản. Nếu văn bản thay đổi kích thước view, requestLayout() cũng được gọi. Nhà phát triển không cần gọi thủ công invalidate() khi làm việc với các widget tiêu chuẩn.
Mỗi lần gọi invalidate() lên lịch vẽ lại trong Vsync tiếp theo (mỗi 16 ms). Nếu onDraw() mất hơn 16 ms, sẽ xảy ra rớt khung hình. Tối ưu onDraw() — lưu cache Bitmap, tránh cấp phát và sử dụng tăng tốc phần cứng để kết xuất GPU.
Có, sau khi thay đổi thuộc tính Paint (màu sắc, độ dày, kiểu), bạn phải gọi invalidate(), vì View không tự động theo dõi các thay đổi của đối tượng Paint. Hệ thống không biết Paint đã thay đổi và sẽ không gọi onDraw() nếu không có yêu cầu rõ ràng.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm