ย้ายข้อมูลไปยัง API ของตัวบ่งชี้และ Ripple

เราได้เปิดตัว API ใหม่เพื่อปรับปรุงประสิทธิภาพการเขียนของคอมโพเนนต์แบบอินเทอร์แอกทีฟที่ใช้ Modifier.clickable API เหล่านี้ช่วยให้Indicationการใช้งานมีประสิทธิภาพมากขึ้น เช่น การกระเพื่อม

androidx.compose.foundation:foundation:1.7.0+ และ androidx.compose.material:material-ripple:1.7.0+ มีการเปลี่ยนแปลง API ดังต่อไปนี้

เลิกใช้งาน

การเปลี่ยนทดแทน

Indication#rememberUpdatedInstance

IndicationNodeFactory

rememberRipple()

ripple() API ใหม่ที่อยู่ในไลบรารี Material แทน

หมายเหตุ: ในบริบทนี้ "คลังวัสดุ" หมายถึง androidx.compose.material:material, androidx.compose.material3:material3, androidx.wear.compose:compose-material และ androidx.wear.compose:compose-material3.

RippleTheme

ดังนี้

  • ใช้ RippleConfigurationAPI ของไลบรารี Material หรือ
  • สร้างการติดตั้งใช้งานการออกแบบระบบของคุณเอง

หน้านี้อธิบายผลกระทบจากการเปลี่ยนแปลงลักษณะการทำงานและวิธีการย้ายข้อมูลไปยัง API ใหม่

การเปลี่ยนแปลงพฤติกรรม

ไลบรารีเวอร์ชันต่อไปนี้มีการเปลี่ยนแปลงลักษณะการทำงานของ Ripple

  • androidx.compose.material:material:1.7.0+
  • androidx.compose.material3:material3:1.3.0+
  • androidx.wear.compose:compose-material:1.4.0+

ไลบรารี Material เวอร์ชันเหล่านี้จะไม่ใช้ rememberRipple() อีกต่อไป แต่จะใช้ API การกระเพื่อมใหม่แทน ด้วยเหตุนี้ จึงไม่ได้ส่งคำค้นหา LocalRippleTheme ดังนั้น หากคุณตั้งค่า LocalRippleTheme ในแอปพลิเคชัน Material components จะไม่ใช้ค่าเหล่านี้

ส่วนต่อไปนี้จะอธิบายวิธีย้ายข้อมูลไปยัง API ใหม่

ย้ายข้อมูลจาก rememberRipple ไปยัง ripple

การใช้คลังวัสดุ

หากคุณใช้ไลบรารี Material ให้แทนที่ rememberRipple() ด้วยการเรียก ripple() จากไลบรารีที่เกี่ยวข้องโดยตรง API นี้จะสร้าง Ripple โดยใช้ค่าที่ได้จาก Material Theme API จากนั้นส่งออบเจ็กต์ที่ส่งคืน ไปยัง Modifier.clickable และ/หรือคอมโพเนนต์อื่นๆ

เช่น ข้อมูลโค้ดต่อไปนี้ใช้ API ที่เลิกใช้งานแล้ว

Box(
    Modifier.clickable(
        onClick = {},
        interactionSource = remember { MutableInteractionSource() },
        indication = rememberRipple()
    )
) {
    // ...
}

คุณควรแก้ไขข้อมูลโค้ดข้างต้นเพื่อทำสิ่งต่อไปนี้

@Composable
private fun RippleExample() {
    Box(
        Modifier.clickable(
            onClick = {},
            interactionSource = remember { MutableInteractionSource() },
            indication = ripple()
        )
    ) {
        // ...
    }
}

โปรดทราบว่า ripple() ไม่ใช่ฟังก์ชันที่ประกอบกันได้อีกต่อไปและไม่จำเป็นต้องจดจำ นอกจากนี้ยังนำไปใช้ซ้ำในคอมโพเนนต์หลายรายการได้ด้วย เช่นเดียวกับ ตัวแก้ไข ดังนั้นให้พิจารณาดึงการสร้าง Ripple ไปยังค่าระดับบนสุดเพื่อ ประหยัดการจัดสรร

การใช้ระบบการออกแบบที่กำหนดเอง

