Vẽ trên đối tượng Canvas

Lớp học lập trình này nằm trong khoá học Kotlin nâng cao cho Android. Bạn sẽ nhận được nhiều giá trị nhất qua khoá học này nếu thực hiện các lớp học lập trình theo trình tự, nhưng đó không phải là yêu cầu bắt buộc. Tất cả các lớp học lập trình của khoá học đều được liệt kê trên trang đích của lớp học lập trình Kiến thức nâng cao về cách tạo ứng dụng Android bằng Kotlin.

Giới thiệu

Trong Android, bạn có một số kỹ thuật để triển khai ảnh động và đồ hoạ 2D tuỳ chỉnh trong các khung hiển thị.

Ngoài việc sử dụng các thành phần có thể vẽ, bạn có thể tạo bản vẽ 2D bằng các phương thức vẽ của lớp Canvas. Canvas là một bề mặt vẽ 2D cung cấp các phương thức để vẽ. Điều này hữu ích khi ứng dụng của bạn cần thường xuyên vẽ lại chính nó, vì những gì người dùng thấy sẽ thay đổi theo thời gian. Trong lớp học lập trình này, bạn sẽ tìm hiểu cách tạo và vẽ trên một canvas xuất hiện trong View.

Các loại thao tác bạn có thể thực hiện trên canvas bao gồm:

  • Tô màu cho toàn bộ canvas.
  • Vẽ các hình dạng, chẳng hạn như hình chữ nhật, hình vòng cung và đường dẫn được tạo kiểu như đã xác định trong đối tượng Paint. Đối tượng Paint lưu giữ thông tin về kiểu và màu sắc của cách vẽ hình học (chẳng hạn như đường thẳng, hình chữ nhật, hình bầu dục và đường dẫn) hoặc ví dụ: kiểu chữ của văn bản.
  • Áp dụng các phép biến đổi, chẳng hạn như dịch, điều chỉnh tỷ lệ hoặc biến đổi tuỳ chỉnh.
  • Cắt, tức là áp dụng một hình dạng hoặc đường dẫn cho canvas để xác định các phần hiển thị của canvas.

Cách bạn có thể hình dung về hoạt động vẽ trên Android (cực kỳ đơn giản!)

Việc vẽ trong Android hoặc trên bất kỳ hệ thống hiện đại nào khác là một quy trình phức tạp bao gồm các lớp trừu tượng và tối ưu hoá cho đến phần cứng. Cách Android vẽ là một chủ đề thú vị mà nhiều người đã viết về, và thông tin chi tiết về chủ đề này nằm ngoài phạm vi của lớp học lập trình này.

Trong bối cảnh của lớp học lập trình này và ứng dụng vẽ trên canvas để hiển thị ở chế độ xem toàn màn hình, bạn có thể nghĩ về nó theo cách sau.

  1. Bạn cần có một khung hiển thị để hiển thị nội dung bạn đang vẽ. Đây có thể là một trong những khung hiển thị do hệ thống Android cung cấp. Hoặc, trong lớp học lập trình này, bạn sẽ tạo một khung hiển thị tuỳ chỉnh đóng vai trò là khung hiển thị nội dung cho ứng dụng của bạn (MyCanvasView).
  2. Giống như mọi khung hiển thị khác, khung hiển thị này cũng có canvas riêng (canvas).
  3. Để vẽ theo cách cơ bản nhất trên canvas của một khung hiển thị, bạn sẽ ghi đè phương thức onDraw() và vẽ trên canvas của khung hiển thị đó.
  4. Khi tạo bản vẽ, bạn cần lưu vào bộ nhớ đệm những gì đã vẽ trước đó. Có một số cách để lưu dữ liệu vào bộ nhớ đệm, một trong số đó là lưu trong bitmap (extraBitmap). Một cách khác là lưu nhật ký những gì bạn đã vẽ dưới dạng toạ độ và hướng dẫn.
  5. Để vẽ vào bitmap lưu vào bộ nhớ đệm (extraBitmap) bằng API vẽ canvas, bạn sẽ tạo một canvas lưu vào bộ nhớ đệm (extraCanvas) cho bitmap lưu vào bộ nhớ đệm.
  6. Sau đó, bạn vẽ trên canvas lưu vào bộ nhớ đệm (extraCanvas), canvas này sẽ vẽ lên bitmap lưu vào bộ nhớ đệm (extraBitmap).
  7. Để hiển thị mọi thứ được vẽ trên màn hình, bạn yêu cầu canvas của khung hiển thị (canvas) vẽ bitmap lưu vào bộ nhớ đệm (extraBitmap).

