แอปจำนวนมากจำเป็นต้องควบคุมสิ่งที่วาดบนหน้าจอได้อย่างแม่นยำ ซึ่งอาจเป็นเพียงการวางกรอบหรือวงกลมบนหน้าจอในตำแหน่งที่เหมาะสม หรืออาจเป็นการจัดเรียงองค์ประกอบกราฟิกอย่างละเอียดในหลายสไตล์
การวาดภาพพื้นฐานด้วยตัวแก้ไขและ DrawScope
วิธีหลักในการวาดอะไรบางอย่างที่กำหนดเองใน Compose คือการใช้ตัวแก้ไข เช่น
Modifier.drawWithContent
Modifier.drawBehind
และ
Modifier.drawWithCache
เช่น หากต้องการวาดอะไรบางอย่างไว้ด้านหลัง Composable คุณสามารถใช้ตัวแก้ไข drawBehind
เพื่อเริ่มเรียกใช้คำสั่งการวาดได้
Spacer( modifier = Modifier .fillMaxSize() .drawBehind { // this = DrawScope } )
หากต้องการเพียง Composable ที่วาด คุณสามารถใช้ Composable
Canvas
ได้ Canvas
Composable เป็น Wrapper ที่สะดวกสำหรับ Modifier.drawBehind
คุณวาง Canvas
ในเลย์เอาต์
ได้เช่นเดียวกับองค์ประกอบ UI อื่นๆ ของ Compose ภายใน
Canvas
คุณสามารถวาดองค์ประกอบพร้อมควบคุมสไตล์และ
ตำแหน่งได้อย่างแม่นยำ
ตัวแก้ไขการวาดทั้งหมดจะแสดง DrawScope
ซึ่งเป็นสภาพแวดล้อมการวาดที่กำหนดขอบเขต
ซึ่งรักษาสถานะของตัวเอง ซึ่งช่วยให้คุณตั้งค่าพารามิเตอร์สำหรับกลุ่มองค์ประกอบกราฟิกได้ DrawScope
มีฟิลด์ที่มีประโยชน์หลายอย่าง เช่น size
,
ออบเจ็กต์ Size
ที่ระบุขนาดปัจจุบันของ DrawScope
หากต้องการวาดภาพ คุณสามารถใช้ฟังก์ชันการวาดภาพมากมายบน DrawScope
ตัวอย่างเช่น โค้ดต่อไปนี้จะวาดสี่เหลี่ยมผืนผ้าที่มุมซ้ายบนของหน้าจอ
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F drawRect( color = Color.Magenta, size = canvasQuadrantSize ) }

ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวแก้ไขการวาดภาพต่างๆ ได้ในเอกสารประกอบตัวแก้ไขกราฟิก
ระบบพิกัด
หากต้องการวาดสิ่งใดสิ่งหนึ่งบนหน้าจอ คุณต้องทราบออฟเซ็ต (x
และ y
) และขนาดของ
รายการ เมื่อใช้เมธอดการวาดหลายๆ เมธอดใน DrawScope
ระบบจะระบุตำแหน่งและขนาด
โดยใช้ค่าพารามิเตอร์เริ่มต้น โดยทั่วไป พารามิเตอร์เริ่มต้นจะวางตำแหน่งไอเทมที่จุด [0, 0]
บน Canvas และระบุ size
เริ่มต้นที่เติมพื้นที่วาดภาพทั้งหมด ดังตัวอย่างด้านบน คุณจะเห็นว่าสี่เหลี่ยมผืนผ้าอยู่ด้านซ้ายบน หากต้องการปรับขนาดและตำแหน่งของ
ไอเทม คุณต้องทำความเข้าใจระบบพิกัดใน Compose
จุดเริ่มต้นของระบบพิกัด ([0,0]
) อยู่ที่พิกเซลซ้ายสุดบนสุดใน
พื้นที่วาดภาพ x
จะเพิ่มขึ้นเมื่อเลื่อนไปทางขวา และ y
จะเพิ่มขึ้นเมื่อเลื่อนลง
![ตารางกริดที่แสดงระบบพิกัดซึ่งแสดงมุมซ้ายบน [0, 0] และมุมขวาล่าง [ความกว้าง, ความสูง]](https://developer.android.com/static/develop/ui/compose/images/graphics/introduction/compose_coordinate_system_drawing.png?authuser=6&hl=th)
เช่น หากต้องการวาดเส้นทแยงมุมจากมุมขวาบนของ
พื้นที่ Canvas ไปยังมุมซ้ายล่าง คุณสามารถใช้ฟังก์ชัน
DrawScope.drawLine()
และระบุออฟเซ็ตเริ่มต้นและสิ้นสุดด้วย
ตำแหน่ง x และ y ที่สอดคล้องกันได้ดังนี้
Canvas(modifier = Modifier.fillMaxSize()) { val canvasWidth = size.width val canvasHeight = size.height drawLine( start = Offset(x = canvasWidth, y = 0f), end = Offset(x = 0f, y = canvasHeight), color = Color.Blue ) }
การเปลี่ยนรูปแบบพื้นฐาน
DrawScope
มีการแปลงเพื่อเปลี่ยนตำแหน่งหรือวิธีเรียกใช้คำสั่งวาด
สเกล
ใช้
DrawScope.scale()
เพื่อเพิ่มขนาดของการดำเนินการวาดภาพตามปัจจัย การดำเนินการ เช่น
scale()
มีผลกับการดำเนินการวาดทั้งหมดภายใน Lambda ที่เกี่ยวข้อง ตัวอย่างเช่น โค้ดต่อไปนี้จะเพิ่ม scaleX
10 เท่าและ scaleY
15 เท่า
Canvas(modifier = Modifier.fillMaxSize()) { scale(scaleX = 10f, scaleY = 15f) { drawCircle(Color.Blue, radius = 20.dp.toPx()) } }

แปลภาษา
ใช้
DrawScope.translate()
เพื่อย้ายการวาดขึ้น ลง ซ้าย หรือขวา ตัวอย่างเช่น โค้ดต่อไปนี้จะเลื่อนภาพวาดไปทางขวา 100 พิกเซลและขึ้นไป 300 พิกเซล
Canvas(modifier = Modifier.fillMaxSize()) { translate(left = 100f, top = -300f) { drawCircle(Color.Blue, radius = 200.dp.toPx()) } }

หมุน
ใช้
DrawScope.rotate()
เพื่อหมุนการวาดรอบจุดหมุน ตัวอย่างเช่น โค้ดต่อไปนี้หมุนสี่เหลี่ยมผืนผ้า 45 องศา
Canvas(modifier = Modifier.fillMaxSize()) { rotate(degrees = 45F) { drawRect( color = Color.Gray, topLeft = Offset(x = size.width / 3F, y = size.height / 3F), size = size / 3F ) } }

rotate()
เพื่อใช้การหมุนกับขอบเขตการวาดปัจจุบัน ซึ่งจะหมุนสี่เหลี่ยมผืนผ้า 45 องศา
ข้างใน
ใช้ DrawScope.inset()
เพื่อปรับพารามิเตอร์เริ่มต้นของDrawScope
ปัจจุบัน
โดยเปลี่ยนขอบเขตการวาดและแปลภาพวาดตามนั้น
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F inset(horizontal = 50f, vertical = 30f) { drawRect(color = Color.Green, size = canvasQuadrantSize) } }
โค้ดนี้จะเพิ่มระยะขอบให้กับคำสั่งการวาดได้อย่างมีประสิทธิภาพ

การเปลี่ยนรูปแบบหลายรายการ
หากต้องการใช้การเปลี่ยนรูปแบบหลายรายการกับภาพวาด ให้ใช้ฟังก์ชัน
DrawScope.withTransform()
ซึ่งจะสร้างและ
ใช้การเปลี่ยนรูปแบบเดียวที่รวมการเปลี่ยนแปลงทั้งหมดที่คุณต้องการ การใช้
withTransform()
มีประสิทธิภาพมากกว่าการเรียกที่ซ้อนกันไปยังการแปลงแต่ละรายการ
เนื่องจากระบบจะทำการแปลงทั้งหมดพร้อมกันใน
การดำเนินการเดียว แทนที่ Compose จะต้องคำนวณและบันทึกการแปลงที่ซ้อนกันแต่ละรายการ
ตัวอย่างเช่น โค้ดต่อไปนี้ใช้ทั้งการแปลและการหมุนกับ สี่เหลี่ยมผืนผ้า
Canvas(modifier = Modifier.fillMaxSize()) { withTransform({ translate(left = size.width / 5F) rotate(degrees = 45F) }) { drawRect( color = Color.Gray, topLeft = Offset(x = size.width / 3F, y = size.height / 3F), size = size / 3F ) } }

withTransform
เพื่อใช้ทั้งการหมุนและการเลื่อน โดยหมุนสี่เหลี่ยมผืนผ้าและเลื่อนไปทางซ้ายการดำเนินการวาดภาพทั่วไป
วาดข้อความ
โดยปกติแล้ว คุณจะใช้ Text
composable เพื่อวาดข้อความใน Compose ได้ อย่างไรก็ตาม หากคุณอยู่ในDrawScope
หรือต้องการวาดข้อความด้วยตนเองพร้อมการปรับแต่ง คุณสามารถใช้วิธีDrawScope.drawText()
ได้
หากต้องการวาดข้อความ ให้สร้าง TextMeasurer
โดยใช้ rememberTextMeasurer
และเรียกใช้ drawText
ด้วยเครื่องมือวัด
val textMeasurer = rememberTextMeasurer() Canvas(modifier = Modifier.fillMaxSize()) { drawText(textMeasurer, "Hello") }

วัดข้อความ
การวาดข้อความจะแตกต่างจากคำสั่งวาดอื่นๆ เล็กน้อย โดยปกติแล้ว คุณ จะให้คำสั่งวาดภาพพร้อมขนาด (ความกว้างและความสูง) เพื่อวาดรูปร่าง/รูปภาพ สำหรับข้อความ มีพารามิเตอร์บางอย่างที่ควบคุมขนาดของข้อความที่แสดงผล เช่น ขนาดแบบอักษร แบบอักษร อักษรควบ และระยะห่างระหว่างตัวอักษร
Compose ช่วยให้คุณใช้ TextMeasurer
เพื่อเข้าถึงขนาดข้อความที่วัดได้โดยขึ้นอยู่กับปัจจัยข้างต้น หากต้องการวาดพื้นหลัง
ด้านหลังข้อความ คุณสามารถใช้ข้อมูลที่วัดได้เพื่อดูขนาดของ
พื้นที่ที่ข้อความใช้
val textMeasurer = rememberTextMeasurer() Spacer( modifier = Modifier .drawWithCache { val measuredText = textMeasurer.measure( AnnotatedString(longTextSample), constraints = Constraints.fixedWidth((size.width * 2f / 3f).toInt()), style = TextStyle(fontSize = 18.sp) ) onDrawBehind { drawRect(pinkColor, size = measuredText.size.toSize()) drawText(measuredText) } } .fillMaxSize() )
ข้อมูลโค้ดนี้จะสร้างพื้นหลังสีชมพูบนข้อความ

การปรับข้อจำกัด ขนาดแบบอักษร หรือพร็อพเพอร์ตี้ใดๆ ที่ส่งผลต่อขนาดที่วัดได้
จะทำให้มีการรายงานขนาดใหม่ คุณสามารถตั้งค่าขนาดคงที่สำหรับทั้ง width
และ height
จากนั้นข้อความจะทำตามTextOverflow
ที่ตั้งไว้ ตัวอย่างเช่น โค้ดต่อไปนี้จะแสดงข้อความในความสูง ⅓ และความกว้าง ⅓ ของพื้นที่ที่คอมโพสได้ และตั้งค่า TextOverflow
เป็น TextOverflow.Ellipsis
val textMeasurer = rememberTextMeasurer() Spacer( modifier = Modifier .drawWithCache { val measuredText = textMeasurer.measure( AnnotatedString(longTextSample), constraints = Constraints.fixed( width = (size.width / 3f).toInt(), height = (size.height / 3f).toInt() ), overflow = TextOverflow.Ellipsis, style = TextStyle(fontSize = 18.sp) ) onDrawBehind { drawRect(pinkColor, size = measuredText.size.toSize()) drawText(measuredText) } } .fillMaxSize() )
ตอนนี้ข้อความจะแสดงในข้อจำกัดโดยมีจุดไข่ปลาที่ท้ายข้อความ

TextOverflow.Ellipsis
โดยมีข้อจำกัดคงที่ในการวัดข้อความวาดรูปภาพ
หากต้องการวาด ImageBitmap
ด้วย DrawScope
ให้โหลดรูปภาพโดยใช้
ImageBitmap.imageResource()
แล้วเรียกใช้ drawImage
val dogImage = ImageBitmap.imageResource(id = R.drawable.dog) Canvas(modifier = Modifier.fillMaxSize(), onDraw = { drawImage(dogImage) })

ImageBitmap
ใน Canvasวาดรูปร่างพื้นฐาน
DrawScope
มีฟังก์ชันวาดรูปร่างมากมาย หากต้องการวาดรูปร่าง ให้ใช้ฟังก์ชันการวาดที่กำหนดไว้ล่วงหน้า เช่น drawCircle
val purpleColor = Color(0xFFBA68C8) Canvas( modifier = Modifier .fillMaxSize() .padding(16.dp), onDraw = { drawCircle(purpleColor) } )
API |
เอาต์พุต |
![]() |
|
![]() |
|
![]() |
|
![]() |
|
![]() |
|
![]() |
|
![]() |
วาดเส้นทาง
เส้นทางคือชุดคำสั่งทางคณิตศาสตร์ที่ทำให้เกิดภาพวาดเมื่อ
ดำเนินการ DrawScope
สามารถวาดเส้นทางโดยใช้วิธี DrawScope.drawPath()
เช่น สมมติว่าคุณต้องการวาดสามเหลี่ยม คุณสร้างเส้นทางด้วยฟังก์ชันต่างๆ เช่น lineTo()
และ moveTo()
โดยใช้ขนาดของพื้นที่วาดได้
จากนั้นเรียกใช้ drawPath()
ด้วยเส้นทางที่สร้างขึ้นใหม่นี้เพื่อรับสามเหลี่ยม
Spacer( modifier = Modifier .drawWithCache { val path = Path() path.moveTo(0f, 0f) path.lineTo(size.width / 2f, size.height / 2f) path.lineTo(size.width, 0f) path.close() onDrawBehind { drawPath(path, Color.Magenta, style = Stroke(width = 10f)) } } .fillMaxSize() )

Path
ในฟีเจอร์เขียนการเข้าถึงออบเจ็กต์ Canvas
DrawScope
จะไม่ให้สิทธิ์เข้าถึงออบเจ็กต์ Canvas
โดยตรง คุณสามารถใช้
DrawScope.drawIntoCanvas()
เพื่อรับ
สิทธิ์เข้าถึงออบเจ็กต์ Canvas
เองซึ่งคุณสามารถเรียกใช้ฟังก์ชันได้
เช่น หากคุณมี Drawable
ที่กำหนดเองซึ่งต้องการวาดลงใน
Canvas คุณสามารถเข้าถึง Canvas และเรียกใช้ Drawable#draw()
โดยส่งออบเจ็กต์
Canvas
ได้ดังนี้
val drawable = ShapeDrawable(OvalShape()) Spacer( modifier = Modifier .drawWithContent { drawIntoCanvas { canvas -> drawable.setBounds(0, 0, size.width.toInt(), size.height.toInt()) drawable.draw(canvas.nativeCanvas) } } .fillMaxSize() )

Drawable
ดูข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับการวาดใน Compose ได้ที่แหล่งข้อมูลต่อไปนี้
- ตัวแก้ไขกราฟิก - ดูข้อมูลเกี่ยวกับตัวแก้ไขการวาดภาพประเภทต่างๆ
- แปรง - ดูวิธีปรับแต่งการวาดเนื้อหา
- เลย์เอาต์และกราฟิกที่กำหนดเองใน Compose - Android Dev Summit 2022 - ดูวิธีสร้าง UI ที่กำหนดเองใน Compose ด้วยเลย์เอาต์ และกราฟิก
- ตัวอย่าง JetLagged - ตัวอย่าง Compose ที่แสดงวิธีวาดกราฟที่กำหนดเอง
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- ตัวปรับแต่งกราฟิก
- กราฟิกในฟีเจอร์เขียน
- เส้นแนวใน Jetpack Compose