Lớp học lập trình này nằm trong khóa học Nâng cao về Android trong Kotlin. Bạn sẽ nhận được nhiều giá trị nhất từ khóa học này nếu bạn làm việc qua các lớp học lập trình theo trình tự, nhưng bạn không bắt buộc phải làm vậy. Tất cả các lớp học lập trình đều có trên trang đích của các lớp học lập trình Android nâng cao trong Kotlin.
Giới thiệu
Để tham gia lớp học lập trình này, bạn có thể xem đoạn video là một cách để xác định những khu vực mà một hình ảnh, canvas hoặc canvas đang được vẽ hoặc không được vẽ trên màn hình. Một mục đích của việc cắt bớt là giảm quá mức. Tràn màn hình là khi một pixel trên màn hình được vẽ nhiều lần để hiển thị hình ảnh cuối cùng. Khi giảm tình trạng vẽ quá mức, bạn sẽ giảm thiểu số lần pixel hoặc vùng hiển thị được vẽ để tối đa hóa hiệu suất vẽ. Bạn cũng có thể sử dụng tính năng cắt để tạo hiệu ứng thú vị trong thiết kế giao diện người dùng và ảnh động.
Ví dụ: khi vẽ một ngăn xếp các thẻ chồng chéo như minh họa bên dưới, thay vì vẽ đầy đủ từng thẻ từ dưới lên, việc vẽ chỉ các phần hiển thị sẽ hiệu quả hơn. "Thường thì", vì các thao tác cắt cũng có chi phí và nhìn chung, hệ thống Android làm rất nhiều việc tối ưu hoá bản vẽ.
Để chỉ vẽ các phần hiển thị của thẻ, bạn chỉ định khu vực cắt cho mỗi thẻ. Ví dụ: trong sơ đồ dưới đây, khi hình chữ nhật cắt được áp dụng cho một hình ảnh, chỉ phần bên trong hình chữ nhật đó được hiển thị.
Vùng cắt thường là một hình chữ nhật nhưng có thể là bất kỳ hình dạng hoặc tổ hợp hình dạng nào, thậm chí là văn bản. Bạn cũng có thể chỉ định xem bạn muốn bao gồm hay loại trừ khu vực bên trong vùng cắt. Ví dụ: bạn có thể tạo vùng cắt hình tròn và chỉ hiển thị những nội dung bên ngoài vòng tròn.
Trong lớp học lập trình này, bạn sẽ thử nghiệm nhiều cách cắt.
Kiến thức bạn cần có
Bạn cần thông thạo:
- Cách tạo một ứng dụng bằng
Activity
và chạy ứng dụng này bằng Android Studio. - Cách tạo và vẽ trên
Canvas
. - Cách tạo
View
tùy chỉnh và ghi đèonDraw()
vàonSizeChanged()
.
Kiến thức bạn sẽ học được
- Cách cắt các đối tượng để vẽ trên
Canvas
. - Cách lưu và khôi phục trạng thái vẽ của canvas.
- Cách áp dụng các phép biến đổi cho canvas và văn bản.
Bạn sẽ thực hiện
- Tạo một ứng dụng vẽ các hình dạng bị cắt trên màn hình để thể hiện nhiều cách cắt và kết quả của ứng dụng về khả năng hiển thị các hình dạng đó.
- Bạn cũng sẽ vẽ một số văn bản đã dịch và bị lệch.
Ứng dụng ClippingExample minh họa cách bạn có thể sử dụng và kết hợp các hình để chỉ định phần nào của canvas sẽ hiển thị trong chế độ xem. Ứng dụng cuối cùng sẽ trông giống như ảnh chụp màn hình bên dưới.
Bạn sẽ tạo ứng dụng này từ đầu, vì vậy, bạn sẽ phải thiết lập một dự án, xác định thứ nguyên và chuỗi, cũng như khai báo một số biến.
Bước 1: Tạo dự án ClippingExample
- Tạo dự án Kotlin có tên là
ClippingExample
bằng mẫu Empty Activity (Hoạt động trống). Hãy sử dụngcom.example.android
cho tiền tố của tên gói. - Mở
MainActivity.kt
. - Trong phương thức
onCreate()
, hãy thay thế chế độ xem nội dung mặc định và đặt chế độ xem nội dung thành một bản sao mới củaClippedView
. Đây sẽ là chế độ xem tùy chỉnh của bạn cho các ví dụ về việc cắt bớt mà bạn sẽ tạo tiếp theo.
setContentView(ClippedView(this))
- Ở cùng cấp với
MainActivity.kt
, hãy tạo một tệp và lớp Kotlin mới cho chế độ xem tùy chỉnh có tên làClippedView
. Tệp này sẽ mở rộngView
. Hãy ký tên vào phần chữ ký bên dưới. Phần còn lại của công việc sẽ nằm trongClippedView
này. Chú thích@JvmOverloads
sẽ hướng dẫn trình biên dịch Kotlin tạo quá tải cho hàm này để thay thế các giá trị tham số mặc định.
class ClippedView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
}
Bước 2: Thêm thứ nguyên và tài nguyên chuỗi
- Xác định thứ nguyên mà bạn sẽ sử dụng cho các chế độ xem bị cắt bớt trong tệp tài nguyên mới trong
res/values/dimens.xml
. Các thứ nguyên mặc định này được mã hóa cứng và định cỡ vừa với màn hình nhỏ.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="clipRectRight">90dp</dimen>
<dimen name="clipRectBottom">90dp</dimen>
<dimen name="clipRectTop">0dp</dimen>
<dimen name="clipRectLeft">0dp</dimen>
<dimen name="rectInset">8dp</dimen>
<dimen name="smallRectOffset">40dp</dimen>
<dimen name="circleRadius">30dp</dimen>
<dimen name="textOffset">20dp</dimen>
<dimen name="strokeWidth">4dp</dimen>
<dimen name="textSize">18sp</dimen>
</resources>
Để ứng dụng trông đẹp hơn trên màn hình lớn hơn (và dễ xem hơn, bạn có thể tạo tệp dimens
với giá trị lớn hơn chỉ áp dụng cho màn hình lớn hơn).
- Trong Android Studio, nhấp chuột phải vào thư mục values rồi chọn New > Value Resource file.
- Trong hộp thoại Tệp tài nguyên mới, hãy gọi tệp
dimens
. Trong Bộ định tính có sẵn, hãy chọn Chiều rộng màn hình nhỏ nhất và nhấp vào nút >> để thêm bộ lọc vào bộ hạn định được chọn. Nhập 480 vào hộp Chiều rộng màn hình nhỏ nhất rồi nhấp vào OK.
- Tệp sẽ xuất hiện trong thư mục giá trị của bạn như được hiển thị dưới đây.
- Nếu bạn không thấy tệp đó, hãy chuyển sang chế độ xem Tệp dự án. Đường dẫn đầy đủ của tệp mới như sau:
ClippingExample/app/src/main/res/values-sw480dp/dimens.xml
.
- Thay thế nội dung mặc định của tệp
values-sw480dp/dimens.xml
bằng các phương diện bên dưới.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="clipRectRight">120dp</dimen>
<dimen name="clipRectBottom">120dp</dimen>
<dimen name="rectInset">10dp</dimen>
<dimen name="smallRectOffset">50dp</dimen>
<dimen name="circleRadius">40dp</dimen>
<dimen name="textOffset">25dp</dimen>
<dimen name="strokeWidth">6dp</dimen>
</resources>
- Trong
strings.xml
, hãy thêm các chuỗi sau. Các thao tác này sẽ dùng để hiển thị văn bản trên canvas.
<string name="clipping">Clipping</string>
<string name="translated">translated text</string>
<string name="skewed">"Skewed and "</string>
Bước 3: Tạo và khởi tạo đối tượng Vẽ và đường dẫn
- Chuyển về chế độ xem Android của dự án.
- Trong
ClippedView
, hãy xác định một biếnPaint
để vẽ. Bật tính năng khử răng cưa và sử dụng chiều rộng và cỡ văn bản được xác định trong các thứ nguyên, như minh họa bên dưới.
private val paint = Paint().apply {
// Smooth out edges of what is drawn without affecting shape.
isAntiAlias = true
strokeWidth = resources.getDimension(R.dimen.strokeWidth)
textSize = resources.getDimension(R.dimen.textSize)
}
- Trong
ClippedView
, hãy tạo và khởi chạyPath
để lưu trữ cục bộ đường dẫn của nội dung đã vẽ. Nhậpandroid.graphics.Path
.
private val path = Path()
Bước 4: Thiết lập hình dạng
Trong ứng dụng này, bạn đang hiển thị một số hàng và hai cột hình dạng bị cắt theo nhiều cách.
Điểm chung của họ:
- Một hình chữ nhật lớn (hình vuông) đóng vai trò là một vùng chứa
- Một đường chéo trên hình chữ nhật lớn
- Hình tròn
- Một chuỗi văn bản ngắn
Ở bước này, bạn thiết lập phương diện cho những hình dạng đó từ tài nguyên để chỉ phải nhận được phương diện một lần khi sử dụng sau.
- Trong
ClippedView
, bên dướipath
, hãy thêm biến cho các phương diện cho một hình chữ nhật cắt xung quanh toàn bộ tập hợp các hình dạng.
private val clipRectRight = resources.getDimension(R.dimen.clipRectRight)
private val clipRectBottom = resources.getDimension(R.dimen.clipRectBottom)
private val clipRectTop = resources.getDimension(R.dimen.clipRectTop)
private val clipRectLeft = resources.getDimension(R.dimen.clipRectLeft)
- Thêm biến cho phần lồng hình chữ nhật và độ lệch của một hình chữ nhật nhỏ.
private val rectInset = resources.getDimension(R.dimen.rectInset)
private val smallRectOffset = resources.getDimension(R.dimen.smallRectOffset)
- Thêm một biến cho bán kính của một hình tròn. Đây là bán kính của hình tròn được vẽ bên trong hình chữ nhật.
private val circleRadius = resources.getDimension(R.dimen.circleRadius)
- Thêm giá trị bù và kích thước văn bản cho văn bản được vẽ bên trong hình chữ nhật.
private val textOffset = resources.getDimension(R.dimen.textOffset)
private val textSize = resources.getDimension(R.dimen.textSize)
Bước 4: Thiết lập vị trí hàng và cột
Các hình dạng cho ứng dụng này hiển thị trong hai cột và bốn hàng, được xác định theo giá trị của phương diện được thiết lập ở trên. Thuật toán cho việc này không thuộc lớp học lập trình này, nhưng hãy xem xét khi bạn sao chép vào mã được cung cấp trong bước này.
- Thiết lập tọa độ cho hai cột.
private val columnOne = rectInset
private val columnTwo = columnOne + rectInset + clipRectRight
- Thêm tọa độ cho từng hàng, bao gồm hàng cuối cùng cho văn bản đã chuyển đổi.
private val rowOne = rectInset
private val rowTwo = rowOne + rectInset + clipRectBottom
private val rowThree = rowTwo + rectInset + clipRectBottom
private val rowFour = rowThree + rectInset + clipRectBottom
private val textRow = rowFour + (1.5f * clipRectBottom)
- Chạy ứng dụng của bạn. Ứng dụng sẽ mở với một màn hình trắng trống bên dưới tên ứng dụng.
Trong onDraw()
, bạn gọi các phương thức để vẽ 7 hình chữ nhật bị cắt khác nhau như được minh họa trong ảnh chụp màn hình ứng dụng bên dưới. Các hình chữ nhật đều được vẽ theo cách giống nhau; sự khác biệt duy nhất là vùng và đoạn mã đã xác định của màn hình trên màn hình.
Thuật toán dùng để vẽ các hình chữ nhật hoạt động như minh họa trong sơ đồ và giải thích bên dưới. Tóm lại, bạn vẽ một chuỗi hình chữ nhật bằng cách di chuyển nguồn gốc của Canvas
. Về mặt lý thuyết, điều này bao gồm các bước sau:
(1) Trước tiên, bạn dịch Canvas
sang nơi bạn muốn vẽ hình chữ nhật. Tức là, thay vì tính vị trí của hình chữ nhật tiếp theo và tất cả các hình dạng khác, bạn cần di chuyển nguồn gốc của Canvas
, tức là hệ tọa độ của hình này.
(2) Sau đó, bạn vẽ hình chữ nhật tại nguồn gốc mới của canvas. Tức là, bạn vẽ các hình dạng ở cùng một vị trí trong hệ tọa độ được dịch. Quy trình này đơn giản hơn và hiệu quả hơn một chút.
(3) Cuối cùng, bạn khôi phục Canvas
về Origin
ban đầu.
Dưới đây là thuật toán mà bạn sẽ triển khai:
- Trong
onDraw()
, hãy gọi một hàm để tô màuCanvas
cho màu nền xám và vẽ các hình dạng ban đầu. - Gọi một hàm cho mỗi hình chữ nhật bị cắt bớt và văn bản để vẽ.
Đối với mỗi hình chữ nhật hoặc văn bản:
- Lưu trạng thái hiện tại của
Canvas
để bạn có thể đặt lại về trạng thái ban đầu đó. - Dịch
Origin
của canvas đến vị trí bạn muốn vẽ. - Áp dụng các cắt và hình dạng được cắt
- Vẽ hình chữ nhật hoặc văn bản.
- Khôi phục trạng thái của
Canvas
.
Bước: Ghi đè onDraw()
- Ghi đè
onDraw()
như hiển thị trong mã bên dưới. Bạn gọi một hàm cho mỗi hình dạng mà bạn đang vẽ mà bạn sẽ triển khai sau.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
drawBackAndUnclippedRectangle(canvas)
drawDifferenceClippingExample(canvas)
drawCircularClippingExample(canvas)
drawIntersectionClippingExample(canvas)
drawCombinedClippingExample(canvas)
drawRoundedRectangleClippingExample(canvas)
drawOutsideClippingExample(canvas)
drawSkewedTextExample(canvas)
drawTranslatedTextExample(canvas)
// drawQuickRejectExample(canvas)
}
- Tạo cuống cho mỗi hàm vẽ để mã sẽ tiếp tục biên dịch. Bạn có thể sao chép mã ở bên dưới.
private fun drawBackAndUnclippedRectangle(canvas: Canvas){
}
private fun drawDifferenceClippingExample(canvas: Canvas){
}
private fun drawCircularClippingExample(canvas: Canvas){
}
private fun drawIntersectionClippingExample(canvas: Canvas){
}
private fun drawCombinedClippingExample(canvas: Canvas){
}
private fun drawRoundedRectangleClippingExample(canvas: Canvas){
}
private fun drawOutsideClippingExample(canvas: Canvas){
}
private fun drawTranslatedTextExample(canvas: Canvas){
}
private fun drawSkewedTextExample(canvas: Canvas){
}
private fun drawQuickRejectExample(canvas: Canvas){
}
Ứng dụng vẽ cùng một hình chữ nhật và hình dạng 7 lần, đầu tiên là không cắt xén, sau đó là 6 lần với nhiều đường dẫn cắt được áp dụng. Phương thức drawClippedRectangle()
tính đến mã để vẽ một hình chữ nhật, như minh họa dưới đây.
Bước 1: Tạo phương thức DrawClipped Đưa()
- Tạo một phương thức
drawClippedRectangle()
nhận đối sốcanvas
thuộc loạiCanvas
.
private fun drawClippedRectangle(canvas: Canvas) {
}
- Bên trong phương thức
drawClippedRectangle()
, hãy đặt ranh giới của hình chữ nhật cắt xén cho toàn bộ hình dạng. Áp dụng một hình chữ nhật cắt chỉ buộc hình vẽ chỉ
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom
)
Phương thức Canvas.clipRect(...)
sẽ giảm vùng màn hình mà các thao tác vẽ trong tương lai có thể ghi vào. Trường này đặt các ranh giới cắt là giao điểm không gian của hình chữ nhật cắt hiện tại và hình chữ nhật được chuyển vào clipRect()
. Có nhiều biến thể của phương thức clipRect()
chấp nhận các dạng khác nhau cho khu vực và cho phép nhiều thao tác trên hình chữ nhật cắt.
- Tô màu trắng cho
canvas
. Có! Toàn bộ canvas, vì bạn không vẽ hình chữ nhật, bạn sẽ cắt xén! Vì hình chữ nhật cắt xén, nên chỉ có vùng được xác định bằng hình chữ nhật cắt mới được lấp đầy, tạo ra một hình chữ nhật màu trắng. Phần còn lại của bề mặt vẫn có màu xám.
canvas.drawColor(Color.WHITE)
- Đổi màu thành đỏ và vẽ một đường chéo bên trong hình chữ nhật cắt xén.
paint.color = Color.RED
canvas.drawLine(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom,paint
)
- Đặt màu thành xanh lục và vẽ một vòng tròn bên trong hình chữ nhật cắt xén.
paint.color = Color.GREEN
canvas.drawCircle(
circleRadius,clipRectBottom - circleRadius,
circleRadius,paint
)
- Đặt màu thành xanh lam và vẽ văn bản được căn chỉnh với cạnh bên phải của hình chữ nhật cắt. Sử dụng
canvas.drawText()
để vẽ văn bản.
paint.color = Color.BLUE
// Align the RIGHT side of the text with the origin.
paint.textSize = textSize
paint.textAlign = Paint.Align.RIGHT
canvas.drawText(
context.getString(R.string.clipping),
clipRectRight,textOffset,paint
)
Bước 2: Triển khai phương thức DrawBackAndUnclipedABCD()
- Để xem phương thức
drawClippedRectangle()
trong thực tế, hãy vẽ hình chữ nhật chưa được cắt đầu tiên bằng cách triển khai phương thứcdrawBackAndUnclippedRectangle()
như được minh họa bên dưới. Lưucanvas
, dịch sang vị trí hàng và cột đầu tiên, vẽ bằng cách gọidrawClippedRectangle()
, sau đó khôi phụccanvas
về trạng thái trước đó.
private fun drawBackAndUnclippedRectangle(canvas: Canvas){
canvas.drawColor(Color.GRAY)
canvas.save()
canvas.translate(columnOne,rowOne)
drawClippedRectangle(canvas)
canvas.restore()
}
- Chạy ứng dụng của bạn. Bạn sẽ thấy hình chữ nhật màu trắng đầu tiên có vòng tròn, đường màu đỏ và văn bản trên nền màu xám.
Trong các phương pháp cắt mẫu sau đây, bạn áp dụng nhiều kiểu kết hợp vùng cắt để đạt được hiệu ứng đồ họa và tìm hiểu cách kết hợp vùng cắt để tạo hình dạng cần thiết.
Mỗi phương thức trong số đó tuân theo một mẫu.
- Lưu trạng thái hiện tại của canvas:
canvas.
save(
)
Ngữ cảnh hoạt động duy trì một ngăn xếp các trạng thái vẽ. Trạng thái bản vẽ bao gồm ma trận biến đổi hiện tại và vùng cắt hiện tại. Bạn có thể lưu trạng thái hiện tại, thực hiện các hành động thay đổi trạng thái vẽ (chẳng hạn như dịch hoặc xoay canvas), sau đó khôi phục trạng thái vẽ đã lưu. (Lưu ý: Lệnh này giống như lệnh "stash" lệnh trong git!).
Khi bản vẽ của bạn bao gồm các phép biến đổi, việc chuyển chuỗi và hủy bỏ các phép biến đổi bằng cách đảo ngược các phép biến đổi đó sẽ dễ xảy ra lỗi. Ví dụ: nếu bạn dịch, kéo giãn rồi xoay, thao tác sẽ nhanh chóng trở nên phức tạp. Thay vào đó, hãy lưu trạng thái của canvas, áp dụng các phép biến đổi, vẽ rồi khôi phục trạng thái trước đó.
Ví dụ: bạn có thể xác định vùng cắt và lưu trạng thái đó. Sau đó, hãy dịch canvas này, thêm một vùng cắt và xoay. Sau khi vẽ, bạn có thể khôi phục trạng thái cắt ban đầu và có thể tiến hành dịch khác và biến đổi độ lệch, như minh họa trong sơ đồ.
- Dịch nguồn gốc của canvas thành tọa độ hàng/cột:
canvas.
translate
()
Việc di chuyển phần gốc của canvas sẽ đơn giản hơn nhiều và vẽ cùng một thành phần trong hệ thống tọa độ mới so với việc di chuyển tất cả các thành phần để vẽ. (Mẹo: Bạn có thể sử dụng cùng một kỹ thuật cho các thành phần xoay vòng.)
- Áp dụng các phép biến đổi cho
path
, nếu có. - Áp dụng cắt:
canvas.clipPath(path)
- Vẽ hình dạng:
drawClippedRectangle() or drawText()
- Khôi phục trạng thái canvas trước đó:
canvas.restore()
Bước 1: Triển khai DrawDifferenceClippingExample(canvas)
Thêm mã để vẽ hình chữ nhật thứ hai, sử dụng sự khác biệt giữa hai hình chữ nhật cắt để tạo hiệu ứng khung ảnh.
Hãy sử dụng mã bên dưới để làm những việc sau:
- Lưu ảnh in trên vải canvas.
- Dịch nguồn gốc của canvas thành không gian mở sang hàng đầu tiên, cột thứ hai, ở bên phải hình chữ nhật đầu tiên.
- Áp dụng hai hình chữ nhật cắt. Toán tử
DIFFERENCE
trừ đi hình chữ nhật thứ hai từ hình đầu tiên.
- Gọi phương thức
drawClippedRectangle()
để vẽ canvas đã sửa đổi. - Khôi phục trạng thái canvas.
private fun drawDifferenceClippingExample(canvas: Canvas) {
canvas.save()
// Move the origin to the right for the next rectangle.
canvas.translate(columnTwo,rowOne)
// Use the subtraction of two clipping rectangles to create a frame.
canvas.clipRect(
2 * rectInset,2 * rectInset,
clipRectRight - 2 * rectInset,
clipRectBottom - 2 * rectInset
)
// The method clipRect(float, float, float, float, Region.Op
// .DIFFERENCE) was deprecated in API level 26. The recommended
// alternative method is clipOutRect(float, float, float, float),
// which is currently available in API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O){
canvas.clipRect(
4 * rectInset,4 * rectInset,
clipRectRight - 4 * rectInset,
clipRectBottom - 4 * rectInset,
Region.Op.DIFFERENCE
)
} else {
canvas.clipOutRect(
4 * rectInset,4 * rectInset,
clipRectRight - 4 * rectInset,
clipRectBottom - 4 * rectInset
)
}
drawClippedRectangle(canvas)
canvas.restore()
}
- Chạy ứng dụng của bạn và ứng dụng sẽ trông như thế này.
Bước 2: Triển khai DrawCirularClippingExample(canvas)
Tiếp theo, thêm mã để vẽ một hình chữ nhật sử dụng vùng cắt hình tròn được tạo từ một đường tròn, về cơ bản loại bỏ (không vẽ) hình tròn đó và do đó hiển thị nền màu xám.
private fun drawCircularClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne, rowTwo)
// Clears any lines and curves from the path but unlike reset(),
// keeps the internal data structure for faster reuse.
path.rewind()
path.addCircle(
circleRadius,clipRectBottom - circleRadius,
circleRadius,Path.Direction.CCW
)
// The method clipPath(path, Region.Op.DIFFERENCE) was deprecated in
// API level 26. The recommended alternative method is
// clipOutPath(Path), which is currently available in
// API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
canvas.clipPath(path, Region.Op.DIFFERENCE)
} else {
canvas.clipOutPath(path)
}
drawClippedRectangle(canvas)
canvas.restore()
}
Bước 3: Triển khai DrawIntersectionClippingExample(canvas)
Tiếp theo, thêm mã để vẽ giao điểm của hai hình chữ nhật cắt ở hàng và cột thứ hai.
Lưu ý rằng tùy thuộc vào độ phân giải màn hình, giao diện của khu vực này sẽ khác nhau. Hãy thử nghiệm phương diện smallRectOffset
để thay đổi kích thước của khu vực hiển thị. smallRectOffset
nhỏ hơn sẽ dẫn đến một vùng lớn hơn trên màn hình.
private fun drawIntersectionClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnTwo,rowTwo)
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight - smallRectOffset,
clipRectBottom - smallRectOffset
)
// The method clipRect(float, float, float, float, Region.Op
// .INTERSECT) was deprecated in API level 26. The recommended
// alternative method is clipRect(float, float, float, float), which
// is currently available in API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
canvas.clipRect(
clipRectLeft + smallRectOffset,
clipRectTop + smallRectOffset,
clipRectRight,clipRectBottom,
Region.Op.INTERSECT
)
} else {
canvas.clipRect(
clipRectLeft + smallRectOffset,
clipRectTop + smallRectOffset,
clipRectRight,clipRectBottom
)
}
drawClippedRectangle(canvas)
canvas.restore()
}
Bước 4: Triển khai DrawDrawdClippingExample(canvas)
Tiếp theo, hãy kết hợp các hình dạng, một hình tròn và một hình chữ nhật và vẽ bất kỳ đường dẫn nào để xác định vùng cắt.
private fun drawCombinedClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne, rowThree)
path.rewind()
path.addCircle(
clipRectLeft + rectInset + circleRadius,
clipRectTop + circleRadius + rectInset,
circleRadius,Path.Direction.CCW
)
path.addRect(
clipRectRight / 2 - circleRadius,
clipRectTop + circleRadius + rectInset,
clipRectRight / 2 + circleRadius,
clipRectBottom - rectInset,Path.Direction.CCW
)
canvas.clipPath(path)
drawClippedRectangle(canvas)
canvas.restore()
}
Bước 5: Triển khai DrawDraweded ứng ứngClipClip(các biểu mẫu)
Tiếp theo, hãy thêm một hình chữ nhật tròn thường dùng là hình cắt.
- Ở trên cùng, hãy tạo và khởi chạy một biến hình chữ nhật.
RectF
là một lớp lưu giữ tọa độ hình chữ nhật trong dấu phẩy động.
private var rectF = RectF(
rectInset,
rectInset,
clipRectRight - rectInset,
clipRectBottom - rectInset
)
- Triển khai hàm
drawRoundedRectangleClippingExample()
. HàmaddRoundRect()
lấy một hình chữ nhật, giá trị cho các giá trị x và y của bán kính góc, cũng như hướng hướng của đường cong hình chữ nhật hình tròn\39;Path.Direction
chỉ định cách các hình dạng đóng (ví dụ: hình chữ nhật, hình bầu dục) được định hướng khi được thêm vào một đường dẫn.CCW
là viết tắt của ngược chiều kim đồng hồ.
private fun drawRoundedRectangleClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnTwo,rowThree)
path.rewind()
path.addRoundRect(
rectF,clipRectRight / 4,
clipRectRight / 4, Path.Direction.CCW
)
canvas.clipPath(path)
drawClippedRectangle(canvas)
canvas.restore()
}
Bước 6: Triển khai DrawOutsideClippingExample(canvas)
Ghim các thành phần bên ngoài xung quanh hình chữ nhật bằng cách tăng gấp đôi phần lồng hình chữ nhật.
private fun drawOutsideClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne,rowFour)
canvas.clipRect(2 * rectInset,2 * rectInset,
clipRectRight - 2 * rectInset,
clipRectBottom - 2 * rectInset)
drawClippedRectangle(canvas)
canvas.restore()
}
Bước 7: Triển khai Draw tượngdTextExample(canvas)
Vẽ văn bản không thực sự khác với bất kỳ hình dạng nào khác và bạn có thể áp dụng các phép biến đổi cho văn bản. Ví dụ: bạn có thể dịch văn bản bằng cách dịch canvas và vẽ văn bản.
- Triển khai hàm dưới đây.
private fun drawTranslatedTextExample(canvas: Canvas) {
canvas.save()
paint.color = Color.GREEN
// Align the RIGHT side of the text with the origin.
paint.textAlign = Paint.Align.LEFT
// Apply transformation to canvas.
canvas.translate(columnTwo,textRow)
// Draw text.
canvas.drawText(context.getString(R.string.translated),
clipRectLeft,clipRectTop,paint)
canvas.restore()
}
- Chạy ứng dụng của bạn để xem văn bản đã dịch.
Bước 8: Triển khai DrawSkewedTextExample(canvas)
Bạn cũng có thể sai lệch văn bản. Tức là hãy bóp méo nó theo nhiều cách.
- Hãy tạo hàm bên dưới trong
ClippedView
.
private fun drawSkewedTextExample(canvas: Canvas) {
canvas.save()
paint.color = Color.YELLOW
paint.textAlign = Paint.Align.RIGHT
// Position text.
canvas.translate(columnTwo, textRow)
// Apply skew transformation.
canvas.skew(0.2f, 0.3f)
canvas.drawText(context.getString(R.string.skewed),
clipRectLeft, clipRectTop, paint)
canvas.restore()
}
- Chạy ứng dụng của bạn để xem văn bản bị lệch đã vẽ trước văn bản đã dịch.
Phương pháp quickReject()
Canvas
cho phép bạn kiểm tra xem một hình chữ nhật hoặc đường dẫn đã chỉ định có nằm hoàn toàn bên ngoài khu vực hiển thị hiện tại hay không, sau khi áp dụng tất cả các phép biến đổi.
Phương thức quickReject()
cực kỳ hữu ích khi bạn đang xây dựng những bản vẽ phức tạp hơn và cần làm việc này nhanh nhất có thể. Với quickReject()
, bạn có thể quyết định những đối tượng mà bạn không cần phải vẽ và không cần viết logic về giao lộ của riêng bạn.
- Phương thức
quickReject()
trả vềtrue
nếu hình chữ nhật hoặc đường dẫn hoàn toàn không hiển thị trên màn hình. Đối với các trường hợp trùng lặp một phần, bạn vẫn phải tự kiểm tra. EdgeType
làAA
(Chống bí danh: Xử lý các cạnh bằng cách làm tròn tròn, vì chúng có thể bị khử răng cưa) hoặcBW
(Đen trắng: Xử lý các cạnh bằng cách chỉ làm tròn đến ranh giới pixel gần nhất) để chỉ làm tròn đến pixel gần nhất.
Có một số phiên bản của quickReject()
và bạn cũng có thể tìm thấy các phiên bản đó trong tài liệu.
| QuickTừ chối |
| Quick sản phẩm từ chối |
| QuickDeny |
Trong bài tập này, bạn sẽ vẽ trong một hàng mới, bên dưới văn bản và bên trong clipRect
, như trước đây.
- Trước tiên, bạn gọi
quickReject()
bằng một hình chữ nhậtinClipRectangle
, trùng lặp vớiclipRect
. Vì vậy,quickReject()
trả về false,clipRect
được lấp đầy bằngBLACK
và hình chữ nhậtinClipRectangle
được vẽ.
- Sau đó, thay đổi mã và gọi
quickReject()
bằngnotInClipRectangle
.quickReject()
hiện trả về true vàclipRect
được lấp đầy bằngWHITE
, cònnotInClipRectangle
không được vẽ.
Khi bạn có các bản vẽ phức tạp, điều này có thể nhanh chóng cho bạn biết, hình dạng nào nằm hoàn toàn ngoài vùng cắt và bạn có thể phải thực hiện các phép tính và vẽ thêm, vì các hình đó nằm một phần hoặc hoàn toàn bên trong vùng cắt.
Bước: Thử nghiệm với quickTừ()
- Ở cấp cao nhất, hãy tạo một biến cho tọa độ y của một hàng bổ sung.
private val rejectRow = rowFour + rectInset + 2*clipRectBottom
- Thêm hàm
drawQuickRejectExample()
sau vàoClippedView
. Đọc mã vì mã này chứa mọi thứ bạn cần biết để sử dụngquickReject()
.
private fun drawQuickRejectExample(canvas: Canvas) {
val inClipRectangle = RectF(clipRectRight / 2,
clipRectBottom / 2,
clipRectRight * 2,
clipRectBottom * 2)
val notInClipRectangle = RectF(RectF(clipRectRight+1,
clipRectBottom+1,
clipRectRight * 2,
clipRectBottom * 2))
canvas.save()
canvas.translate(columnOne, rejectRow)
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom
)
if (canvas.quickReject(
inClipRectangle, Canvas.EdgeType.AA)) {
canvas.drawColor(Color.WHITE)
}
else {
canvas.drawColor(Color.BLACK)
canvas.drawRect(inClipRectangle, paint
)
}
canvas.restore()
}
- Trong
onDraw()
, hãy hủy nhận xét lệnh gọi củadrawQuickRejectExample()
. - Chạy ứng dụng của bạn và bạn sẽ thấy một hình chữ nhật màu đen, đó là vùng cắt đầy màu sắc và các phần của
inClipRectangle
, vì hai hình chữ nhật này trùng nhau nênquickReject()
trả vềfalse
vàinClipRectangle
được vẽ.
- Trong
drawQuickRejectExample()
, hãy thay đổi mã để chạyquickReject()
so vớinotInClipRectangle.
Bây giờ,quickReject()
sẽ trả vềtrue
và vùng cắt được tô màu trắng.
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-clipping
Hoặc bạn có thể tải kho lưu trữ xuống dưới dạng tệp Zip, giải nén và mở tệp đó trong Android Studio.
Context
của một hoạt động duy trì trạng thái duy trì các phép biến đổi và đoạn mã choCanvas
.- Hãy dùng
canvas.save()
vàcanvas.restore()
để vẽ và quay lại trạng thái ban đầu của canvas. - Để vẽ nhiều hình dạng trên một canvas, bạn có thể tính vị trí của chúng hoặc bạn có thể di chuyển (dịch) nguồn gốc của bề mặt vẽ. Quy trình sau có thể giúp việc tạo các phương thức tiện ích cho trình tự vẽ lặp lại trở nên dễ dàng hơn.
- Vùng cắt có thể là bất kỳ hình dạng, tổ hợp hình dạng hoặc đường dẫn nào.
- Bạn có thể thêm, trừ và phân tách các khu vực cắt để có được chính xác khu vực bạn cần.
- Bạn có thể áp dụng các phép biến đổi cho văn bản bằng cách biến đổi canvas.
- Phương pháp
quickReject()
Canvas
cho phép bạn kiểm tra xem một hình chữ nhật hoặc đường dẫn đã chỉ định có nằm hoàn toàn bên ngoài những khu vực đang hiển thị hay không.
Khóa học từ Udacity:
Tài liệu dành cho nhà phát triển Android:
- Lớp
Canvas
- Lớp
Bitmap
- Lớp
View
- Lớp
Paint
- Cấu hình
Bitmap.config
- Toán tử
Region.Op
- Lớp
Path
- Lớp
Canvas
- Lớp
Bitmap
- Lớp
View
- Lớp
Paint
- Cấu hình
Bitmap.config
- Toán tử
Region.Op
- Lớp
Path
- Công cụ đồ họa của
android.graphics
- Cấu hình
Bitmap.Config
Canvas
- Canvas và bản vẽ
- Canvas.translate() có chức năng gì
- Tìm hiểu lưu() và khôi phục() cho ngữ cảnh Canvas
- quay video
- quá khổ.
@JvmOverloads
Đồng thời, xem loạt bài viết Cấu trúc đồ họa để biết giải thích chi tiết về cách khung Android vẽ lên màn hình.
Phần này liệt kê các bài tập về nhà có thể được giao cho học viên đang làm việc qua lớp học lập trình này trong khóa học do người hướng dẫn tổ chức. Người hướng dẫn có thể làm những việc sau:
- Giao bài tập về nhà nếu được yêu cầu.
- Trao đổi với học viên 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 những đề xuất này ít hay nhiều tùy ý. Do đó, họ có thể thoải mái giao 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ự mình làm việc qua lớp học lập trình này, hãy thoải mái sử dụng các bài tập về nhà này để kiểm tra kiến thức của bạn.
Trả lời những câu hỏi này
Câu hỏi 1
Bạn sử dụng phương pháp nào để loại trừ hiệu quả các hình dạng đang được vẽ?
▢ excludeFromDrawing()
▢ quickReject()
▢ onDraw()
▢ clipRect()
Câu hỏi 2
Canvas.save()
và Canvas.restore()
lưu và khôi phục thông tin nào?
▢ Màu, độ rộng đường, v.v.
▢ Chỉ chuyển đổi hiện tại
▢ Khu vực biến đổi và khu vực cắt hiện tại
▢ Chỉ vùng cắt hiện tại
Câu hỏi 3
Paint.Align
xác định:
▢ Cách căn chỉnh các hình vẽ sau
▢ Phía bên gốc của văn bản được vẽ
▢ Vị trí căn chỉnh trong vùng cắt
▢ Phía cạnh của văn bản để căn chỉnh theo nguồn gốc
Để xem đường liên kết đến các lớp học lập trình khác trong khóa học này, hãy xem trang đích Nâng cao cho Android trong Kotlin.