Compose มีตัวแก้ไขมากมายสำหรับลักษณะการทำงานทั่วไปตั้งแต่เริ่มต้น แต่คุณก็สร้างตัวแก้ไขที่กำหนดเองได้เช่นกัน
ตัวแก้ไขมีหลายส่วน ดังนี้
- โรงงานตัวปรับแต่ง
- นี่คือฟังก์ชันส่วนขยายใน
Modifier
ซึ่งมี API ที่เป็นสำนวน สำหรับตัวแก้ไข และช่วยให้เชื่อมโยงตัวแก้ไขเข้าด้วยกันได้อย่างง่ายดาย Modifier Factory จะสร้างองค์ประกอบตัวแก้ไขที่ Compose ใช้เพื่อแก้ไข UI
- นี่คือฟังก์ชันส่วนขยายใน
- องค์ประกอบตัวปรับแต่ง
- คุณสามารถใช้ลักษณะการทำงานของตัวแก้ไขได้ที่นี่
คุณใช้ตัวแก้ไขที่กำหนดเองได้หลายวิธี ทั้งนี้ขึ้นอยู่กับฟังก์ชันการทำงานที่ต้องการ
โดยทั่วไปแล้ว วิธีที่ง่ายที่สุดในการใช้ตัวแก้ไขที่กำหนดเองคือ
การใช้โรงงานตัวแก้ไขที่กำหนดเองซึ่งรวมโรงงานตัวแก้ไขอื่นๆ ที่กำหนดไว้แล้ว
เข้าด้วยกัน หากต้องการลักษณะการทำงานที่กำหนดเองเพิ่มเติม ให้ใช้
องค์ประกอบตัวแก้ไขโดยใช้ Modifier.Node
API ซึ่งอยู่ในระดับที่ต่ำกว่า แต่
มีความยืดหยุ่นมากกว่า
เชื่อมโยงตัวแก้ไขที่มีอยู่เข้าด้วยกัน
โดยปกติแล้ว คุณจะสร้างตัวแก้ไขที่กำหนดเองได้เพียงแค่ใช้ตัวแก้ไขที่มีอยู่
เช่น Modifier.clip()
จะใช้ตัวแก้ไข graphicsLayer
กลยุทธ์นี้ใช้องค์ประกอบตัวแก้ไขที่มีอยู่ และคุณ
ระบุโรงงานตัวแก้ไขที่กำหนดเองของคุณเอง
ก่อนที่จะใช้ตัวแก้ไขที่กำหนดเองของคุณเอง ให้ดูว่าคุณสามารถใช้กลยุทธ์เดียวกันได้หรือไม่
fun Modifier.clip(shape: Shape) = graphicsLayer(shape = shape, clip = true)
หรือหากพบว่าคุณใช้กลุ่มตัวแก้ไขเดียวกันซ้ำๆ บ่อยๆ คุณสามารถ รวมตัวแก้ไขเหล่านั้นไว้ในตัวแก้ไขของคุณเองได้
fun Modifier.myBackground(color: Color) = padding(16.dp) .clip(RoundedCornerShape(8.dp)) .background(color)
สร้างตัวแก้ไขที่กำหนดเองโดยใช้โรงงานตัวแก้ไขที่ใช้ร่วมกันได้
นอกจากนี้ คุณยังสร้างตัวแก้ไขที่กำหนดเองโดยใช้ฟังก์ชันที่ประกอบได้เพื่อส่งค่า ไปยังตัวแก้ไขที่มีอยู่ได้ด้วย ซึ่งเรียกว่าโรงงานตัวแก้ไขที่ประกอบได้
การใช้โรงงานตัวแก้ไขที่ประกอบได้เพื่อสร้างตัวแก้ไขยังช่วยให้ใช้
API ของ Compose ระดับสูงขึ้นได้ เช่น animate*AsState
และ Compose
API ของภาพเคลื่อนไหวที่สนับสนุนโดยสถานะอื่นๆ ตัวอย่างเช่น ข้อมูลโค้ดต่อไปนี้แสดง
ตัวแก้ไขที่เคลื่อนไหวการเปลี่ยนแปลงอัลฟ่าเมื่อเปิด/ปิดใช้
@Composable fun Modifier.fade(enable: Boolean): Modifier { val alpha by animateFloatAsState(if (enable) 0.5f else 1.0f) return this then Modifier.graphicsLayer { this.alpha = alpha } }
หากตัวแก้ไขที่กำหนดเองเป็นวิธีที่สะดวกในการระบุค่าเริ่มต้นจาก
CompositionLocal
วิธีที่ง่ายที่สุดในการติดตั้งใช้งานคือการใช้โรงงานตัวแก้ไขที่ประกอบได้
@Composable fun Modifier.fadedBackground(): Modifier { val color = LocalContentColor.current return this then Modifier.background(color.copy(alpha = 0.5f)) }
วิธีการนี้มีข้อควรระวังบางประการตามรายละเอียดด้านล่าง
ค่า CompositionLocal
จะได้รับการแก้ไขที่เว็บไซต์การเรียกของโรงงานตัวแก้ไข
เมื่อสร้างตัวแก้ไขที่กำหนดเองโดยใช้โรงงานตัวแก้ไขที่ประกอบได้ Composition locals จะใช้ค่าจากโครงสร้าง Composition ที่สร้างขึ้น ไม่ใช่ ที่ใช้ ซึ่งอาจทำให้เกิดผลลัพธ์ที่ไม่คาดคิด ตัวอย่างเช่น ลองดูตัวอย่างตัวแก้ไข local ขององค์ประกอบจากด้านบน ซึ่งมีการใช้งานที่แตกต่างกันเล็กน้อยโดยใช้ฟังก์ชัน Composable
@Composable fun Modifier.myBackground(): Modifier { val color = LocalContentColor.current return this then Modifier.background(color.copy(alpha = 0.5f)) } @Composable fun MyScreen() { CompositionLocalProvider(LocalContentColor provides Color.Green) { // Background modifier created with green background val backgroundModifier = Modifier.myBackground() // LocalContentColor updated to red CompositionLocalProvider(LocalContentColor provides Color.Red) { // Box will have green background, not red as expected. Box(modifier = backgroundModifier) } } }
หากคุณไม่ต้องการให้ตัวปรับทำงานในลักษณะนี้ ให้ใช้ตัวปรับแต่งที่กำหนดเอง
Modifier.Node
แทน เนื่องจากระบบจะแก้ไขตัวปรับแต่งองค์ประกอบอย่างถูกต้องที่เว็บไซต์ที่ใช้งานและสามารถยกขึ้นได้อย่างปลอดภัย
ระบบจะไม่ข้ามตัวแก้ไขฟังก์ชันที่ประกอบกันได้
ระบบจะไม่ข้ามตัวแก้ไขโรงงานที่สามารถเขียนร่วมกันได้เนื่องจากฟังก์ชันที่สามารถเขียนร่วมกันได้ ซึ่งมีค่าที่ส่งคืนจะข้ามไม่ได้ ซึ่งหมายความว่าฟังก์ชันตัวแก้ไข จะเรียกใช้ทุกครั้งที่มีการจัดองค์ประกอบใหม่ ซึ่งอาจมีค่าใช้จ่ายสูงหากมีการจัดองค์ประกอบใหม่ บ่อยครั้ง
ต้องเรียกตัวแก้ไขฟังก์ชันที่ประกอบกันได้ภายในฟังก์ชันที่ประกอบกันได้
เช่นเดียวกับฟังก์ชันที่ใช้ร่วมกันได้ทั้งหมด ตัวแก้ไขโรงงานที่ใช้ร่วมกันได้ต้องเรียกใช้จาก ภายใน Composition ซึ่งจะจำกัดตำแหน่งที่สามารถยกระดับตัวแก้ไขได้ เนื่องจากตัวแก้ไขจะ ยกระดับออกจากองค์ประกอบไม่ได้ ในทางตรงกันข้าม ตัวแก้ไขที่ไม่สามารถคอมโพสได้ โรงงานสามารถยกออกจากฟังก์ชันที่สามารถคอมโพสได้เพื่อให้ใช้ซ้ำได้ง่ายขึ้นและ ปรับปรุงประสิทธิภาพ
val extractedModifier = Modifier.background(Color.Red) // Hoisted to save allocations @Composable fun Modifier.composableModifier(): Modifier { val color = LocalContentColor.current.copy(alpha = 0.5f) return this then Modifier.background(color) } @Composable fun MyComposable() { val composedModifier = Modifier.composableModifier() // Cannot be extracted any higher }
ใช้ลักษณะการทำงานของตัวแก้ไขที่กำหนดเองโดยใช้ Modifier.Node
Modifier.Node
เป็น API ระดับล่างสำหรับการสร้างตัวแก้ไขใน Compose ซึ่งเป็น API เดียวกันกับที่ Compose ใช้ในการติดตั้งใช้งานตัวแก้ไขของตัวเอง และเป็นวิธีที่มีประสิทธิภาพมากที่สุดในการสร้างตัวแก้ไขที่กำหนดเอง
ใช้ตัวแก้ไขที่กำหนดเองโดยใช้ Modifier.Node
การใช้ตัวแก้ไขที่กำหนดเองโดยใช้ Modifier.Node มี 3 ส่วน ดังนี้
- การติดตั้งใช้งาน
Modifier.Node
ที่มีตรรกะและ สถานะของตัวแก้ไข ModifierNodeElement
ที่สร้างและอัปเดตอินสแตนซ์โหนดตัวแก้ไข- โรงงานตัวแก้ไขที่ไม่บังคับตามที่อธิบายไว้ข้างต้น
ModifierNodeElement
เป็นแบบไม่มีสถานะและระบบจะจัดสรรอินสแตนซ์ใหม่ทุกครั้งที่มีการประกอบใหม่ ในขณะที่คลาส Modifier.Node
สามารถเป็นแบบมีสถานะและจะยังคงอยู่แม้จะมีการประกอบใหม่หลายครั้ง และยังนำกลับมาใช้ใหม่ได้ด้วย
ส่วนต่อไปนี้จะอธิบายแต่ละส่วนและแสดงตัวอย่างการสร้าง ตัวแก้ไขที่กำหนดเองเพื่อวาดวงกลม
Modifier.Node
Modifier.Node
การติดตั้งใช้งาน (ในตัวอย่างนี้คือ CircleNode
) จะใช้ฟังก์ชันการทำงานของตัวแก้ไขที่กำหนดเอง
// Modifier.Node private class CircleNode(var color: Color) : DrawModifierNode, Modifier.Node() { override fun ContentDrawScope.draw() { drawCircle(color) } }
ในตัวอย่างนี้ จะวาดวงกลมด้วยสีที่ส่งไปยังฟังก์ชัน ตัวแก้ไข
โหนดจะใช้ Modifier.Node
รวมถึงประเภทโหนด 0 ประเภทขึ้นไปด้วย มี
โหนดประเภทต่างๆ ตามฟังก์ชันการทำงานที่ตัวแก้ไขของคุณต้องการ
ตัวอย่างข้างต้นต้องวาดได้ จึงใช้ DrawModifierNode
ซึ่งช่วยให้แทนที่เมธอดวาดได้
ประเภทที่ใช้ได้มีดังนี้
โหนด |
การใช้งาน |
ลิงก์ตัวอย่าง |
|
||
|
||
การใช้ส่วนติดต่อนี้จะช่วยให้ |
||
|
||
|
||
|
||
|
||
|
||
|
||
ซึ่งจะเป็นประโยชน์ในการรวมการใช้งานหลายโหนดไว้ในโหนดเดียว |
||
อนุญาตให้คลาส |
ระบบจะล้างข้อมูลโหนดโดยอัตโนมัติเมื่อมีการเรียกการอัปเดตในองค์ประกอบที่เกี่ยวข้อง
เนื่องจากตัวอย่างของเราเป็น DrawModifierNode
เมื่อใดก็ตามที่มีการอัปเดตเวลาในองค์ประกอบ โหนดจะทริกเกอร์การวาดใหม่และอัปเดตสีอย่างถูกต้อง คุณเลือกไม่ใช้การลบล้างอัตโนมัติได้ตามรายละเอียดด้านล่าง
ModifierNodeElement
ModifierNodeElement
คือคลาสที่ไม่เปลี่ยนแปลงซึ่งเก็บข้อมูลเพื่อสร้างหรือ
อัปเดตตัวแก้ไขที่กำหนดเอง
// ModifierNodeElement private data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>() { override fun create() = CircleNode(color) override fun update(node: CircleNode) { node.color = color } }
การติดตั้งใช้งาน ModifierNodeElement
ต้องลบล้างเมธอดต่อไปนี้
create
: นี่คือฟังก์ชันที่สร้างอินสแตนซ์ของโหนดตัวแก้ไข ระบบจะเรียกใช้ฟังก์ชันนี้ เพื่อสร้างโหนดเมื่อใช้ตัวแก้ไขเป็นครั้งแรก โดยปกติแล้ว การดำเนินการนี้จะเทียบเท่ากับการสร้างโหนดและกำหนดค่าด้วยพารามิเตอร์ ที่ส่งไปยังโรงงานตัวแก้ไขupdate
: ฟังก์ชันนี้จะเรียกใช้ทุกครั้งที่มีการระบุตัวแก้ไขนี้ใน ตำแหน่งเดียวกับที่โหนดนี้มีอยู่แล้ว แต่มีการเปลี่ยนแปลงพร็อพเพอร์ตี้ ซึ่งจะกำหนดโดยequals
วิธีการของชั้นเรียน ระบบจะส่งโหนดตัวแก้ไขที่สร้างไว้ก่อนหน้านี้เป็นพารามิเตอร์ไปยังการเรียกใช้update
ในตอนนี้ คุณควรอัปเดตพร็อพเพอร์ตี้ของโหนดให้สอดคล้องกับพารามิเตอร์ที่อัปเดต ความสามารถในการนำโหนดกลับมาใช้ใหม่ในลักษณะนี้เป็นกุญแจสำคัญในการ เพิ่มประสิทธิภาพที่Modifier.Node
มอบให้ ดังนั้นคุณต้อง อัปเดตโหนดที่มีอยู่แทนที่จะสร้างโหนดใหม่ในเมธอดupdate
ในตัวอย่างวงกลม สีของโหนดจะได้รับการอัปเดต
นอกจากนี้ การติดตั้งใช้งาน ModifierNodeElement
ยังต้องติดตั้งใช้งาน
equals
และ hashCode
ด้วย update
จะเรียกใช้ก็ต่อเมื่อการเปรียบเทียบเท่ากับ
องค์ประกอบก่อนหน้าแสดงผลเป็นเท็จ
ตัวอย่างด้านบนใช้คลาสข้อมูลเพื่อให้บรรลุเป้าหมายนี้ ระบบจะใช้วิธีการเหล่านี้เพื่อ
ตรวจสอบว่าต้องอัปเดตโหนดหรือไม่ หากองค์ประกอบมีพร็อพเพอร์ตี้ที่ไม่ได้มีส่วนช่วยในการพิจารณาว่าต้องอัปเดตโหนดหรือไม่ หรือคุณต้องการหลีกเลี่ยงคลาสข้อมูลเพื่อเหตุผลด้านความเข้ากันได้แบบไบนารี คุณก็สามารถใช้ equals
และ hashCode
ด้วยตนเองได้ เช่น องค์ประกอบตัวแก้ไขการเว้นวรรค
โรงงานตัวปรับแต่ง
นี่คือพื้นผิว API สาธารณะของตัวแก้ไข การใช้งานส่วนใหญ่เพียงแค่ สร้างองค์ประกอบตัวแก้ไขและเพิ่มลงในเชนตัวแก้ไข
// Modifier factory fun Modifier.circle(color: Color) = this then CircleElement(color)
ตัวอย่างที่สมบูรณ์
ทั้ง 3 ส่วนนี้รวมกันเพื่อสร้างตัวแก้ไขที่กำหนดเองเพื่อวาดวงกลม
โดยใช้ Modifier.Node
API ดังนี้
// Modifier factory fun Modifier.circle(color: Color) = this then CircleElement(color) // ModifierNodeElement private data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>() { override fun create() = CircleNode(color) override fun update(node: CircleNode) { node.color = color } } // Modifier.Node private class CircleNode(var color: Color) : DrawModifierNode, Modifier.Node() { override fun ContentDrawScope.draw() { drawCircle(color) } }
สถานการณ์ทั่วไปที่ใช้ Modifier.Node
เมื่อสร้างตัวแก้ไขที่กำหนดเองด้วย Modifier.Node
คุณอาจพบสถานการณ์ทั่วไปบางอย่างดังนี้
ไม่มีพารามิเตอร์
หากตัวแก้ไขไม่มีพารามิเตอร์ ก็ไม่จำเป็นต้องอัปเดต และไม่จำเป็นต้องเป็นคลาสข้อมูล ต่อไปนี้คือตัวอย่างการใช้งาน ตัวแก้ไขที่ใช้ระยะเว้นคงที่กับ Composable
fun Modifier.fixedPadding() = this then FixedPaddingElement data object FixedPaddingElement : ModifierNodeElement<FixedPaddingNode>() { override fun create() = FixedPaddingNode() override fun update(node: FixedPaddingNode) {} } class FixedPaddingNode : LayoutModifierNode, Modifier.Node() { private val PADDING = 16.dp override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints ): MeasureResult { val paddingPx = PADDING.roundToPx() val horizontal = paddingPx * 2 val vertical = paddingPx * 2 val placeable = measurable.measure(constraints.offset(-horizontal, -vertical)) val width = constraints.constrainWidth(placeable.width + horizontal) val height = constraints.constrainHeight(placeable.height + vertical) return layout(width, height) { placeable.place(paddingPx, paddingPx) } } }
การอ้างอิงองค์ประกอบในเครื่อง
ตัวแก้ไข Modifier.Node
จะไม่สังเกตการเปลี่ยนแปลงในออบเจ็กต์สถานะ Compose โดยอัตโนมัติ เช่น CompositionLocal
ข้อได้เปรียบของตัวแก้ไข Modifier.Node
เหนือตัวแก้ไขที่สร้างขึ้นด้วยโรงงานที่ใช้ร่วมกันได้คือตัวแก้ไขสามารถ
อ่านค่าของ CompositionLocal จากตำแหน่งที่ใช้ตัวแก้ไขใน
แผนผัง UI ไม่ใช่ตำแหน่งที่จัดสรรตัวแก้ไข โดยใช้ currentValueOf
อย่างไรก็ตาม อินสแตนซ์โหนดตัวแก้ไขจะไม่สังเกตการเปลี่ยนแปลงสถานะโดยอัตโนมัติ หากต้องการ ตอบสนองต่อการเปลี่ยนแปลงการเรียบเรียงเพลงในเครื่องโดยอัตโนมัติ คุณสามารถอ่านค่าปัจจุบัน ภายในขอบเขตได้
DrawModifierNode
:ContentDrawScope
LayoutModifierNode
:MeasureScope
&IntrinsicMeasureScope
SemanticsModifierNode
:SemanticsPropertyReceiver
ตัวอย่างนี้จะสังเกตค่าของ LocalContentColor
เพื่อวาดพื้นหลัง
ตามสีของค่า เนื่องจาก ContentDrawScope
จะสังเกตการเปลี่ยนแปลงสแนปชอต การดำเนินการนี้จะวาดใหม่โดยอัตโนมัติเมื่อค่าของ LocalContentColor
เปลี่ยนแปลง
class BackgroundColorConsumerNode : Modifier.Node(), DrawModifierNode, CompositionLocalConsumerModifierNode { override fun ContentDrawScope.draw() { val currentColor = currentValueOf(LocalContentColor) drawRect(color = currentColor) drawContent() } }
หากต้องการตอบสนองต่อการเปลี่ยนแปลงสถานะภายนอกขอบเขตและอัปเดตตัวแก้ไขโดยอัตโนมัติ ให้ใช้ ObserverModifierNode
เช่น Modifier.scrollable
ใช้เทคนิคนี้เพื่อ
สังเกตการเปลี่ยนแปลงใน LocalDensity
ตัวอย่างแบบง่ายแสดงอยู่ด้านล่าง
class ScrollableNode : Modifier.Node(), ObserverModifierNode, CompositionLocalConsumerModifierNode { // Place holder fling behavior, we'll initialize it when the density is available. val defaultFlingBehavior = DefaultFlingBehavior(splineBasedDecay(UnityDensity)) override fun onAttach() { updateDefaultFlingBehavior() observeReads { currentValueOf(LocalDensity) } // monitor change in Density } override fun onObservedReadsChanged() { // if density changes, update the default fling behavior. updateDefaultFlingBehavior() } private fun updateDefaultFlingBehavior() { val density = currentValueOf(LocalDensity) defaultFlingBehavior.flingDecay = splineBasedDecay(density) } }
ตัวปรับแต่งการเคลื่อนไหว
การติดตั้งใช้งาน Modifier.Node
มีสิทธิ์เข้าถึง coroutineScope
ซึ่งจะช่วยให้ใช้ Compose Animatable APIs ได้ ตัวอย่างเช่น ข้อมูลโค้ดนี้จะแก้ไข CircleNode
จากด้านบนให้จางเข้าและออกซ้ำๆ
class CircleNode(var color: Color) : Modifier.Node(), DrawModifierNode { private lateinit var alpha: Animatable<Float, AnimationVector1D> override fun ContentDrawScope.draw() { drawCircle(color = color, alpha = alpha.value) drawContent() } override fun onAttach() { alpha = Animatable(1f) coroutineScope.launch { alpha.animateTo( 0f, infiniteRepeatable(tween(1000), RepeatMode.Reverse) ) { } } } }
สถานะการแชร์ระหว่างตัวแก้ไขโดยใช้การมอบสิทธิ์
Modifier.Node
ตัวแก้ไขสามารถมอบสิทธิ์ให้กับโหนดอื่นๆ ได้ กรณีการใช้งานสำหรับฟีเจอร์นี้มีหลายอย่าง เช่น การแยกการใช้งานทั่วไปในตัวแก้ไขต่างๆ
แต่ก็ยังใช้เพื่อแชร์สถานะทั่วไปในตัวแก้ไขได้ด้วย
ตัวอย่างเช่น การติดตั้งใช้งานพื้นฐานของโหนดตัวแก้ไขที่คลิกได้ซึ่งแชร์ข้อมูลการโต้ตอบมีดังนี้
class ClickableNode : DelegatingNode() { val interactionData = InteractionData() val focusableNode = delegate( FocusableNode(interactionData) ) val indicationNode = delegate( IndicationNode(interactionData) ) }
การเลือกไม่ใช้การล้างข้อมูลโหนดอัตโนมัติ
โหนด Modifier.Node
จะล้างข้อมูลโดยอัตโนมัติเมื่อการเรียก ModifierNodeElement
ที่เกี่ยวข้องได้รับการอัปเดต บางครั้งในตัวแก้ไขที่ซับซ้อนมากขึ้น คุณอาจต้องการเลือกไม่ใช้ลักษณะการทำงานนี้เพื่อให้ควบคุมได้ละเอียดยิ่งขึ้นเมื่อตัวแก้ไขทำให้เฟสไม่ถูกต้อง
ซึ่งจะมีประโยชน์อย่างยิ่งหากตัวแก้ไขที่กำหนดเองแก้ไขทั้งเลย์เอาต์และ
การวาด การเลือกไม่ใช้การลบล้างอัตโนมัติจะช่วยให้คุณลบล้างการวาดได้เฉพาะเมื่อมีการเปลี่ยนแปลงพร็อพเพอร์ตี้ที่เกี่ยวข้องกับการวาดเท่านั้น เช่น color
และไม่ลบล้างเลย์เอาต์
ซึ่งจะช่วยปรับปรุงประสิทธิภาพของตัวแก้ไขได้
ตัวอย่างสมมติของกรณีนี้แสดงอยู่ด้านล่างโดยมีตัวแก้ไขที่มีแลมบ์ดา
color
, size
และ onClick
เป็นพร็อพเพอร์ตี้ ตัวแก้ไขนี้จะทำให้เฉพาะสิ่งที่จำเป็นเป็นโมฆะ และข้ามการทำให้เป็นโมฆะที่ไม่จำเป็น
class SampleInvalidatingNode( var color: Color, var size: IntSize, var onClick: () -> Unit ) : DelegatingNode(), LayoutModifierNode, DrawModifierNode { override val shouldAutoInvalidate: Boolean get() = false private val clickableNode = delegate( ClickablePointerInputNode(onClick) ) fun update(color: Color, size: IntSize, onClick: () -> Unit) { if (this.color != color) { this.color = color // Only invalidate draw when color changes invalidateDraw() } if (this.size != size) { this.size = size // Only invalidate layout when size changes invalidateMeasurement() } // If only onClick changes, we don't need to invalidate anything clickableNode.update(onClick) } override fun ContentDrawScope.draw() { drawRect(color) } override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints ): MeasureResult { val size = constraints.constrain(size) val placeable = measurable.measure(constraints) return layout(size.width, size.height) { placeable.place(0, 0) } } }