เราได้เปิดตัว API ใหม่เพื่อปรับปรุงประสิทธิภาพการคอมโพสของคอมโพเนนต์แบบอินเทอร์แอกทีฟที่ใช้
Modifier.clickable API เหล่านี้ช่วยให้Indicationการใช้งานมีประสิทธิภาพมากขึ้น
เช่น การกระเพื่อม
androidx.compose.foundation:foundation:1.7.0+ และ
androidx.compose.material:material-ripple:1.7.0+ มีการเปลี่ยนแปลง API
ดังนี้
เลิกใช้งาน |
การเปลี่ยนทดแทน |
|---|---|
|
|
|
หมายเหตุ: ในบริบทนี้ "คลังวัสดุ" หมายถึง |
|
ดังนี้
|
หน้านี้อธิบายผลกระทบจากการเปลี่ยนแปลงลักษณะการทำงานและวิธีการย้ายข้อมูลไปยัง 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 ขั้นตอนดังนี้
ย้ายข้อมูล
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เนื่องจากตอนนี้ได้ย้ายตรรกะของคอลเล็กชันไปไว้ในโหนดแล้ว ออบเจ็กต์ 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