หากคุณกำลังใช้ระบบการออกแบบของคุณเอง และก่อนหน้านี้ใช้ rememberRipple() ร่วมกับ RippleTheme ที่กำหนดเองเพื่อกำหนดค่า Ripple คุณควรระบุ Ripple API ของคุณเองแทน ซึ่งจะมอบสิทธิ์ให้กับ API ของโหนด Ripple ที่แสดงใน material-ripple จากนั้น คอมโพเนนต์จะใช้ Ripple ของคุณเอง ซึ่งใช้ค่าธีมโดยตรงได้ ดูข้อมูลเพิ่มเติมได้ที่ย้ายข้อมูล จากRippleTheme

ย้ายข้อมูลจาก RippleTheme

การใช้ RippleTheme เพื่อปิดใช้ Ripple สำหรับคอมโพเนนต์ที่ต้องการ

ไลบรารี material และ material3 แสดง RippleConfiguration และ LocalRippleConfiguration ซึ่งช่วยให้คุณกำหนดค่าลักษณะที่ปรากฏของ เอฟเฟกต์ระลอกภายในซับทรีได้ โปรดทราบว่า RippleConfiguration และ LocalRippleConfiguration มีไว้สําหรับการปรับแต่ง ต่อคอมโพเนนต์เท่านั้น API เหล่านี้ไม่รองรับการปรับแต่งทั่วโลก/ทั้งธีม ดูข้อมูลเพิ่มเติมเกี่ยวกับกรณีการใช้งานดังกล่าวได้ที่การใช้ RippleTheme เพื่อเปลี่ยนการกระเพื่อมทั้งหมดในแอปพลิเคชันทั่วโลก

เช่น ข้อมูลโค้ดต่อไปนี้ใช้ API ที่เลิกใช้งานแล้ว

private object DisabledRippleTheme : RippleTheme {

    @Composable
    override fun defaultColor(): Color = Color.Transparent

    @Composable
    override fun rippleAlpha(): RippleAlpha = RippleAlpha(0f, 0f, 0f, 0f)
}

// ...
    CompositionLocalProvider(LocalRippleTheme provides DisabledRippleTheme) {
        Button {
            // ...
        }
    }

คุณควรแก้ไขข้อมูลโค้ดข้างต้นเพื่อทำสิ่งต่อไปนี้

CompositionLocalProvider(LocalRippleConfiguration provides null) {
    Button {
        // ...
    }
}

การใช้ RippleTheme เพื่อเปลี่ยนสี/อัลฟ่าของ Ripple สำหรับคอมโพเนนต์ที่กำหนด

ดังที่อธิบายไว้ในส่วนก่อนหน้า RippleConfiguration และ LocalRippleConfiguration มีไว้สำหรับการปรับแต่งต่อคอมโพเนนต์เท่านั้น

เช่น ข้อมูลโค้ดต่อไปนี้ใช้ API ที่เลิกใช้งานแล้ว

private object DisabledRippleThemeColorAndAlpha : RippleTheme {

    @Composable
    override fun defaultColor(): Color = Color.Red

    @Composable
    override fun rippleAlpha(): RippleAlpha = MyRippleAlpha
}

// ...
    CompositionLocalProvider(LocalRippleTheme provides DisabledRippleThemeColorAndAlpha) {
        Button {
            // ...
        }
    }

คุณควรแก้ไขข้อมูลโค้ดข้างต้นเพื่อทำสิ่งต่อไปนี้

@OptIn(ExperimentalMaterialApi::class)
private val MyRippleConfiguration =
    RippleConfiguration(color = Color.Red, rippleAlpha = MyRippleAlpha)

// ...
    CompositionLocalProvider(LocalRippleConfiguration provides MyRippleConfiguration) {
        Button {
            // ...
        }
    }

การใช้ RippleTheme เพื่อเปลี่ยนการกระเพื่อมทั้งหมดในแอปพลิเคชันทั่วโลก

ก่อนหน้านี้ คุณสามารถใช้ LocalRippleTheme เพื่อกำหนดลักษณะการกระเพื่อมที่ระดับธีมทั้งหมดได้ ซึ่งเป็นจุดผสานรวมระหว่างองค์ประกอบระบบการออกแบบที่กำหนดเองกับ Ripple material-ripple ตอนนี้จะแสดงฟังก์ชัน createRippleModifierNode() แทนที่จะแสดงองค์ประกอบการจัดรูปแบบทั่วไป ฟังก์ชันนี้ช่วยให้ไลบรารีระบบการออกแบบสร้างการใช้งานwrapperระดับสูงขึ้นได้ ซึ่งจะค้นหาค่าธีม แล้วมอบหมายการใช้งาน Ripple ให้กับโหนดที่สร้างขึ้นโดยฟังก์ชันนี้

