Untuk meningkatkan performa komposisi komponen interaktif yang menggunakan
Modifier.clickable
, kami telah memperkenalkan API baru. API ini memungkinkan penerapan Indication
yang lebih efisien, seperti riak.
androidx.compose.foundation:foundation:1.7.0+
dan
androidx.compose.material:material-ripple:1.7.0+
mencakup perubahan API berikut:
Tidak digunakan lagi |
Penggantian |
---|---|
|
|
|
API Catatan: Dalam konteks ini, "library Material" mengacu pada |
|
Yakni:
|
Halaman ini menjelaskan dampak perubahan perilaku dan petunjuk untuk bermigrasi ke API baru.
Perubahan perilaku
Versi library berikut menyertakan perubahan perilaku riak:
androidx.compose.material:material:1.7.0+
androidx.compose.material3:material3:1.3.0+
androidx.wear.compose:compose-material:1.4.0+
Versi library Material ini tidak lagi menggunakan rememberRipple()
; sebagai gantinya,
library ini menggunakan API riak baru. Akibatnya, mereka tidak mengkueri LocalRippleTheme
.
Oleh karena itu, jika Anda menyetel LocalRippleTheme
di aplikasi, komponen Material tidak akan menggunakan nilai ini.
Bagian berikut menjelaskan cara bermigrasi ke API baru.
Migrasi dari rememberRipple
ke ripple
Menggunakan library Material
Jika Anda menggunakan library Material, ganti rememberRipple()
secara langsung dengan
panggilan ke ripple()
dari library yang sesuai. API ini membuat riak
menggunakan nilai yang berasal dari API tema Material. Kemudian, teruskan objek yang ditampilkan ke Modifier.clickable
dan/atau komponen lainnya.
Misalnya, cuplikan berikut menggunakan API yang tidak digunakan lagi:
Box( Modifier.clickable( onClick = {}, interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple() ) ) { // ... }
Anda harus mengubah cuplikan di atas menjadi:
@Composable private fun RippleExample() { Box( Modifier.clickable( onClick = {}, interactionSource = remember { MutableInteractionSource() }, indication = ripple() ) ) { // ... } }
Perhatikan bahwa ripple()
bukan lagi fungsi composable dan tidak perlu
diingat. Ripple juga dapat digunakan kembali di beberapa komponen, mirip dengan
pengubah, jadi pertimbangkan untuk mengekstrak pembuatan ripple ke nilai tingkat teratas untuk
menghemat alokasi.
Menerapkan sistem desain kustom
Jika Anda menerapkan sistem desain sendiri, dan sebelumnya menggunakan
rememberRipple()
bersama dengan RippleTheme
kustom untuk mengonfigurasi riak, sebaiknya berikan API riak Anda sendiri yang mendelegasikan ke API node riak yang diekspos di material-ripple
. Kemudian, komponen Anda dapat menggunakan ripple Anda sendiri
yang menggunakan nilai tema Anda secara langsung. Untuk mengetahui informasi selengkapnya, lihat Bermigrasi
dariRippleTheme
.
Migrasi dari RippleTheme
Menggunakan RippleTheme
untuk menonaktifkan riak untuk komponen tertentu
Library material
dan material3
mengekspos RippleConfiguration
dan
LocalRippleConfiguration
, yang memungkinkan Anda mengonfigurasi tampilan
efek riak dalam subtree. Perhatikan bahwa RippleConfiguration
dan
LocalRippleConfiguration
bersifat eksperimental, dan hanya ditujukan untuk penyesuaian
per komponen. Penyesuaian global/di seluruh tema tidak didukung dengan API ini; lihat Menggunakan RippleTheme
untuk mengubah semua riak secara global dalam aplikasi untuk mengetahui informasi selengkapnya tentang kasus penggunaan tersebut.
Misalnya, cuplikan berikut menggunakan API yang tidak digunakan lagi:
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 { // ... } }
Anda harus mengubah cuplikan di atas menjadi:
CompositionLocalProvider(LocalRippleConfiguration provides null) { Button { // ... } }
Menggunakan RippleTheme
untuk mengubah warna/alfa riak untuk komponen tertentu
Seperti yang dijelaskan di bagian sebelumnya, RippleConfiguration
dan
LocalRippleConfiguration
adalah API eksperimental dan hanya ditujukan untuk
penyesuaian per komponen.
Misalnya, cuplikan berikut menggunakan API yang tidak digunakan lagi:
private object DisabledRippleThemeColorAndAlpha : RippleTheme { @Composable override fun defaultColor(): Color = Color.Red @Composable override fun rippleAlpha(): RippleAlpha = MyRippleAlpha } // ... CompositionLocalProvider(LocalRippleTheme provides DisabledRippleThemeColorAndAlpha) { Button { // ... } }
Anda harus mengubah cuplikan di atas menjadi:
@OptIn(ExperimentalMaterialApi::class) private val MyRippleConfiguration = RippleConfiguration(color = Color.Red, rippleAlpha = MyRippleAlpha) // ... CompositionLocalProvider(LocalRippleConfiguration provides MyRippleConfiguration) { Button { // ... } }
Menggunakan RippleTheme
untuk mengubah semua riak dalam aplikasi secara global
Sebelumnya, Anda dapat menggunakan LocalRippleTheme
untuk menentukan perilaku riak di tingkat seluruh tema. Pada dasarnya, ini adalah titik integrasi antara lokal komposisi sistem desain kustom dan riak. Daripada menampilkan primitif
tema generik, material-ripple
kini menampilkan fungsi createRippleModifierNode()
. Fungsi ini memungkinkan library sistem desain membuat implementasi wrapper
urutan yang lebih tinggi, yang membuat kueri nilai tema mereka, lalu mendelegasikan implementasi riak ke node yang dibuat oleh fungsi ini.
Hal ini memungkinkan sistem desain untuk langsung membuat kueri yang mereka butuhkan, dan mengekspos lapisan tema yang dapat dikonfigurasi pengguna yang diperlukan di atas tanpa harus sesuai dengan apa yang disediakan di lapisan material-ripple
. Perubahan ini juga membuat tema/spesifikasi yang dipatuhi riak menjadi lebih eksplisit, karena API riak itu sendiri yang menentukan kontrak tersebut, bukan diturunkan secara implisit dari tema.
Untuk panduan, lihat penerapan API riak di library Material, dan ganti panggilan ke lokal komposisi Material sesuai kebutuhan untuk sistem desain Anda sendiri.
Migrasi dari Indication
ke IndicationNodeFactory
Melewati Indication
Jika Anda hanya membuat Indication
untuk dibagikan, seperti membuat
ripl untuk dibagikan ke Modifier.clickable
atau Modifier.indication
, Anda tidak
perlu melakukan perubahan apa pun. IndicationNodeFactory
diwarisi dari Indication
,
sehingga semuanya akan terus dikompilasi dan berfungsi.
Membuat Indication
Jika Anda membuat implementasi Indication
sendiri, migrasi seharusnya
sederhana dalam sebagian besar kasus. Misalnya, pertimbangkan Indication
yang menerapkan
efek penskalaan saat ditekan:
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() } } }
Anda dapat memigrasikan ini dalam dua langkah:
Migrasikan
ScaleIndicationInstance
menjadiDrawModifierNode
. Permukaan API untukDrawModifierNode
sangat mirip denganIndicationInstance
: API ini mengekspos fungsiContentDrawScope#draw()
yang secara fungsional setara denganIndicationInstance#drawContent()
. Anda perlu mengubah fungsi tersebut, lalu menerapkan logikacollectLatest
langsung di dalam node, bukanIndication
.Misalnya, cuplikan berikut menggunakan API yang tidak digunakan lagi:
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() } } }
Anda harus mengubah cuplikan di atas menjadi:
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() } } }
Migrasikan
ScaleIndication
untuk menerapkanIndicationNodeFactory
. Karena logika pengumpulan kini dipindahkan ke node, ini adalah objek factory yang sangat sederhana yang hanya bertanggung jawab untuk membuat instance node.Misalnya, cuplikan berikut menggunakan API yang tidak digunakan lagi:
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 } }
Anda harus mengubah cuplikan di atas menjadi:
object ScaleIndicationNodeFactory : IndicationNodeFactory { override fun create(interactionSource: InteractionSource): DelegatableNode { return ScaleIndicationNode(interactionSource) } override fun hashCode(): Int = -1 override fun equals(other: Any?) = other === this }
Menggunakan Indication
untuk membuat IndicationInstance
Dalam sebagian besar kasus, Anda harus menggunakan Modifier.indication
untuk menampilkan Indication
untuk
komponen. Namun, jika Anda membuat IndicationInstance
secara manual menggunakan rememberUpdatedInstance
, Anda harus memperbarui penerapan untuk memeriksa apakah Indication
adalah IndicationNodeFactory
sehingga Anda dapat menggunakan penerapan yang lebih ringan. Misalnya, Modifier.indication
akan
mendelegasikan secara internal ke node yang dibuat jika merupakan IndicationNodeFactory
. Jika
tidak, Modifier.composed
akan digunakan untuk memanggil rememberUpdatedInstance
.