Kiến thức bạn cần có

  • Cách tạo một ứng dụng có Hoạt động, bố cục cơ bản và chạy ứng dụng đó bằng Android Studio.
  • Cách liên kết trình xử lý sự kiện với các khung hiển thị.
  • Cách tạo chế độ xem tuỳ chỉnh.

Kiến thức bạn sẽ học được

  • Cách tạo một Canvas và vẽ trên đó để phản hồi thao tác chạm của người dùng.

Bạn sẽ thực hiện

  • Tạo một ứng dụng vẽ các đường trên màn hình để phản hồi khi người dùng chạm vào màn hình.
  • Ghi lại các sự kiện chuyển động và để phản hồi, hãy vẽ các đường trên một canvas xuất hiện trong một khung hiển thị tuỳ chỉnh ở chế độ toàn màn hình trên màn hình.

Ứng dụng MiniPaint sử dụng một khung hiển thị tuỳ chỉnh để hiển thị một đường kẻ nhằm phản hồi các thao tác chạm của người dùng, như minh hoạ trong ảnh chụp màn hình bên dưới.

Bước 1. Tạo dự án MiniPaint

  1. Tạo một dự án Kotlin mới có tên là MiniPaint bằng mẫu Empty Activity (Hoạt động trống).
  2. Mở tệp app/res/values/colors.xml rồi thêm 2 màu sau.
<color name="colorBackground">#FFFF5500</color>
<color name="colorPaint">#FFFFEB3B</color>
  1. Mở styles.xml
  2. Trong phần tử mẹ của kiểu AppTheme đã cho, hãy thay thế DarkActionBar bằng NoActionBar. Thao tác này sẽ xoá thanh thao tác để bạn có thể vẽ ở chế độ toàn màn hình.
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

Bước 2. Tạo lớp MyCanvasView

Trong bước này, bạn sẽ tạo một khung hiển thị tuỳ chỉnh, MyCanvasView, để vẽ.

  1. Trong gói app/java/com.example.android.minipaint, hãy tạo một New > Kotlin File/Class (Mới > Lớp/Tệp Kotlin) có tên là MyCanvasView.
  2. Mở rộng lớp MyCanvasView thành lớp View và truyền vào context: Context. Chấp nhận các mục nhập được đề xuất.
import android.content.Context
import android.view.View

class MyCanvasView(context: Context) : View(context) {
}

Bước 3. Đặt MyCanvasView làm thành phần hiển thị nội dung

Để hiển thị nội dung bạn sẽ vẽ trong MyCanvasView, bạn phải đặt nội dung đó làm khung hiển thị nội dung của MainActivity.

  1. Mở strings.xml và xác định một chuỗi để dùng cho nội dung mô tả của khung hiển thị.
<string name="canvasContentDescription">Mini Paint is a simple line drawing app.
   Drag your fingers to draw. Rotate the phone to clear.</string>
  1. Mở MainActivity.kt
  2. Trong onCreate(), hãy xoá setContentView(R.layout.activity_main).
  3. Tạo một thực thể của MyCanvasView.
val myCanvasView = MyCanvasView(this)
  1. Bên dưới, hãy yêu cầu chế độ toàn màn hình cho bố cục của myCanvasView. Thực hiện việc này bằng cách đặt cờ SYSTEM_UI_FLAG_FULLSCREEN trên myCanvasView. Bằng cách này, khung hiển thị sẽ lấp đầy hoàn toàn màn hình.
myCanvasView.systemUiVisibility = SYSTEM_UI_FLAG_FULLSCREEN
  1. Thêm nội dung mô tả.
myCanvasView.contentDescription = getString(R.string.canvasContentDescription)
  1. Bên dưới, hãy đặt thành phần hiển thị nội dung thành myCanvasView.