ซึ่งช่วยให้ระบบการออกแบบสามารถค้นหาสิ่งที่ต้องการได้โดยตรง และแสดงเลเยอร์การจัดธีมที่ผู้ใช้กำหนดค่าได้ตามต้องการที่ด้านบนโดยไม่ต้องยึดตามสิ่งที่ระบุไว้ในเลเยอร์ material-ripple การเปลี่ยนแปลงนี้ยังทำให้ธีม/ข้อกำหนดที่ริปเปิลเป็นไปตามนั้นชัดเจนยิ่งขึ้นด้วย เนื่องจากเป็น API ของริปเปิลเองที่กำหนดสัญญาดังกล่าว ไม่ใช่การอนุมานจากธีมโดยปริยาย

ดูคำแนะนำได้ที่การใช้งาน Ripple API ในไลบรารี Material และแทนที่การเรียกไปยัง Material Composition Local ตามที่จำเป็นสำหรับ ระบบการออกแบบของคุณเอง

ย้ายข้อมูลจาก Indication ไปยัง IndicationNodeFactory

ผ่านประมาณ Indication

หากคุณเพียงสร้าง Indication เพื่อส่งต่อ เช่น สร้าง การกระเพื่อมเพื่อส่งต่อให้ Modifier.clickable หรือ Modifier.indication คุณก็ไม่ จำเป็นต้องทำการเปลี่ยนแปลงใดๆ IndicationNodeFactory สืบทอดมาจาก Indication ดังนั้นทุกอย่างจะยังคงคอมไพล์และทำงานได้

กำลังสร้าง Indication

หากคุณสร้างIndicationการติดตั้งใช้งานของคุณเอง การย้ายข้อมูลควร จะง่ายในกรณีส่วนใหญ่ ตัวอย่างเช่น ลองพิจารณา Indication ที่ใช้เอฟเฟกต์การปรับขนาดเมื่อกด

object ScaleIndication : Indication {
    @Composable
    override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
        // key the remember against interactionSource, so if it changes we create a new instance
        val instance = remember(interactionSource) { ScaleIndicationInstance() }

        LaunchedEffect(interactionSource) {
            interactionSource.interactions.collectLatest { interaction ->
                when (interaction) {
                    is PressInteraction.Press -> instance.animateToPressed(interaction.pressPosition)
                    is PressInteraction.Release -> instance.animateToResting()
                    is PressInteraction.Cancel -> instance.animateToResting()
                }
            }
        }

        return instance
    }
}

private class ScaleIndicationInstance : IndicationInstance {
    var currentPressPosition: Offset = Offset.Zero
    val animatedScalePercent = Animatable(1f)

    suspend fun animateToPressed(pressPosition: Offset) {
        currentPressPosition = pressPosition
        animatedScalePercent.animateTo(0.9f, spring())
    }

    suspend fun animateToResting() {
        animatedScalePercent.animateTo(1f, spring())
    }

    override fun ContentDrawScope.drawIndication() {
        scale(
            scale = animatedScalePercent.value,
            pivot = currentPressPosition
        ) {
            this@drawIndication.drawContent()
        }
    }
}

