Przewiń

Modyfikatory przewijania

Modyfikatory verticalScroll i horizontalScroll to najprostszy sposób, aby umożliwić użytkownikowi przewijanie elementu, gdy granice jego zawartości są większe niż ograniczenia maksymalnego rozmiaru. W przypadku modyfikatorów verticalScrollhorizontalScroll nie musisz tłumaczyć ani przesuwać treści.

@Composable
private fun ScrollBoxes() {
    Column(
        modifier = Modifier
            .background(Color.LightGray)
            .size(100.dp)
            .verticalScroll(rememberScrollState())
    ) {
        repeat(10) {
            Text("Item $it", modifier = Modifier.padding(2.dp))
        }
    }
}

Prosta lista pionowa reagująca na gesty przewijania
Rysunek 1. Prosta lista pionowa reagująca na gesty przewijania.

ScrollState umożliwia zmianę pozycji przewijania lub uzyskanie jej bieżącego stanu. Aby utworzyć go z parametrami domyślnymi, użyj rememberScrollState().

@Composable
private fun ScrollBoxesSmooth() {
    // Smoothly scroll 100px on first composition
    val state = rememberScrollState()
    LaunchedEffect(Unit) { state.animateScrollTo(100) }

    Column(
        modifier = Modifier
            .background(Color.LightGray)
            .size(100.dp)
            .padding(horizontal = 8.dp)
            .verticalScroll(state)
    ) {
        repeat(10) {
            Text("Item $it", modifier = Modifier.padding(2.dp))
        }
    }
}

Modyfikator obszaru przewijania

Modyfikator scrollableArea jest podstawowym elementem składowym służącym do tworzenia niestandardowych kontenerów z możliwością przewijania. Zapewnia wyższy poziom abstrakcji niż modyfikator scrollable, obsługując typowe wymagania, takie jak interpretacja delty gestu, przycinanie treści i efekty przewijania poza zakres.

Chociaż scrollableArea jest używane w przypadku niestandardowych implementacji, w przypadku standardowych list przewijanych zwykle lepiej jest używać gotowych rozwiązań, takich jak verticalScroll, horizontalScroll lub komponentów kompozycyjnych, np. LazyColumn. Te komponenty wyższego poziomu są prostsze w przypadku typowych zastosowań i są tworzone przy użyciu scrollableArea.

Różnica między modyfikatorami scrollableAreascrollable

Główna różnica między scrollableAreascrollable polega na tym, jak interpretują one gesty przewijania użytkownika:

  • scrollable (surowa wartość delta): wartość delta bezpośrednio odzwierciedla fizyczny ruch danych wejściowych użytkownika (np. przeciągnięcie wskaźnika) na ekranie.
  • scrollableArea (delta zorientowana na treść): symbol delta jest semantycznie odwrócony, aby odzwierciedlać wybraną zmianę pozycji przewijania, dzięki czemu treść wydaje się poruszać wraz z gestem użytkownika, co zwykle jest odwrotnością ruchu wskaźnika.

Wyobraź sobie, że scrollable informuje o tym, jak porusza się wskaźnik, a scrollableArea przekłada ten ruch na to, jak treści powinny się poruszać w typowym widoku z możliwością przewijania. Ta inwersja sprawia, że scrollableArea jest bardziej naturalne podczas wdrażania standardowego kontenera z możliwością przewijania.

W tabeli poniżej znajdziesz podsumowanie znaków delta w przypadku typowych scenariuszy:

Gest użytkownika

Zmiana zgłoszona do dispatchRawDelta przez scrollable

zmiana zgłoszona do dispatchRawDelta przez scrollableArea*

Wskaźnik przesuwa się W GÓRĘ

Negatywna

Pozytywny

Wskaźnik przesuwa się W DÓŁ

Pozytywny

Negatywna

Wskaźnik przesuwa się w LEWO.

Negatywna

Dodatnia (ujemna w przypadku języków zapisywanych od prawej do lewej)

Wskaźnik przesuwa się W PRAWO

Pozytywny

Negatywny (pozytywny w przypadku języków zapisywanych od prawej do lewej)

(*) Uwaga dotycząca scrollableArea znaku delta: znak delty od scrollableArea nie jest tylko prostym odwróceniem. Inteligentnie uwzględnia:

  1. Orientacja: pionowa lub pozioma.
  2. LayoutDirection: od lewej do prawej lub od prawej do lewej (szczególnie ważne w przypadku przewijania w poziomie).
  3. Flaga reverseScrolling: określa, czy kierunek przewijania jest odwrócony.