setContentView(myCanvasView)
  1. Chạy ứng dụng. Bạn sẽ thấy một màn hình hoàn toàn màu trắng, vì canvas không có kích thước và bạn chưa vẽ gì cả.

Bước 1. Ghi đè onSizeChanged()

Phương thức onSizeChanged() được hệ thống Android gọi bất cứ khi nào một khung hiển thị thay đổi kích thước. Vì khung hiển thị bắt đầu mà không có kích thước, nên phương thức onSizeChanged() của khung hiển thị cũng được gọi sau khi Hoạt động tạo và mở rộng khung hiển thị lần đầu tiên. Do đó, phương thức onSizeChanged() này là nơi lý tưởng để tạo và thiết lập canvas của khung hiển thị.

  1. Trong MyCanvasView, ở cấp lớp, hãy xác định các biến cho một canvas và một bitmap. Gọi họ là extraCanvasextraBitmap. Đây là bitmap và canvas của bạn để lưu vào bộ nhớ đệm những nội dung đã được vẽ trước đó.
private lateinit var extraCanvas: Canvas
private lateinit var extraBitmap: Bitmap
  1. Xác định biến cấp lớp backgroundColor cho màu nền của canvas và khởi tạo biến đó thành colorBackground mà bạn đã xác định trước đó.
private val backgroundColor = ResourcesCompat.getColor(resources, R.color.colorBackground, null)
  1. Trong MyCanvasView, hãy ghi đè phương thức onSizeChanged(). Phương thức gọi lại này được hệ thống Android gọi với kích thước màn hình đã thay đổi, tức là với chiều rộng và chiều cao mới (để thay đổi thành) và chiều rộng và chiều cao cũ (để thay đổi từ).
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
   super.onSizeChanged(width, height, oldWidth, oldHeight)
}
  1. Trong onSizeChanged(), hãy tạo một thực thể của Bitmap với chiều rộng và chiều cao mới (là kích thước màn hình) rồi chỉ định thực thể đó cho extraBitmap. Đối số thứ ba là cấu hình màu bitmap. ARGB_8888 lưu trữ mỗi màu trong 4 byte và được đề xuất.
extraBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
  1. Tạo một thực thể Canvas từ extraBitmap và chỉ định thực thể đó cho extraCanvas.
 extraCanvas = Canvas(extraBitmap)
  1. Chỉ định màu nền để tô extraCanvas.
extraCanvas.drawColor(backgroundColor)
  1. Nhìn vào onSizeChanged(), một bitmap và canvas mới sẽ được tạo mỗi khi hàm thực thi. Bạn cần một bitmap mới vì kích thước đã thay đổi. Tuy nhiên, đây là một lỗi rò rỉ bộ nhớ, khiến các bitmap cũ vẫn còn. Để khắc phục vấn đề này, hãy tái chế extraBitmap trước khi tạo extraBitmap tiếp theo bằng cách thêm mã này ngay sau lệnh gọi đến super.
if (::extraBitmap.isInitialized) extraBitmap.recycle()

Bước 2. Ghi đè onDraw()

Mọi thao tác vẽ cho MyCanvasView đều diễn ra trong onDraw().

Để bắt đầu, hãy hiển thị canvas, lấp đầy màn hình bằng màu nền mà bạn đã đặt trong onSizeChanged().

  1. Ghi đè onDraw() và vẽ nội dung của extraBitmap được lưu vào bộ nhớ đệm trên canvas liên kết với khung hiển thị. Phương thức drawBitmap() Canvas có nhiều phiên bản. Trong mã này, bạn cung cấp bitmap, toạ độ x và y (tính bằng pixel) của góc trên cùng bên trái và null cho Paint, vì bạn sẽ đặt giá trị này sau.
override fun onDraw(canvas: Canvas) {
   super.onDraw(canvas)
canvas.drawBitmap(extraBitmap, 0f, 0f, null)
}


Xin lưu ý rằng canvas được truyền đến onDraw() và được hệ thống dùng để hiển thị bitmap khác với canvas mà bạn đã tạo trong phương thức onSizeChanged() và bạn dùng để vẽ trên bitmap.

  1. Chạy ứng dụng. Bạn sẽ thấy toàn bộ màn hình được tô bằng màu nền đã chỉ định.

