Canvas trong Android: nó là gì, lớp Canvas và các phương thức draw

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

Canvas trong Android — là một lớp từ gói android.graphics cung cấp API để kết xuất 2D trên View. Canvas hoạt động như một tấm vải mà trên đó nhà phát triển vẽ các hình dạng hình học, văn bản, hình ảnh và đường dẫn bằng các phương thức draw* khác nhau. Theo Tài liệu dành cho nhà phát triển Android (2025), Canvas là công cụ chính để kết xuất tùy chỉnh trong Android và được sử dụng cùng với lớp Paint để định nghĩa kiểu dáng và màu sắc. Mỗi lần gọi onDraw() nhận được một phiên bản Canvas sẵn sàng, được liên kết với bộ đệm bitmap của view.

Các điểm chính

  • Canvas — lớp để kết xuất 2D trong Android, cung cấp các phương thức drawRect, drawCircle, drawText, drawBitmap và các phương thức khác
  • Paint — lớp định nghĩa kiểu kết xuất: màu sắc, độ dày, tô màu, hiệu ứng (bóng, chuyển màu)
  • onDraw(Canvas) — phương thức của View nơi tất cả việc kết xuất diễn ra thông qua Canvas nhận được
  • Canvas.save() và restore() — quản lý ngăn xếp biến đổi cho các biến đổi hệ tọa độ lồng nhau
  • Path — lớp để xây dựng các đường viền và đường cong tùy ý, được truyền vào drawPath(Path, Paint)

Canvas trong Android là gì

Canvas — là lớp đồ họa 2D trung tâm trong Android cung cấp các phương thức để vẽ trên một mặt phẳng. Mỗi Canvas được gắn với một Bitmap — vùng bộ nhớ nơi kết quả của các thao tác vẽ được ghi lại. Khi một View được vẽ lại, hệ thống truyền vào onDraw() một Canvas đã được liên kết với bộ đệm của View đó.

Canvas hoạt động trong hệ tọa độ nơi (0,0) là góc trên cùng bên trái, trục X hướng sang phải, trục Y hướng xuống dưới. Tất cả các thao tác vẽ sử dụng trạng thái hiện tại của Canvas, bao gồm ma trận biến đổi, vùng cắt và kiểu Paint. Theo Tài liệu Android (2025), Canvas API bao gồm hơn 30 phương thức draw* cho các loại nguyên thủy đồ họa khác nhau.

Canvas và Tăng tốc phần cứng

Từ Android 3.0 (API 11), Canvas hỗ trợ tăng tốc phần cứng thông qua DisplayList. Ở chế độ tăng tốc, các thao tác Canvas được ghi vào DisplayList và sau đó thực thi trên GPU. Điều này mang lại hiệu suất tăng đáng kể nhưng áp đặt các hạn chế: một số thao tác Canvas (clipPath với clip không phải Path) không được hỗ trợ ở chế độ tăng tốc.

Các phương thức kết xuất cơ bản của Canvas

Canvas cung cấp một tập hợp các phương thức để vẽ các nguyên thủy cơ bản. Mỗi phương thức chấp nhận các tham số hình học và một đối tượng Paint định nghĩa kiểu trực quan.

Phương thứcMục đíchVí dụ
drawRect()Vẽ hình chữ nhậtcanvas.drawRect(10f, 10f, 100f, 100f, paint)
drawCircle()Vẽ hình tròncanvas.drawCircle(50f, 50f, 30f, paint)
drawLine()Vẽ đường thẳngcanvas.drawLine(0f, 0f, 100f, 100f, paint)
drawText()Vẽ văn bảncanvas.drawText(“Hello”, 10f, 20f, paint)
drawBitmap()Vẽ hình ảnhcanvas.drawBitmap(bitmap, 0f, 0f, paint)
drawPath()Vẽ đường dẫn tùy ýcanvas.drawPath(path, paint)
drawOval()Vẽ hình bầu dụccanvas.drawOval(rectF, paint)
drawArc()Vẽ cung hoặc hình quạtcanvas.drawArc(rectF, 0f, 90f, true, paint)

Mỗi phương thức có thể được gọi theo bất kỳ thứ tự nào — Canvas vẽ theo thứ tự các lời gọi, xếp chồng các phần tử mới lên trên các phần tử trước đó. Đối với các bố cục phức tạp, kỹ thuật kết xuất nhiều lớp với lưu và khôi phục trạng thái Canvas được sử dụng.

Lớp Paint: cấu hình kiểu dáng và hiệu ứng

Paint — là lớp định nghĩa cách Canvas sẽ vẽ. Không có Paint, các phương thức Canvas vô dụng — nó đặt màu sắc, độ dày đường kẻ, kiểu tô, kích thước văn bản, hiệu ứng (bóng, chuyển màu, làm mờ) và chế độ hòa trộn.

