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
.