Để vẽ, bạn cần có một đối tượng Paint chỉ định cách tạo kiểu cho các đối tượng khi vẽ và một đối tượng Path chỉ định nội dung đang được vẽ.

Bước 1. Khởi chạy đối tượng Paint

  1. Trong MyCanvasView.kt, ở cấp tệp trên cùng, hãy xác định một hằng số cho độ rộng nét vẽ.
private const val STROKE_WIDTH = 12f // has to be float
  1. Ở cấp lớp của MyCanvasView, hãy xác định một biến drawColor để lưu giữ màu cần vẽ và khởi tạo biến đó bằng tài nguyên colorPaint mà bạn đã xác định trước đó.
private val drawColor = ResourcesCompat.getColor(resources, R.color.colorPaint, null)
  1. Ở cấp lớp, bên dưới, hãy thêm một biến paint cho đối tượng Paint và khởi tạo biến đó như sau.
// Set up the paint with which to draw.
private val paint = Paint().apply {
   color = drawColor
   // Smooths out edges of what is drawn without affecting shape.
   isAntiAlias = true
   // Dithering affects how colors with higher-precision than the device are down-sampled.
   isDither = true
   style = Paint.Style.STROKE // default: FILL
   strokeJoin = Paint.Join.ROUND // default: MITER
   strokeCap = Paint.Cap.ROUND // default: BUTT
   strokeWidth = STROKE_WIDTH // default: Hairline-width (really thin)
}
  • color của paintdrawColor mà bạn đã xác định trước đó.
  • isAntiAlias xác định xem có áp dụng tính năng làm mịn cạnh hay không. Khi đặt isAntiAlias thành true, các cạnh của nội dung được vẽ sẽ trở nên mượt mà mà không ảnh hưởng đến hình dạng.
  • isDither, khi true, ảnh hưởng đến cách lấy mẫu xuống các màu có độ chính xác cao hơn thiết bị. Ví dụ: phương pháp tạo hiệu ứng chuyển màu là cách phổ biến nhất để giảm dải màu của hình ảnh xuống còn 256 màu (hoặc ít hơn).
  • style đặt loại hoạt động vẽ sẽ được thực hiện cho một nét vẽ, về cơ bản là một đường thẳng. Paint.Style chỉ định xem phần tử cơ bản đang được vẽ là được tô màu, được vẽ đường viền hay cả hai (cùng màu). Theo mặc định, đối tượng mà bạn áp dụng sơn sẽ được tô. ("Fill" (Tô màu) cho phần bên trong hình dạng, trong khi "stroke" (đường viền) theo đường viền của hình dạng.)
  • strokeJoin của Paint.Join chỉ định cách các đường và đoạn cong kết hợp trên một đường có nét vẽ. Giá trị mặc định là MITER.
  • strokeCap đặt hình dạng của phần cuối đường kẻ thành một nắp. Paint.Cap chỉ định cách bắt đầu và kết thúc các đường kẻ và đường dẫn được vẽ bằng nét. Giá trị mặc định là BUTT.
  • strokeWidth chỉ định chiều rộng của nét vẽ bằng pixel. Theo mặc định, chiều rộng là rất nhỏ, vì vậy, bạn nên đặt thành hằng số STROKE_WIDTH mà bạn đã xác định trước đó.

Bước 2. Khởi tạo một đối tượng Đường dẫn

Path là đường dẫn của nội dung mà người dùng đang vẽ.

  1. Trong MyCanvasView, hãy thêm một biến path và khởi tạo biến đó bằng một đối tượng Path để lưu trữ đường dẫn đang được vẽ khi theo dõi thao tác chạm của người dùng trên màn hình. Nhập android.graphics.Path cho Path.
private var path = Path()

Bước 1. Phản hồi chuyển động trên màn hình

Phương thức onTouchEvent() trên một khung hiển thị được gọi mỗi khi người dùng chạm vào màn hình.

  1. Trong MyCanvasView, hãy ghi đè phương thức onTouchEvent() để lưu vào bộ nhớ đệm toạ độ xy của event đã truyền vào. Sau đó, hãy dùng biểu thức when để xử lý các sự kiện chuyển động khi chạm xuống màn hình, di chuyển trên màn hình và nhấc tay khỏi màn hình. Đây là những sự kiện cần thiết để vẽ một đường thẳng trên màn hình. Đối với mỗi loại sự kiện, hãy gọi một phương thức tiện ích, như minh hoạ trong mã dưới đây. Hãy xem tài liệu về lớp MotionEvent để biết danh sách đầy đủ các sự kiện chạm.