Paint có các thuộc tính chính: color (màu sắc), style (FILL, STROKE hoặc FILL_AND_STROKE), strokeWidth (độ dày nét vẽ), textSize (kích thước văn bản), isAntiAlias (làm mịn), shader (chuyển màu hoặc hình ảnh). Tạo một đối tượng Paint là thao tác tốn kém, vì vậy nên được tạo một lần và tái sử dụng.

kotlin
private val fillPaint = Paint().apply {
    color = Color.parseColor("#FF6200EE")
    style = Paint.Style.FILL
    isAntiAlias = true
}

private val strokePaint = Paint().apply {
    color = Color.WHITE
    style = Paint.Style.STROKE
    strokeWidth = 4f
    isAntiAlias = true
}

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    canvas.drawCircle(100f, 100f, 80f, fillPaint)
    canvas.drawCircle(100f, 100f, 80f, strokePaint)
}

Trong ví dụ này, hai đối tượng Paint được sử dụng: một để tô hình tròn với màu tím, cái thứ hai cho đường viền trắng. Tách biệt tô và đường viền là thực hành tiêu chuẩn cho phép kiểm soát linh hoạt giao diện mà không cần tạo lại đối tượng.

Shader và chuyển màu

Đối với tô màu chuyển tiếp, Paint chấp nhận Shader — các lớp con LinearGradient, RadialGradient, SweepGradient. Shader được gắn vào Paint thông qua phương thức setShader() và định nghĩa cách màu sắc thay đổi trên vùng vẽ.

Các biến đổi Canvas: translate, rotate, scale

Canvas hỗ trợ các biến đổi ma trận của hệ tọa độ: translate() (dịch chuyển), rotate() (xoay), scale() (thu phóng) và skew() (nghiêng). Các biến đổi tích lũy: mỗi lần gọi tiếp theo được áp dụng lên trên các lần gọi trước.

Việc quản lý ngăn xếp biến đổi được thực hiện thông qua save()restore(). save() lưu trạng thái hiện tại của Canvas (ma trận, vùng cắt) vào ngăn xếp, restore() khôi phục trạng thái đã lưu gần nhất. Giữa save() và restore(), các biến đổi lồng nhau có thể được áp dụng.

kotlin
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    canvas.save() // Save initial state
    canvas.translate(100f, 100f) // Translate origin

    // First square
    canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)

    canvas.save() // Save after translate
    canvas.rotate(45f, 25f, 25f) // Rotate around square center

    // Rotated square
    canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
    canvas.restore() // Restore after rotate

    canvas.restore() // Restore initial state
}

Luôn sử dụng save() trước khi bắt đầu các biến đổi và restore() sau khi hoàn thành chúng. Các cặp save/restore mất cân bằng dẫn đến tích lũy ma trận và kết xuất không chính xác các phần tử tiếp theo.

Canvas với Bitmap và Path cho đồ họa phức tạp

Đối với đồ họa phức tạp, Canvas được kết hợp với BitmapPath. Bitmap được sử dụng để tải hình ảnh và lưu đệm các khung hình đã kết xuất. Path được sử dụng để xây dựng các đường cong Bezier tùy ý, đa giác và các đường viền phức tạp.

Bộ đệm Bitmap ngoài màn hình

Kỹ thuật kết xuất ngoài màn hình: một Bitmap được tạo, một cảnh phức tạp được vẽ lên đó thông qua một Canvas riêng, và sau đó Bitmap hoàn chỉnh được xuất ra trong onDraw() thông qua drawBitmap(). Điều này cho phép cập nhật đồ họa phức tạp mà không cần vẽ lại toàn bộ mỗi khung hình.

kotlin
private var offscreenBitmap: Bitmap? = null
private var offscreenCanvas: Canvas? = null

fun buildOffscreenScene(width: Int, height: Int) {
    offscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    offscreenCanvas = Canvas(offscreenBitmap)

    // Render scene once
    offscreenCanvas!!.drawColor(Color.WHITE)
    drawGrid(offscreenCanvas!!)
    drawComplexShape(offscreenCanvas!!)
}

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    offscreenBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
}

Path được sử dụng để xây dựng các đường viền phức tạp: các đường cong Bezier (quadTo, cubicTo), cung (arcTo), đường thẳng (lineTo). Path có thể được đóng (close()) cho các hình dạng tô màu hoặc mở cho các đường thẳng. Phương thức chính drawPath() kết hợp Path và Paint để kết xuất cuối cùng.

Tối ưu hóa kết xuất Canvas

