invalidate() — Nó là gì, cơ chế vẽ lại và invalidate(Rect)

Tác giả: IT Sectr Đã đăng: 2026-07-20 Thời gian đọc: 7 phút

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() — một yêu cầu không đồng bộ để vẽ lại View trong Android, hoạt động qua cơ chế cờ dirty
  • postInvalidate() — phiên bản của invalidate() để gọi từ luồng nền, an toàn với luồng
  • invalidate(Rect) — vẽ lại một phần chỉ khu vực được chỉ định để tối ưu hiệu suất
  • onDraw() — phương thức được hệ thống gọi sau invalidate(), tương tự draw(_:) trong iOS
  • invalidate() vs requestLayout() — invalidate() vẽ lại nội dung, requestLayout() tính toán lại hình học

invalidate() trong Android là gì

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 qua invalidate() hoạt động như thế nào

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.

Chu kỳ vẽ lại

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.

kotlin
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.

invalidate() vs postInvalidate()

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ểminvalidate()postInvalidate()
Luồng gọiLuồng UI (luồng chính)Bất kỳ luồng nào
Cơ chếCập nhật trực tiếp cờ dirtyQua 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ấtCaoChi phí Handler nhỏ
Khuyến nghịLuôn dùng invalidate() cho luồng UIChỉ 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.

kotlin
    // Đượ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()

Vẽ lại một phần qua invalidate(Rect)

invalidate(Rect)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.

kotlin
    // 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.

invalidate() vs requestLayout(): sự khác biệt là gì

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ạnhinvalidate()requestLayout()
Các giai đoạn chu kỳChỉ drawmeasure + layout + draw
Khi nào sử dụngChỉ 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ấtNhẹ — chỉ vẽ lạiNặng — tính toán lại hệ phân cấp
Tác động đến hệ phân cấpChỉ view hiện tạiCó 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.

Tối ưu hiệu suất invalidate()

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.

Giảm tần suất gọi

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.

Sử dụng tăng tốc phần cứng

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ể.

kotlin
// 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

Có thể gọi invalidate() từ onDraw() không?

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() khác postInvalidate() như thế nào?

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.

setText() trong TextView có tự động gọi invalidate() không?

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.

invalidate() ảnh hưởng đến hiệu suất ở 60 FPS như thế nào?

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ó cần gọi invalidate() sau khi thay đổi thuộc tính Paint không?

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

  • invalidate() — cơ chế chính để yêu cầu vẽ lại View trong Android, hoạt động không đồng bộ qua cờ dirty
  • postInvalidate() — phiên bản an toàn luồng để gọi từ luồng nền, sử dụng Handler
  • invalidate(Rect) — vẽ lại một phần chỉ khu vực được chỉ định, quan trọng cho hiệu suất với các thay đổi mục tiêu
  • requestLayout() — kích hoạt chu kỳ đầy đủ measure + layout + draw, nặng hơn đáng kể so với invalidate()
  • Choreographer — dịch vụ hệ thống đồng bộ Vsync, được khuyến nghị cho hoạt ảnh với invalidate()
  • Hardware Acceleration — tăng tốc GPU có sẵn từ API 14, tăng tốc xử lý invalidate() cho Canvas API
  • GPU Profile Rendering — công cụ đo lường để đo thời gian kết xuất và xác định các phương thức onDraw() chậm

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.

Thảo luận dự án

Đọc thêm