override fun onTouchEvent(event: MotionEvent): Boolean {
   motionTouchEventX = event.x
   motionTouchEventY = event.y

   when (event.action) {
       MotionEvent.ACTION_DOWN -> touchStart()
       MotionEvent.ACTION_MOVE -> touchMove()
       MotionEvent.ACTION_UP -> touchUp()
   }
   return true
}
  1. Ở cấp lớp, hãy thêm các biến motionTouchEventXmotionTouchEventY còn thiếu để lưu vào bộ nhớ đệm toạ độ x và y của sự kiện chạm hiện tại (toạ độ MotionEvent). Khởi tạo chúng thành 0f.
private var motionTouchEventX = 0f
private var motionTouchEventY = 0f
  1. Tạo các phần giữ chỗ cho 3 hàm touchStart(), touchMove()touchUp().
private fun touchStart() {}

private fun touchMove() {}

private fun touchUp() {}
  1. Mã của bạn sẽ được tạo và chạy, nhưng bạn sẽ chưa thấy gì khác ngoài nền có màu.

Bước 2. Triển khai touchStart()

Phương thức này được gọi khi người dùng chạm vào màn hình lần đầu tiên.

  1. Ở cấp lớp, hãy thêm các biến để lưu vào bộ nhớ đệm các giá trị x và y mới nhất. Sau khi người dùng ngừng di chuyển và nhấc ngón tay lên, đây là điểm bắt đầu cho đường dẫn tiếp theo (đoạn tiếp theo của đường kẻ cần vẽ).
private var currentX = 0f
private var currentY = 0f
  1. Triển khai phương thức touchStart() như sau. Đặt lại path, di chuyển đến toạ độ x-y của sự kiện chạm (motionTouchEventXmotionTouchEventY), rồi chỉ định currentXcurrentY cho giá trị đó.
private fun touchStart() {
   path.reset()
   path.moveTo(motionTouchEventX, motionTouchEventY)
   currentX = motionTouchEventX
   currentY = motionTouchEventY
}

Bước 3. Triển khai touchMove()

  1. Ở cấp lớp, hãy thêm một biến touchTolerance rồi đặt biến đó thành ViewConfiguration.get(context).scaledTouchSlop.
private val touchTolerance = ViewConfiguration.get(context).scaledTouchSlop

Khi sử dụng đường dẫn, bạn không cần vẽ từng pixel và mỗi lần yêu cầu làm mới màn hình. Thay vào đó, bạn có thể (và sẽ) nội suy một đường dẫn giữa các điểm để có hiệu suất tốt hơn nhiều.

  • Nếu ngón tay hầu như không di chuyển, bạn không cần vẽ.
  • Nếu ngón tay di chuyển ít hơn khoảng cách touchTolerance, thì không vẽ.
  • scaledTouchSlop trả về khoảng cách tính bằng pixel mà một thao tác chạm có thể di chuyển trước khi hệ thống cho rằng người dùng đang di chuyển.
  1. Xác định phương thức touchMove(). Tính khoảng cách đã đi (dx, dy), tạo một đường cong giữa hai điểm và lưu trữ đường cong đó trong path, cập nhật tổng số currentXcurrentY đang chạy, đồng thời vẽ path. Sau đó, hãy gọi invalidate() để buộc vẽ lại màn hình bằng path đã cập nhật.
private fun touchMove() {
   val dx = Math.abs(motionTouchEventX - currentX)
   val dy = Math.abs(motionTouchEventY - currentY)
   if (dx >= touchTolerance || dy >= touchTolerance) {
       // QuadTo() adds a quadratic bezier from the last point,
       // approaching control point (x1,y1), and ending at (x2,y2).
       path.quadTo(currentX, currentY, (motionTouchEventX + currentX) / 2, (motionTouchEventY + currentY) / 2)
       currentX = motionTouchEventX
       currentY = motionTouchEventY
       // Draw the path in the extra bitmap to cache it.
       extraCanvas.drawPath(path, paint)
   }
   invalidate()
}

