แอปจำนวนมากจำเป็นต้องควบคุมสิ่งที่วาดบนหน้าจอได้อย่างแม่นยำ ซึ่งอาจเป็นเพียงการวางกล่องหรือวงกลมบนหน้าจอในตำแหน่งที่เหมาะสม หรืออาจเป็นการจัดเรียงองค์ประกอบกราฟิกอย่างละเอียดในสไตล์ต่างๆ มากมาย
การวาดพื้นฐานด้วยตัวปรับแต่งและ DrawScope
วิธีหลักในการวาดสิ่งต่างๆ ที่กำหนดเองใน Compose คือการใช้ตัวปรับแต่ง เช่น
Modifier.drawWithContent,
Modifier.drawBehind และ
Modifier.drawWithCache
ตัวอย่างเช่น หากต้องการวาดสิ่งต่างๆ ไว้ด้านหลัง Composable คุณสามารถใช้ตัวปรับแต่ง drawBehind เพื่อเริ่มเรียกใช้คำสั่งการวาดได้ดังนี้
Spacer( modifier = Modifier .fillMaxSize() .drawBehind { // this = DrawScope } )
หากต้องการเพียง Composable ที่วาดได้ คุณสามารถใช้
Canvas Composable Composable Canvas เป็น
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 จะเพิ่มขึ้นเมื่อเคลื่อนลง
ตัวอย่างเช่น หากต้องการวาดเส้นทแยงมุมจากมุมขวาบนของ
พื้นที่ 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() จะมีผลกับการดำเนินการวาดทั้งหมดภายในแลมบ์ดาที่เกี่ยวข้อง ตัวอย่างเช่น โค้ดต่อไปนี้จะเพิ่ม 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() เพื่อปรับพารามิเตอร์เริ่มต้นของ current
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 เพื่อใช้ทั้งการหมุนและการแปล โดยจะหมุนสี่เหลี่ยมผืนผ้าและเลื่อนไปทางซ้ายการดำเนินการวาดทั่วไป
วาดข้อความ
หากต้องการวาดข้อความใน Compose โดยทั่วไปคุณสามารถใช้ Composable Text อย่างไรก็ตาม
หากคุณอยู่ใน 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 ที่ตั้งไว้ ตัวอย่างเช่น โค้ดต่อไปนี้จะแสดงข้อความในพื้นที่ ⅓ ของความสูงและ ⅓ ของความกว้างของพื้นที่ Composable และตั้งค่า 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 ใน Composeการเข้าถึงออบเจ็กต์ 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 ปิดอยู่
- ตัวปรับแต่งกราฟิก
- กราฟิกใน Compose
- เส้นแนวการจัดตำแหน่งใน Jetpack Compose