คุณย้ายข้อมูลนี้ได้ใน 2 ขั้นตอน ดังนี้

  1. ย้ายข้อมูล ScaleIndicationInstance เพื่อเป็น DrawModifierNode API Surface สำหรับ DrawModifierNode คล้ายกับ IndicationInstance มาก โดยจะแสดงฟังก์ชัน ContentDrawScope#draw() ซึ่งเทียบเท่ากับ IndicationInstance#drawContent() ในด้านฟังก์ชันการทำงาน คุณต้องเปลี่ยนฟังก์ชันนั้น แล้ว ใช้ตรรกะ collectLatest ภายในโหนดโดยตรงแทน Indication

    เช่น ข้อมูลโค้ดต่อไปนี้ใช้ API ที่เลิกใช้งานแล้ว

    private class ScaleIndicationInstance : IndicationInstance {
        var currentPressPosition: Offset = Offset.Zero
        val animatedScalePercent = Animatable(1f)
    
        suspend fun animateToPressed(pressPosition: Offset) {
            currentPressPosition = pressPosition
            animatedScalePercent.animateTo(0.9f, spring())
        }
    
        suspend fun animateToResting() {
            animatedScalePercent.animateTo(1f, spring())
        }
    
        override fun ContentDrawScope.drawIndication() {
            scale(
                scale = animatedScalePercent.value,
                pivot = currentPressPosition
            ) {
                this@drawIndication.drawContent()
            }
        }
    }

    คุณควรแก้ไขข้อมูลโค้ดข้างต้นเพื่อดำเนินการต่อไปนี้

    private class ScaleIndicationNode(
        private val interactionSource: InteractionSource
    ) : Modifier.Node(), DrawModifierNode {
        var currentPressPosition: Offset = Offset.Zero
        val animatedScalePercent = Animatable(1f)
    
        private suspend fun animateToPressed(pressPosition: Offset) {
            currentPressPosition = pressPosition
            animatedScalePercent.animateTo(0.9f, spring())
        }
    
        private suspend fun animateToResting() {
            animatedScalePercent.animateTo(1f, spring())
        }
    
        override fun onAttach() {
            coroutineScope.launch {
                interactionSource.interactions.collectLatest { interaction ->
                    when (interaction) {
                        is PressInteraction.Press -> animateToPressed(interaction.pressPosition)
                        is PressInteraction.Release -> animateToResting()
                        is PressInteraction.Cancel -> animateToResting()
                    }
                }
            }
        }
    
        override fun ContentDrawScope.draw() {
            scale(
                scale = animatedScalePercent.value,
                pivot = currentPressPosition
            ) {
                this@draw.drawContent()
            }
        }
    }

  2. ย้ายข้อมูล ScaleIndication เพื่อติดตั้งใช้งาน IndicationNodeFactory เนื่องจากตอนนี้เราย้ายตรรกะของคอลเล็กชันไปไว้ในโหนดแล้ว นี่จึงเป็นออบเจ็กต์ Factory ที่เรียบง่ายมาก ซึ่งมีหน้าที่เพียงอย่างเดียวคือสร้างอินสแตนซ์ของโหนด

    เช่น ข้อมูลโค้ดต่อไปนี้ใช้ API ที่เลิกใช้งานแล้ว

    object ScaleIndication : Indication {
        @Composable
        override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
            // key the remember against interactionSource, so if it changes we create a new instance
            val instance = remember(interactionSource) { ScaleIndicationInstance() }
    
            LaunchedEffect(interactionSource) {
                interactionSource.interactions.collectLatest { interaction ->
                    when (interaction) {
                        is PressInteraction.Press -> instance.animateToPressed(interaction.pressPosition)
                        is PressInteraction.Release -> instance.animateToResting()
                        is PressInteraction.Cancel -> instance.animateToResting()
                    }
                }
            }
    
            return instance
        }
    }

    คุณควรแก้ไขข้อมูลโค้ดข้างต้นเพื่อดำเนินการต่อไปนี้

    object ScaleIndicationNodeFactory : IndicationNodeFactory {
        override fun create(interactionSource: InteractionSource): DelegatableNode {
            return ScaleIndicationNode(interactionSource)
        }
    
        override fun hashCode(): Int = -1
    
        override fun equals(other: Any?) = other === this
    }

การใช้ Indication เพื่อสร้าง IndicationInstance

ในกรณีส่วนใหญ่ คุณควรใช้ Modifier.indication เพื่อแสดง Indication สำหรับ คอมโพเนนต์ อย่างไรก็ตาม ในกรณีที่พบได้ยากซึ่งคุณสร้าง IndicationInstance ด้วยตนเองโดยใช้ rememberUpdatedInstance คุณจะต้องอัปเดตการติดตั้งใช้งานเพื่อตรวจสอบว่า Indication เป็น IndicationNodeFactory หรือไม่ เพื่อให้คุณใช้การติดตั้งใช้งานที่เบากว่าได้ ตัวอย่างเช่น Modifier.indication จะ มอบหมายงานภายในไปยังโหนดที่สร้างขึ้นหากเป็น IndicationNodeFactory หากไม่มี ระบบจะใช้ Modifier.composed เพื่อโทรหา rememberUpdatedInstance