เราได้เปิดตัว API ใหม่เพื่อปรับปรุงประสิทธิภาพการคอมโพสของคอมโพเนนต์แบบอินเทอร์แอกทีฟที่ใช้ Modifier.clickable API เหล่านี้ช่วยให้การใช้งาน Indication มีประสิทธิภาพมากขึ้น เช่น เอฟเฟกต์ริปเปิล
androidx.compose.foundation:foundation:1.7.0+ และ androidx.compose.material:material-ripple:1.7.0+ มีการเปลี่ยนแปลง API ต่อไปนี้
เลิกใช้งาน |
การเปลี่ยนทดแทน |
|---|---|
|
|
|
API ใหม่ของ หมายเหตุ: ในบริบทนี้ "คลังเนื้อหา" หมายถึง |
|
ดังนี้
|
หน้านี้อธิบายผลกระทบจากการเปลี่ยนแปลงลักษณะการทำงานและวิธีการย้ายข้อมูลไปยัง 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() อีกต่อไป แต่จะใช้ Ripple API ใหม่แทน ด้วยเหตุนี้ จึงไม่มีการค้นหา LocalRippleTheme
ดังนั้น หากคุณตั้งค่า LocalRippleTheme ในแอปพลิเคชัน คอมโพเนนต์ Material จะไม่ใช้ค่าเหล่านี้
ส่วนต่อไปนี้จะอธิบายวิธีย้ายข้อมูลไปยัง API ใหม่
ย้ายข้อมูลจาก rememberRipple ไปยัง ripple
การใช้คลัง Material
หากคุณใช้ไลบรารี Material ให้แทนที่ rememberRipple() ด้วยการเรียกใช้ ripple() จากไลบรารีที่เกี่ยวข้องโดยตรง API นี้จะสร้างเอฟเฟกต์ริปเปิลโดยใช้ค่าที่ได้มาจาก 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() ไม่ใช่ฟังก์ชันที่ประกอบกันได้อีกต่อไป และไม่จำเป็นต้องจดจำ นอกจากนี้ยังนำกลับมาใช้ซ้ำในคอมโพเนนต์หลายรายการได้ด้วย เช่นเดียวกับตัวแก้ไข ดังนั้นให้พิจารณาดึงข้อมูลการสร้างระลอกคลื่นไปยังค่าระดับบนสุดเพื่อประหยัดการจัดสรร
กำลังใช้ระบบการออกแบบที่กำหนดเอง
หากคุณกำลังใช้ระบบการออกแบบของคุณเอง และก่อนหน้านี้คุณใช้ rememberRipple() ร่วมกับ RippleTheme ที่กำหนดเองเพื่อกำหนดค่าเอฟเฟกต์ริปเปิล คุณควรระบุ Ripple API ของคุณเองที่มอบสิทธิ์ให้กับ Ripple Node API ที่แสดงใน material-ripple แทน จากนั้นคอมโพเนนต์จะใช้ Ripple ของคุณเองซึ่งใช้ค่าธีมโดยตรงได้ ดูข้อมูลเพิ่มเติมได้ที่ย้ายข้อมูลจาก RippleTheme
ย้ายข้อมูลจาก RippleTheme
การใช้ RippleTheme เพื่อปิดใช้เอฟเฟกต์ Ripple สำหรับคอมโพเนนต์หนึ่งๆ
ไลบรารี material และ material3 จะแสดง RippleConfiguration และ LocalRippleConfiguration ซึ่งช่วยให้คุณกำหนดค่าลักษณะที่ปรากฏของระลอกคลื่นภายในซับทรีได้ โปรดทราบว่า RippleConfiguration และ LocalRippleConfiguration มีไว้สำหรับการปรับแต่งต่อคอมโพเนนต์เท่านั้น API เหล่านี้ไม่รองรับการปรับแต่งทั่วโลก/ทั้งธีม โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับ Use Case ดังกล่าวที่หัวข้อการใช้ 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 ที่เลิกใช้งานแล้ว
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 เพื่อเปลี่ยนเอฟเฟกต์ Ripple ทั้งหมดในแอปพลิเคชันพร้อมกัน
ก่อนหน้านี้ คุณสามารถใช้ LocalRippleTheme เพื่อกำหนดลักษณะการทำงานของเอฟเฟกต์ริปเปิลในระดับทั้งธีมได้ โดยพื้นฐานแล้ว นี่คือจุดผสานรวมระหว่างองค์ประกอบภายในของระบบการออกแบบที่กำหนดเองกับ Ripple ตอนนี้ material-ripple จะแสดงฟังก์ชัน createRippleModifierNode() แทนที่จะแสดงองค์ประกอบพื้นฐานของธีมทั่วไป ฟังก์ชันนี้ช่วยให้ไลบรารีระบบการออกแบบสร้างการใช้งาน wrapper ที่มีลำดับสูงขึ้น ซึ่งจะค้นหาค่าธีม แล้วมอบสิทธิ์การใช้งาน Ripple ให้กับโหนดที่สร้างโดยฟังก์ชันนี้
ซึ่งช่วยให้ระบบการออกแบบค้นหาสิ่งที่ต้องการได้โดยตรง และแสดงเลเยอร์ธีมที่ผู้ใช้กำหนดค่าได้ซึ่งจำเป็นไว้ด้านบนโดยไม่ต้องสอดคล้องกับสิ่งที่ระบุไว้ที่เลเยอร์ material-ripple การเปลี่ยนแปลงนี้ยังทำให้เห็นได้ชัดเจนยิ่งขึ้นว่าเอฟเฟกต์ริปเปิลเป็นไปตามธีม/ข้อกำหนดใด เนื่องจาก API ของเอฟเฟกต์ริปเปิลเป็นตัวกำหนดสัญญานั้น แทนที่จะได้มาจากธีมโดยนัย
ดูคำแนะนำได้ที่การติดตั้งใช้งาน Ripple API ในไลบรารี Material และแทนที่การเรียกใช้ Composition Local ของ Material ตามที่จำเป็นสำหรับระบบการออกแบบของคุณเอง
ย้ายข้อมูลจาก 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 ขั้นตอน ดังนี้
ย้ายข้อมูล
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() } } }
ย้ายข้อมูล
ScaleIndicationเพื่อใช้IndicationNodeFactoryเนื่องจากตอนนี้ตรรกะการรวบรวมย้ายไปอยู่ในโหนดแล้ว นี่จึงเป็นออบเจ็กต์โรงงานที่เรียบง่ายมากซึ่งมีหน้าที่รับผิดชอบเพียงอย่างเดียวคือสร้างอินสแตนซ์โหนดตัวอย่างเช่น ข้อมูลโค้ดต่อไปนี้ใช้ 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