สร้างตัวแก้ไขที่กำหนดเอง

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 จะช่วยให้กฎลบล้างวิธีการวาดได้

ประเภทที่ใช้ได้มีดังนี้

โหนด

การใช้งาน

ลิงก์ตัวอย่าง

LayoutModifierNode

Modifier.Node ที่เปลี่ยนวิธีวัดและจัดวางเนื้อหาที่รวมไว้

ตัวอย่าง

DrawModifierNode

Modifier.Node ที่ดึงเข้าไปในพื้นที่ของเลย์เอาต์

ตัวอย่าง

CompositionLocalConsumerModifierNode

การใช้อินเทอร์เฟซนี้จะช่วยให้ Modifier.Node อ่านองค์ประกอบท้องถิ่นได้

ตัวอย่าง

SemanticsModifierNode

Modifier.Node ที่เพิ่มคีย์/ค่าความหมายเพื่อใช้ในการทดสอบ การช่วยเหลือพิเศษ และกรณีการใช้งานที่คล้ายกัน

ตัวอย่าง

PointerInputModifierNode

Modifier.Node ที่ได้รับ PointerInputChanges

ตัวอย่าง

ParentDataModifierNode

Modifier.Node ที่ให้ข้อมูลกับเลย์เอาต์หลัก

ตัวอย่าง

LayoutAwareModifierNode

Modifier.Node ที่ได้รับ Callback onMeasured และ onPlaced

ตัวอย่าง

GlobalPositionAwareModifierNode

Modifier.Node ที่ได้รับการติดต่อกลับ onGloballyPositioned พร้อม LayoutCoordinates สุดท้ายของเลย์เอาต์เมื่อตำแหน่งส่วนกลางของเนื้อหาอาจมีการเปลี่ยนแปลง

ตัวอย่าง

ObserverModifierNode

Modifier.Node ที่ใช้ ObserverNode สามารถให้การใช้งาน onObservedReadsChanged ของตัวเอง ซึ่งจะถูกเรียกใช้เพื่อตอบสนองต่อการเปลี่ยนแปลงออบเจ็กต์สแนปชอตที่อ่านภายในบล็อก observeReads

ตัวอย่าง

DelegatingNode

Modifier.Node ที่มอบสิทธิ์งานให้กับอินสแตนซ์ Modifier.Node อื่นๆ ได้

วิธีนี้จะมีประโยชน์ในการเขียนการติดตั้งใช้งานหลายโหนดเป็นโหนดเดียว

ตัวอย่าง

TraversableNode

อนุญาตให้คลาส Modifier.Node ข้ามผ่านขึ้น/ลงของโหนดทรีสำหรับคลาสในประเภทเดียวกันหรือสำหรับคีย์เฉพาะ

ตัวอย่าง

โหนดจะถูกทำให้เป็นโมฆะโดยอัตโนมัติเมื่อมีการเรียกใช้การอัปเดตในโหนดที่เกี่ยวข้อง เนื่องจากตัวอย่างของเราคือ 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 ต้องลบล้างเมธอดต่อไปนี้

  1. create: นี่คือฟังก์ชันที่เริ่มสร้างโหนดตัวปรับแต่งของคุณ สิ่งนี้จะได้รับ ถูกเรียกให้สร้างโหนดเมื่อมีการใช้ตัวแก้ไขของคุณเป็นครั้งแรก โดยทั่วไปแล้ว เป็นจำนวนเท่ากับการสร้างโหนดและกำหนดค่าโหนดด้วยพารามิเตอร์ที่ ไปยังโรงงานเครื่องปรับได้
  2. 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

อย่างไรก็ตาม อินสแตนซ์โหนดตัวแก้ไขจะไม่สังเกตการเปลี่ยนแปลงสถานะโดยอัตโนมัติ ถึง ตอบสนองต่อการเปลี่ยนแปลงภายในของบทประพันธ์โดยอัตโนมัติ คุณสามารถอ่าน ภายในขอบเขต

ตัวอย่างนี้สังเกตค่า 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)
        }
    }
}