Hiệu suất kết xuất Canvas ảnh hưởng trực tiếp đến FPS của ứng dụng. Các vấn đề chính: cấp phát quá mức trong onDraw(), gọi invalidate() thường xuyên và sử dụng Tăng tốc phần cứng không tối ưu.

Tạo các đối tượng bên ngoài onDraw()

Paint, Path, Rect, RectF và các đối tượng khác nên được tạo một lần trong bộ khởi tạo hoặc phương thức setup(). Mỗi lần cấp phát trong onDraw() tạo áp lực lên bộ thu gom rác, dẫn đến các micro-lag. Ngoại lệ là static hoặc companion object cho các đối tượng bất biến.

Sử dụng clipRect() để giới hạn vùng

Nếu phần lớn View bị ẩn bên ngoài vùng hiển thị (ví dụ, trong ScrollView), hãy sử dụng canvas.clipRect() để giới hạn kết xuất. Tăng tốc phần cứng trong trường hợp này tối ưu hóa thêm việc cắt các pixel không nhìn thấy.

kotlin
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    // Clip to visible area
    canvas.save()
    canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)

    // Render large scene
    renderScene(canvas)
    canvas.restore()
}

Sử dụng View.setLayerType() với LAYER_TYPE_HARDWARE cho các View có đồ họa Canvas phức tạp ít khi được cập nhật. Đối với hoạt ảnh cập nhật thường xuyên, ngược lại, sử dụng LAYER_TYPE_NONE để tránh cập nhật lớp phần cứng tốn kém. Hãy cân đo bằng GPU Profile RenderingAndroid Studio Profiler.

Câu hỏi thường gặp

Có thể tạo Canvas mà không có View không?

Có, bạn có thể tạo Canvas(Bitmap) — một hàm tạo chấp nhận Bitmap. Điều này được sử dụng cho kết xuất ngoài màn hình: bạn vẽ lên Bitmap, sau đó xuất nó qua drawBitmap(). Canvas cũng có thể được tạo từ Surface (cho SurfaceView) hoặc thông qua Picture.

Canvas khác gì với SurfaceView?

Canvas — là API để vẽ trên View trong luồng UI. SurfaceView — là một Surface riêng biệt có thể được cập nhật từ luồng nền thông qua Canvas riêng của nó (lockCanvas() / unlockCanvasAndPost()). SurfaceView được ưu tiên cho trò chơi và video.

Tại sao Canvas không vẽ sau khi biến đổi?

Nguyên nhân phổ biến nhất là các cặp save()restore() mất cân bằng. Nếu sau khi biến đổi (translate, rotate) không gọi restore(), Canvas vẫn ở trong hệ tọa độ đã bị thay đổi và các phần tử tiếp theo được vẽ với độ lệch. Hãy kiểm tra các cặp save()/restore() của bạn.

Làm thế nào để vẽ văn bản canh giữa Canvas?

Sử dụng Paint.setTextAlign(Paint.Align.CENTER) để canh giữa theo chiều ngang và Paint.getTextBounds() hoặc Paint.descent() / ascent() cho chiều dọc. Canh giữa: canvas.drawText(văn bản, centerX, centerY - (ascent + descent) / 2, paint).

Canvas có hỗ trợ hoạt ảnh không?

Bản thân Canvas không tạo hoạt ảnh — nó chỉ vẽ trạng thái hiện tại. Hoạt ảnh đạt được thông qua các lời gọi liên tiếp tới invalidate() (hoặc postInvalidateOnAnimation()), mỗi lần vẽ một khung hình mới trong onDraw(). Để có hoạt ảnh mượt mà, hãy sử dụng ValueAnimator hoặc Choreographer để đồng bộ với Vsync.

Tổng kết

  • Canvas — lớp kết xuất 2D trung tâm trong Android, cung cấp các phương thức draw* để vẽ nguyên thủy, văn bản và hình ảnh
  • Paint — định nghĩa kiểu trực quan (màu sắc, độ dày, tô, shaders); được tạo một lần và tái sử dụng
  • save() / restore() — quản lý ngăn xếp biến đổi; mỗi biến đổi phải trong một cặp save/restore
  • Path — xây dựng các đường viền và đường cong Bezier tùy ý cho đồ họa vector phức tạp
  • Offscreen Bitmap — kỹ thuật lưu đệm các cảnh phức tạp qua Canvas(Bitmap) để giảm tải cho onDraw()
  • Tăng tốc phần cứng — tăng tốc GPU của Canvas từ Android 3.0, áp đặt giới hạn trên một số thao tác (clipPath)
  • Cân đo hiệu năng — GPU Profile Rendering và Android Studio Profiler bắt buộc để tìm các điểm nghẽn trong onDraw()

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