Oprócz odwracania delty przewijania scrollableArea przycina też treść do granic układu i obsługuje renderowanie efektów przewijania poza zakres. Domyślnie używa efektu dostarczonego przez LocalOverscrollFactory. Możesz dostosować lub wyłączyć tę funkcję, używając przeciążenia scrollableArea, które akceptuje parametr OverscrollEffect.

Kiedy używać modyfikatora scrollableArea

Modyfikatora scrollableArea należy używać, gdy chcesz utworzyć niestandardowy komponent przewijania, który nie jest odpowiednio obsługiwany przez modyfikatory horizontalScroll lub verticalScroll ani przez układy leniwe. Często dotyczy to przypadków, w których:

  • Niestandardowa logika układu: gdy układ elementów zmienia się dynamicznie w zależności od pozycji przewijania.
  • Unikalne efekty wizualne: stosowanie przekształceń, skalowania lub innych efektów w przypadku dzieci podczas przewijania.
  • Bezpośrednia kontrola: potrzebujesz precyzyjnej kontroli nad mechanizmami przewijania, która wykracza poza możliwości verticalScroll lub układów leniwych.

Tworzenie niestandardowych list w formie koła za pomocą symbolu scrollableArea

Ten przykład pokazuje, jak za pomocą parametru scrollableArea utworzyć niestandardową listę pionową, na której elementy zmniejszają się w miarę oddalania się od środka, co daje efekt wizualny przypominający koło. Tego rodzaju przekształcenie zależne od przewijania jest idealnym przykładem zastosowania scrollableArea.

Rysunek 2. Dostosowana lista pionowa z użyciem scrollableArea.

@Composable
private fun ScrollableAreaSample() {
    // ...
    Layout(
        modifier =
            Modifier
                .size(150.dp)
                .scrollableArea(scrollState, Orientation.Vertical)
                .background(Color.LightGray),
        // ...
    ) { measurables, constraints ->
        // ...
        // Update the maximum scroll value to not scroll beyond limits and stop when scroll
        // reaches the end.
        scrollState.maxValue = (totalHeight - viewportHeight).coerceAtLeast(0)

        // Position the children within the layout.
        layout(constraints.maxWidth, viewportHeight) {
            // The current vertical scroll position, in pixels.
            val scrollY = scrollState.value
            val viewportCenterY = scrollY + viewportHeight / 2

            var placeableLayoutPositionY = 0
            placeables.forEach { placeable ->
                // This sample applies a scaling effect to items based on their distance
                // from the center, creating a wheel-like effect.
                // ...
                // Place the item horizontally centered with a layer transformation for
                // scaling to achieve wheel-like effect.
                placeable.placeRelativeWithLayer(
                    x = constraints.maxWidth / 2 - placeable.width / 2,
                    // Offset y by the scroll position to make placeable visible in the viewport.
                    y = placeableLayoutPositionY - scrollY,
                ) {
                    scaleX = scaleFactor
                    scaleY = scaleFactor
                }
                // Move to the next item's vertical position.
                placeableLayoutPositionY += placeable.height
            }
        }
    }
}
// ...

Modyfikator przewijania

Modyfikator scrollable różni się od modyfikatorów przewijania tym, że scrollable wykrywa gesty przewijania i rejestruje zmiany, ale nie przesuwa automatycznie zawartości. Zamiast tego uprawnienia są delegowane na użytkownika za pomocą ScrollableState , co jest wymagane do prawidłowego działania tego modyfikatora.

Podczas tworzenia funkcji ScrollableState musisz podać funkcję consumeScrollDelta, która będzie wywoływana przy każdym kroku przewijania (za pomocą gestu, płynnego przewijania lub szybkiego przewijania) z wartością delta w pikselach. Ta funkcja musi zwracać ilość przewiniętej odległości, aby zdarzenie było prawidłowo propagowane w przypadku zagnieżdżonych elementów, które mają modyfikator scrollable.

Poniższy fragment kodu wykrywa gesty i wyświetla wartość liczbową przesunięcia, ale nie przesuwa żadnych elementów:

@Composable
private fun ScrollableSample() {
    // actual composable state
    var offset by remember { mutableFloatStateOf(0f) }
    Box(
        Modifier
            .size(150.dp)
            .scrollable(
                orientation = Orientation.Vertical,
                // Scrollable state: describes how to consume
                // scrolling delta and update offset
                state = rememberScrollableState { delta ->
                    offset += delta
                    delta
                }
            )
            .background(Color.LightGray),
        contentAlignment = Alignment.Center
    ) {
        Text(offset.toString())
    }
}

Element interfejsu wykrywający naciśnięcie palcem i wyświetlający wartość liczbową w miejscu, w którym znajduje się palec.
Rysunek 3. Element interfejsu, który wykrywa naciśnięcie palcem i wyświetla wartość liczbową położenia palca.