Aby poprawić kompozycję komponentów interaktywnych, które korzystają z funkcji
Modifier.clickable
, wprowadziliśmy nowe interfejsy API. Te interfejsy API dają większe możliwości
efektywne implementacje Indication
, takie jak Echo.
androidx.compose.foundation:foundation:1.7.0+
i
androidx.compose.material:material-ripple:1.7.0+
obejmuje ten interfejs API
zmiany:
Wycofano |
Zamiennik |
---|---|
|
|
|
Nowe interfejsy API Uwaga: w tym kontekście „Biblioteki materiałów” odnoszą się do: |
|
Wykonaj jedną z tych czynności:
|
Na tej stronie opisano wpływ zmiany działania i instrukcje migracji do nowych interfejsów API.
Zmiana w działaniu
W tych wersjach biblioteki występują zmiany w działaniu fal:
androidx.compose.material:material:1.7.0+
androidx.compose.material3:material3:1.3.0+
androidx.wear.compose:compose-material:1.4.0+
Te wersje bibliotek Material Design nie korzystają już z narzędzia rememberRipple()
. zamiast
korzystają z nowych interfejsów API Echo. W rezultacie nie wysyła zapytania do: LocalRippleTheme
.
Dlatego, jeśli ustawisz w aplikacji LocalRippleTheme
, Material
nie będą z nich korzystać.
W tej sekcji opisano, jak tymczasowo wrócić do starego sposobu działania.
bez migracji; zalecamy jednak przejście na nowe interfejsy API. Dla:
instrukcje migracji znajdziesz w artykule Migracja z rememberRipple
do ripple
i kolejnych.
Uaktualnij wersję biblioteki Material Design bez migracji
Aby odblokować uaktualnienie wersji biblioteki, możesz użyć
LocalUseFallbackRippleImplementation CompositionLocal
interfejs API do skonfigurowania
Komponenty, z których należy przywrócić poprzedni sposób działania:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme { App() } }
Pamiętaj, by umieścić go poza obszarem MaterialTheme
, aby stare Echo
muszą być dostarczane za pośrednictwem firmy LocalIndication
.
W poniższych sekcjach opisano, jak przejść na nowe interfejsy API.
Migracja z usługi rememberRipple
do środowiska ripple
Korzystanie z biblioteki Material
Jeśli używasz biblioteki Material Design, zastąp rememberRipple()
bezpośrednio
ripple()
z odpowiedniej biblioteki. Ten interfejs API tworzy falę
za pomocą wartości pozyskanych z interfejsów API motywu Material. Następnie prześlij zwrócony
obiektu Modifier.clickable
lub innych komponentów.
Na przykład ten fragment kodu używa wycofanych interfejsów API:
Box( Modifier.clickable( onClick = {}, interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple() ) ) { // ... }
Zmodyfikuj powyższy fragment, aby:
@Composable private fun RippleExample() { Box( Modifier.clickable( onClick = {}, interactionSource = remember { MutableInteractionSource() }, indication = ripple() ) ) { // ... } }
Pamiętaj, że ripple()
nie jest już funkcją kompozycyjną i nie musi być
zapamiętanych informacji. Można go również używać w wielu komponentach, podobnie jak
modyfikatory, więc rozważ wyodrębnienie kreacji Echo do wartości najwyższego poziomu,
zapisz alokacje.
Wdrażanie systemu projektowania niestandardowego
Jeśli wdrażasz własny system projektowania, a wcześniej korzystasz z
rememberRipple()
wraz z niestandardowym RippleTheme
do skonfigurowania Echa,
należy zamiast tego udostępnić własny interfejs API Ripple, który przekazuje dostęp do tego węzła
Interfejsy API udostępnione w material-ripple
. Następnie Twoje komponenty mogą używać własnych fal
który bezpośrednio wykorzystuje wartości motywu. Więcej informacji znajdziesz w artykule Migracja
od RippleTheme
.
Migracja z usługi RippleTheme
Tymczasowo zrezygnuj ze zmiany w działaniu
Biblioteki materiałów mają tymczasowy CompositionLocal
,
LocalUseFallbackRippleImplementation
, za pomocą których możesz skonfigurować wszystkie
Komponenty materiałowe, które mają zostać użyte w zastępstwie z użyciem rememberRipple
. W ten sposób
rememberRipple
nadal wysyła zapytanie do: LocalRippleTheme
.
Fragment kodu poniżej pokazuje, jak korzystać z funkcji
Interfejs API LocalUseFallbackRippleImplementation CompositionLocal
:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme { App() } }
Jeśli korzystasz z niestandardowego motywu aplikacji opartego na Material, możesz możesz bezpiecznie umieścić kompozycję lokalną jako część motywu aplikacji:
@OptIn(ExperimentalMaterialApi::class) @Composable fun MyAppTheme(content: @Composable () -> Unit) { CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme(content = content) } }
Więcej informacji znajdziesz w artykule o uaktualnianiu biblioteki Material Design bez migracji.
Używanie RippleTheme
do wyłączania echa dla danego komponentu
Biblioteki material
i material3
udostępniają biblioteki RippleConfiguration
i
LocalRippleConfiguration
, która pozwala skonfigurować wygląd
i fale w poddrzewie. Pamiętaj, że RippleConfiguration
i
LocalRippleConfiguration
mają charakter eksperymentalny i są przeznaczone wyłącznie dla poszczególnych komponentów
i personalizacji reklam. Dostosowanie globalne/tematyczne nie jest obsługiwane w tych
interfejsy API; Więcej informacji: Używanie RippleTheme
do globalnej zmiany wszystkich echo w
, aby dowiedzieć się więcej o tym przypadku użycia.
Na przykład ten fragment kodu używa wycofanych interfejsów 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 { // ... } }
Zmodyfikuj powyższy fragment, aby:
CompositionLocalProvider(LocalRippleConfiguration provides null) { Button { // ... } }
Użycie funkcji RippleTheme
do zmiany koloru/alfa fali danego komponentu
Jak opisano w poprzedniej sekcji RippleConfiguration
i
LocalRippleConfiguration
to eksperymentalne interfejsy API, które są przeznaczone tylko dla
z każdym komponentem.
Na przykład ten fragment kodu używa wycofanych interfejsów API:
private object DisabledRippleThemeColorAndAlpha : RippleTheme { @Composable override fun defaultColor(): Color = Color.Red @Composable override fun rippleAlpha(): RippleAlpha = MyRippleAlpha } // ... CompositionLocalProvider(LocalRippleTheme provides DisabledRippleThemeColorAndAlpha) { Button { // ... } }
Zmodyfikuj powyższy fragment, aby:
@OptIn(ExperimentalMaterialApi::class) private val MyRippleConfiguration = RippleConfiguration(color = Color.Red, rippleAlpha = MyRippleAlpha) // ... CompositionLocalProvider(LocalRippleConfiguration provides MyRippleConfiguration) { Button { // ... } }
Użycie RippleTheme
do globalnej zmiany wszystkich echo w aplikacji
Wcześniej można było użyć parametru LocalRippleTheme
, aby definiować zachowanie fal przy
całą tematykę. Był to zasadniczo punkt integracji danych niestandardowych
lokalnych kompozycji systemu projektowania i fali. Zamiast udostępniać ogólną wersję
podstawowego motywu, material-ripple
udostępnia teraz createRippleModifierNode()
. Ta funkcja pozwala projektować biblioteki systemowe na tworzenie
zamówić implementację wrapper
, wykonać zapytanie o wartości motywu, a następnie przekazać
implementacji fali w węźle utworzonym przez tę funkcję.
Dzięki temu systemy projektowe mogą bezpośrednio wysyłać zapytania dotyczące potrzeb i udostępniać
wymagane konfigurowalne przez użytkownika warstwy tematyczne bez konieczności
co znajduje się w warstwie material-ripple
. Ta zmiana sprawi też, że
wyraźnie określić motyw lub specyfikację, do której pasuje fala.
Ripple API, który definiuje tę umowę, a nie w sposób niejawny
wywodzących się z motywu.
Wskazówki znajdziesz w artykule o implementacji interfejsu Ripple API w artykule Material Design. bibliotek. W razie potrzeby zastąpimy wywołania z własnym systemem projektowania.
Migracja z usługi Indication
do środowiska IndicationNodeFactory
Od okolicy Indication
Jeśli tylko tworzysz Indication
do przekazywania, np.
faluje, przechodząc do Modifier.clickable
lub Modifier.indication
, nie
musisz wprowadzić zmiany. Funkcja IndicationNodeFactory
dziedziczy dane z zakresu Indication
,
więc wszystko będzie się nadal kompilować i działać.
Tworzę: Indication
Jeśli tworzysz własną implementację Indication
, migracja powinna
w większości przypadków będzie prosta. Weźmy na przykład właściwość Indication
, która stosuje:
efekt skali po naciśnięciu:
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() } } }
Możesz to zrobić w 2 krokach:
Zmień status z
ScaleIndicationInstance
naDrawModifierNode
. Interfejs API dlaDrawModifierNode
jest bardzo podobny doIndicationInstance
: ujawnia FunkcjaContentDrawScope#draw()
, która jest funkcjonalnie równoważna funkcjiIndicationInstance#drawContent()
Trzeba zmienić tę funkcję, a następnie zaimplementuj logikęcollectLatest
bezpośrednio w węźle, zamiastIndication
Na przykład ten fragment kodu używa wycofanych interfejsów 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() } } }
Zmodyfikuj powyższy fragment, aby:
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() } } }
Przenieś
ScaleIndication
, aby wdrożyćIndicationNodeFactory
. Ponieważ mechanizm zbierania danych został przeniesiony do węzła. To bardzo prosta fabryka, którego jedynym zadaniem jest utworzenie instancji węzła.Na przykład ten fragment kodu używa wycofanych interfejsów 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 } }
Zmodyfikuj powyższy fragment, aby:
object ScaleIndicationNodeFactory : IndicationNodeFactory { override fun create(interactionSource: InteractionSource): DelegatableNode { return ScaleIndicationNode(interactionSource) } override fun hashCode(): Int = -1 override fun equals(other: Any?) = other === this }
Użycie Indication
do utworzenia IndicationInstance
W większości przypadków należy użyć parametru Modifier.indication
, aby wyświetlić element Indication
. W rzadkich przypadkach zdarza się jednak, że ręcznie tworzysz
IndicationInstance
za pomocą usługi rememberUpdatedInstance
. Musisz zaktualizować swoje
do sprawdzenia, czy Indication
to IndicationNodeFactory
, dzięki czemu
które są prostsze. Na przykład Modifier.indication
będzie
wewnętrznie przekazać dostęp do utworzonego węzła, jeśli jest to IndicationNodeFactory
. Jeśli
nie, użyjemy funkcji Modifier.composed
do wywołania funkcji rememberUpdatedInstance
.