Chi tiết hơn về phương thức này:

  1. Tính khoảng cách đã di chuyển (dx, dy).
  2. Nếu chuyển động vượt quá ngưỡng dung sai chạm, hãy thêm một đoạn vào đường dẫn.
  3. Đặt điểm bắt đầu cho phân đoạn tiếp theo thành điểm cuối của phân đoạn này.
  4. Sử dụng quadTo() thay vì lineTo() để tạo một đường kẻ được vẽ mượt mà mà không có góc. Xem phần Đường cong Bezier.
  5. Gọi invalidate() để (cuối cùng gọi onDraw() và) vẽ lại khung hiển thị.

Bước 4: Triển khai touchUp()

Khi người dùng nhấc ngón tay lên, bạn chỉ cần đặt lại đường dẫn để đường dẫn không được vẽ lại. Không có gì được vẽ, nên không cần phải vô hiệu hoá.

  1. Triển khai phương thức touchUp().
private fun touchUp() {
   // Reset the path so it doesn't get drawn again.
   path.reset()
}
  1. Chạy mã và dùng ngón tay để vẽ trên màn hình. Lưu ý rằng nếu bạn xoay thiết bị, màn hình sẽ bị xoá vì trạng thái vẽ không được lưu. Đối với ứng dụng mẫu này, đây là theo thiết kế, nhằm mang đến cho người dùng một cách đơn giản để xoá màn hình.

Bước 5: Vẽ một khung xung quanh bản phác thảo

Khi người dùng vẽ trên màn hình, ứng dụng của bạn sẽ tạo đường dẫn và lưu đường dẫn đó vào bitmap extraBitmap. Phương thức onDraw() hiển thị bitmap bổ sung trong canvas của khung hiển thị. Bạn có thể vẽ thêm trong onDraw(). Ví dụ: bạn có thể vẽ các hình dạng sau khi vẽ bitmap.

Ở bước này, bạn vẽ một khung xung quanh rìa của bức ảnh.

  1. Trong MyCanvasView, hãy thêm một biến có tên là frame chứa một đối tượng Rect.
private lateinit var frame: Rect
  1. Ở cuối onSizeChanged(), hãy xác định một phần lồng ghép và thêm mã để tạo Rect sẽ được dùng cho khung, sử dụng kích thước mới và phần lồng ghép.
// Calculate a rectangular frame around the picture.
val inset = 40
frame = Rect(inset, inset, width - inset, height - inset)
  1. Trong onDraw(), sau khi vẽ bitmap, hãy vẽ một hình chữ nhật.
// Draw a frame around the canvas.
canvas.drawRect(frame, paint)
  1. Chạy ứng dụng của bạn. Chú ý đến khung hình.

Nhiệm vụ (không bắt buộc): Lưu trữ dữ liệu trong một Đường dẫn

Trong ứng dụng hiện tại, thông tin vẽ được lưu trữ trong một bitmap. Mặc dù đây là một giải pháp hay, nhưng không phải là cách duy nhất có thể dùng để lưu trữ thông tin bản vẽ. Cách bạn lưu trữ nhật ký vẽ sẽ tuỳ thuộc vào ứng dụng và các yêu cầu của bạn. Ví dụ: nếu đang vẽ hình dạng, bạn có thể lưu danh sách các hình dạng cùng với vị trí và kích thước của chúng. Đối với ứng dụng MiniPaint, bạn có thể lưu đường dẫn dưới dạng Path. Dưới đây là hướng dẫn chung về cách thực hiện việc đó (nếu bạn muốn thử).

  1. Trong MyCanvasView, hãy xoá tất cả mã cho extraCanvasextraBitmap.
  2. Thêm các biến cho đường dẫn cho đến thời điểm hiện tại và đường dẫn đang được vẽ.
// Path representing the drawing so far
private val drawing = Path()

// Path representing what's currently being drawn
private val curPath = Path()
  1. Trong onDraw(), thay vì vẽ bitmap, hãy vẽ các đường dẫn đã lưu và đường dẫn hiện tại.
