Compose มีตัวแก้ไขการทำงานทั่วไปมากมายให้ใช้ได้ทันทีตั้งแต่แกะกล่อง แต่คุณก็สร้างตัวแก้ไข ที่กำหนดเองของตัวเองได้เช่นกัน
ตัวปรับแต่งมีหลายส่วน ดังนี้
- โรงงานเครื่องปรับแต่ง
- นี่เป็นฟังก์ชันส่วนขยายใน
Modifier
ซึ่งให้ API ที่มีลักษณะเฉพาะ สำหรับแป้นกดร่วม และช่วยให้เครื่องปรับเชื่อมโยงเข้าด้วยกันได้ง่าย โรงงานแป้นกดร่วมจะสร้างองค์ประกอบตัวปรับแต่งที่ Compose ใช้เพื่อทำการแก้ไข UI ของคุณ
- นี่เป็นฟังก์ชันส่วนขยายใน
- องค์ประกอบตัวปรับแต่ง
- ซึ่งเป็นที่ที่คุณจะนำลักษณะการทำงานของแป้นกดร่วมไปใช้ได้
คุณสามารถใช้งานตัวแก้ไขที่กำหนดเองได้หลายวิธี ขึ้นอยู่กับ
ต้องมีฟังก์ชัน บ่อยครั้งที่วิธีที่ง่ายที่สุดในการนำตัวแก้ไขที่กำหนดเองไปใช้คือ
เพื่อสร้างโรงงานปรับแต่งที่กำหนดเอง ซึ่งรวมทั้ง
โรงงานเครื่องพ่วง ถ้าคุณต้องการการทำงานที่กำหนดเองเพิ่มเติม ให้ใช้
องค์ประกอบตัวปรับแต่งที่ใช้ API ของ Modifier.Node
ซึ่งอยู่ระดับต่ำกว่าแต่
ให้ความยืดหยุ่นมากกว่า
เชื่อมโยงตัวแก้ไขที่มีอยู่เข้าด้วยกัน
คุณมักจะสร้างตัวแก้ไขที่กำหนดเองได้ด้วยการใช้ตัวแก้ไขที่มีอยู่
แป้นกดร่วม ตัวอย่างเช่น 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)
สร้างตัวปรับแต่งที่กำหนดเองโดยใช้โรงงานปรับแต่งที่ประกอบกันได้
นอกจากนี้ คุณยังสร้างตัวแก้ไขที่กำหนดเองโดยใช้ฟังก์ชัน Composable เพื่อส่งผ่านค่าได้ด้วย ลงในตัวปรับแต่งที่มีอยู่ ลักษณะดังกล่าวเรียกว่าโรงงานปรับแต่งที่ประกอบกันได้
การใช้ค่าเริ่มต้นจากโรงงานปรับแต่ง Composable เพื่อสร้างคีย์ตัวปรับแต่งยังทำให้สามารถใช้
API การเขียนระดับสูงกว่า เช่น 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
ค่าตัวปรับแต่งเริ่มต้น:
@Composable fun Modifier.fadedBackground(): Modifier { val color = LocalContentColor.current return this then Modifier.background(color.copy(alpha = 0.5f)) }
วิธีการนี้มีข้อควรระวังบางประการตามรายละเอียดด้านล่าง
ระบบจะแก้ไขค่า CompositionLocal
ที่เว็บไซต์การเรียกใช้ของค่าจากโรงงานของตัวปรับแต่ง
เมื่อสร้างตัวปรับแต่งที่กำหนดเองโดยใช้โรงงานปรับแต่งที่ประกอบกันได้ องค์ประกอบ คนท้องถิ่นจะนำค่าจากแผนผังองค์ประกอบมาสร้าง ไม่ใช่ ซึ่งอาจทำให้ได้ผลลัพธ์ที่ไม่คาดคิด เช่น ลองนำบทประพันธ์เพลง ตัวอย่างคีย์ตัวปรับแต่งภายในจากด้านบน ซึ่งมีการใช้งานแตกต่างกันเล็กน้อยโดยใช้ ฟังก์ชันที่ประกอบกันได้:
@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
แทน เนื่องจากคนท้องถิ่นในการเรียบเรียงจะ
ได้รับการแก้ไขอย่างถูกต้องบนไซต์การใช้งานและสามารถยกขึ้นได้อย่างปลอดภัย
ไม่ข้ามตัวแก้ไขฟังก์ชันที่ประกอบกันได้
ระบบจะไม่ข้ามตัวแก้ไขจากโรงงานที่ประกอบกันได้ เนื่องจากฟังก์ชันที่ประกอบกันได้ ข้ามที่มีค่าส่งกลับไม่ได้ หมายความว่าฟังก์ชันแป้นกดร่วม จะถูกเรียกทุกครั้งที่มีการจัดองค์ประกอบใหม่ ซึ่งอาจมีค่าใช้จ่ายสูงหากปรับแก้ เป็นประจำ
ต้องเรียกใช้ตัวแก้ไขฟังก์ชันที่ประกอบกันได้ภายในฟังก์ชันที่ประกอบกันได้
เช่นเดียวกับฟังก์ชัน Composable ทั้งหมด ต้องเรียกตัวปรับแต่งจากโรงงานที่ประกอบกันได้จาก ภายในองค์ประกอบ ซึ่งจะจำกัดขอบเขตของการใช้ตัวแก้ไขนี้ จะไม่ถูกทำลายจากองค์ประกอบ ในการเปรียบเทียบ ตัวแก้ไขที่ไม่สามารถเขียนได้ สามารถยกโรงงานออกจากฟังก์ชันที่ประกอบกันได้ เพื่อการใช้งานซ้ำและ ปรับปรุงประสิทธิภาพ:
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
ตัวจะไม่เห็นการเปลี่ยนแปลงสถานะการเขียนโดยอัตโนมัติ
ออบเจ็กต์ เช่น CompositionLocal
ตัวแก้ไขข้อได้เปรียบของ Modifier.Node
มีมากกว่า
ตัวแก้ไขที่เพิ่งสร้างขึ้นด้วยโรงงานที่ประกอบกันได้คือฟังก์ชันที่อ่านได้
ค่าการเรียบเรียงภายในจากที่ที่ใช้ตัวแก้ไขใน 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 API ตัวอย่างเช่น ข้อมูลโค้ดนี้จะแก้ไข
CircleNode
จากด้านบนเพื่อค่อยๆ เลือนหายเข้าและออก
class CircleNode(var color: Color) : Modifier.Node(), DrawModifierNode { private val alpha = Animatable(1f) override fun ContentDrawScope.draw() { drawCircle(color = color, alpha = alpha.value) drawContent() } override fun onAttach() { 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
, การเปลี่ยนแปลง และไม่ทำให้เลย์เอาต์เป็นโมฆะ
วิธีนี้จะช่วยปรับปรุงประสิทธิภาพของตัวแก้ไขได้
ตัวอย่างสมมติของ URL นี้แสดงอยู่ด้านล่างพร้อมด้วยตัวปรับแต่งที่มี 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) } } }