ย้ายข้อมูลไปยัง 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 ใหม่

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

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

  • 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 Ripple ใหม่แทน ด้วยเหตุนี้ จึงไม่ได้ค้นหา LocalRippleTheme ดังนั้นหากคุณตั้งค่า LocalRippleTheme ในแอปพลิเคชัน Material components จะไม่ใช้ค่าเหล่านี้

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

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

การใช้ไลบรารี Material

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

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

การใช้ RippleTheme เพื่อปิดใช้เอฟเฟกต์ระลอกสำหรับคอมโพเนนต์หนึ่งๆ

ไลบรารี material และ material3 จะแสดง RippleConfiguration และ LocalRippleConfiguration ซึ่งช่วยให้คุณกำหนดค่าลักษณะที่ปรากฏของ การกระเพื่อมภายใน Subtree ได้ โปรดทราบว่า 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 เพื่อเปลี่ยนสี/อัลฟ่าของระลอกสำหรับคอมโพเนนต์ที่กำหนด

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

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

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

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

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

ผ่านประมาณ Indication

หากคุณเพียงสร้าง Indication เพื่อส่งต่อ เช่น สร้าง Ripple เพื่อส่งต่อให้ 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 สำหรับ 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