// Draw the drawing so far
canvas.drawPath(drawing, paint)
// Draw any current squiggle
canvas.drawPath(curPath, paint)
// Draw a frame around the canvas
canvas.drawRect(frame, paint)
  1. Trong touchUp(), hãy thêm đường dẫn hiện tại vào đường dẫn trước đó và đặt lại đường dẫn hiện tại.
// Add the current path to the drawing so far
drawing.addPath(curPath)
// Rewind the current path for the next touch
curPath.reset()
  1. Chạy ứng dụng của bạn và chắc chắn là sẽ không có sự khác biệt nào.

Tải mã xuống cho lớp học lập trình đã hoàn thành.

$  git clone https://github.com/googlecodelabs/android-kotlin-drawing-canvas


Ngoài ra, bạn có thể tải kho lưu trữ xuống dưới dạng tệp ZIP, sau đó giải nén và mở tệp đó trong Android Studio.

Tải tệp Zip xuống

  • Canvas là một bề mặt vẽ 2D cung cấp các phương thức vẽ.
  • Canvas có thể được liên kết với một thực thể View hiển thị thực thể đó.
  • Đối tượng Paint lưu giữ thông tin về kiểu và màu sắc về cách vẽ các hình học (chẳng hạn như đường thẳng, hình chữ nhật, hình bầu dục và đường dẫn) và văn bản.
  • Một mẫu phổ biến để làm việc với canvas là tạo một khung hiển thị tuỳ chỉnh và ghi đè các phương thức onDraw()onSizeChanged().
  • Ghi đè phương thức onTouchEvent() để ghi lại các thao tác chạm của người dùng và phản hồi các thao tác đó bằng cách vẽ các đối tượng.
  • Bạn có thể sử dụng một bitmap bổ sung để lưu thông tin vào bộ nhớ đệm cho các bản vẽ thay đổi theo thời gian. Ngoài ra, bạn có thể lưu trữ các hình dạng hoặc một đường dẫn.

Khoá học của Udacity:

Tài liệu dành cho nhà phát triển Android:

Phần này liệt kê các bài tập về nhà cho học viên của lớp học lập trình này trong phạm vi khoá học có người hướng dẫn. Người hướng dẫn phải thực hiện các việc sau đây:

  • Giao bài tập về nhà nếu cần.
  • Trao đổi với học viên về cách nộp bài tập về nhà.
  • Chấm điểm bài tập về nhà.

Người hướng dẫn có thể sử dụng các đề xuất này ít hoặc nhiều tuỳ ý và nên giao cho học viên bất kỳ bài tập về nhà nào khác mà họ cảm thấy phù hợp.

Nếu bạn đang tự học các lớp học lập trình, hãy sử dụng những bài tập về nhà này để kiểm tra kiến thức của mình.

Trả lời các câu hỏi sau

Câu hỏi 1

Bạn cần có những thành phần nào sau đây để làm việc với Canvas? Hãy chọn mọi câu trả lời phù hợp.

Bitmap

Paint

Path

View

Câu hỏi 2

Lệnh gọi đến invalidate() có chức năng gì (nói chung)?

▢ Vô hiệu hoá và khởi động lại ứng dụng của bạn.

▢ Xoá bản vẽ khỏi bitmap.

▢ Cho biết bạn không nên chạy mã trước đó.

▢ Yêu cầu hệ thống vẽ lại màn hình.

Câu hỏi 3

Chức năng của các đối tượng Canvas, BitmapPaint là gì?

▢ Bề mặt vẽ 2D, bitmap hiển thị trên màn hình, thông tin định kiểu để vẽ.

▢ Bề mặt vẽ 3D, bitmap để lưu vào bộ nhớ đệm đường dẫn, thông tin định kiểu để vẽ.

▢ Nền tảng vẽ 2D, bitmap hiển thị trên màn hình, tạo kiểu cho khung hiển thị.

▢ Bộ nhớ đệm cho thông tin vẽ, bitmap để vẽ, thông tin định kiểu để vẽ.

Để biết đường liên kết đến các lớp học lập trình khác trong khoá học này, hãy xem trang đích của các lớp học lập trình trong khoá học Kiến thức nâng cao về cách tạo ứng dụng Android bằng Kotlin.