Modyfikatory przewijania
Modyfikatory verticalScroll
i horizontalScroll
stanowią najprostszy sposób na umożliwienie użytkownikowi przewijania elementu, gdy granice jego zawartości są większe niż maksymalne ograniczenia rozmiaru. Dzięki modyfikatorom verticalScroll
i horizontalScroll
nie musisz tłumaczyć ani przesuwać zawartoś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)) } } }
ScrollState
umożliwia zmianę pozycji przewijania lub uzyskanie jej bieżącego stanu. Aby utworzyć go z parametrami domyślnymi, użyj funkcji 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 przewijania
Modyfikator scrollable
różni się od modyfikatorów przewijania tym, że scrollable
wykrywa gesty przewijania i przechwytuje wartości delta, ale nie przesuwa zawartości automatycznie. Zamiast tego jest ona delegowana do użytkownika za pomocą parametru ScrollableState
, który jest wymagany do prawidłowego działania tego modyfikatora.
Podczas tworzenia funkcji ScrollableState
musisz podać funkcję consumeScrollDelta
, która będzie wywoływana w przypadku każdego kroku przewijania (za pomocą gestów, płynnego przewijania lub przewijania szybkiego) z delta w pikselach. Ta funkcja musi zwracać odległość przewinięcia, aby zapewnić prawidłowe propagowanie zdarzenia w przypadku elementów zagnieżdżonych z modyfikatorem 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 { mutableStateOf(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()) } }
Zagnieżdżone przewijanie
Przewijanie zagnieżdżone to system, w którym wiele elementów przewijania zawartych w sobie współpracuje ze sobą, reagując na pojedynczy gest przewijania i przekazując swoje wartości delta (zmiany).
System przewijania zagnieżdżonego umożliwia koordynację komponentów, które są przewijalne i połączone hierarchicznie (najczęściej przez udostępnianie tego samego elementu nadrzędnego). Ten system łączy kontenery przewijania i umożliwia interakcję z deltami przewijania, które są propagowane i udostępniane.
Compose oferuje wiele sposobów obsługi nawiasów zagnieżdżonych między składanymi elementami. Typowym przykładem przewijania zagnieżdżonego jest lista wewnątrz innej listy, a bardziej złożonym przypadkiem jest zwijana pasek narzędzi.
Automatyczne przewijanie zagnieżdżone
Prosty nawias imbrikowany nie wymaga od Ciebie żadnego działania. Gesty, które inicjują działanie przewijania, są automatycznie przekazywane z elementów podrzędnych do nadrzędnych. Oznacza to, że gdy dziecko nie może już dalej przewijać, gest jest obsługiwany przez element nadrzędny.
Automatyczne przewijanie zagnieżdżonych elementów jest obsługiwane i dostępne w ramach niektórych komponentów i modyfikatorów w Compose: verticalScroll
, horizontalScroll
, scrollable
, interfejsów API Lazy
i TextField
. Oznacza to, że gdy użytkownik przewija wewnętrzne elementy potomne zagnieżdżonych komponentów, poprzednie modyfikatory przekazują zmiany przewijania do elementów nadrzędnych, które obsługują zagnieżdżone przewijanie.
Ten przykład przedstawia elementy z modyfikatorem verticalScroll
w kontenerze, który również ma zastosowany modyfikator verticalScroll
.
@Composable private fun AutomaticNestedScroll() { val gradient = Brush.verticalGradient(0f to Color.Gray, 1000f to Color.White) Box( modifier = Modifier .background(Color.LightGray) .verticalScroll(rememberScrollState()) .padding(32.dp) ) { Column { repeat(6) { Box( modifier = Modifier .height(128.dp) .verticalScroll(rememberScrollState()) ) { Text( "Scroll here", modifier = Modifier .border(12.dp, Color.DarkGray) .background(brush = gradient) .padding(24.dp) .height(150.dp) ) } } } } }
Korzystanie z modyfikatora nestedScroll
Jeśli chcesz utworzyć zaawansowane przewijanie skoordynowane między wieloma elementami, modyfikator nestedScroll
zapewni Ci większą elastyczność dzięki zdefiniowaniu zagnieżdżonej hierarchii przewijania. Jak wspomnieliśmy w poprzedniej sekcji, niektóre komponenty mają wbudowane wsparcie dla przewijania zagnieżdżonego. Jednak w przypadku komponentów, które nie są przewijane automatycznie, takich jak Box
czy Column
, różnice przewijania w takich komponentach nie będą się rozprzestrzeniać w ramach wbudowanego systemu przewijania i nie dotrą do komponentu nadrzędnego NestedScrollConnection
. Aby rozwiązać ten problem, możesz użyć nestedScroll
, aby zapewnić obsługę innym komponentom, w tym komponentom niestandardowym.
Zagnieżdżony cykl przewijania
Zagnieżdżony cykl przewijania to przepływ wartości delta przewijania, które są wysyłane w górę i w dół drzewa hierarchicznego przez wszystkie komponenty (lub węzły) będące częścią zagnieżdżonego systemu przewijania, np. za pomocą przewijanych komponentów i modyfikatorów lub za pomocą elementu nestedScroll
.
Fazy cyklu przewijania zagnieżdżonego
Gdy element z możliwością przewijania wykryje zdarzenie wyzwalające (np. gest), jeszcze przed wywołaniem faktycznego działania przewijania, wygenerowane różnice są wysyłane do wbudowanego systemu przewijania i przechodzą 3 fazy: przed przewijaniem, wykorzystanie węzła i po przewijaniu.
W pierwszej fazie, czyli przed przewijaniem, komponent, który otrzymał różnice zdarzeń inicjujących, prześle te zdarzenia w górę po drzewie hierarchicznym do najwyższego elementu nadrzędnego. Zdarzenia delta będą się następnie przemieszczać w dół, co oznacza, że będą propagowane od elementu nadrzędnego na poziomie wyższym do elementu podrzędnego, który rozpoczął cykl zwinięcia wgłąb.
Dzięki temu zagnieżdżone elementy rodzicielskie przewijania (komponenty korzystające z elementów nestedScroll
lub modyfikatorów przewijania) mogą wykonać jakąś operację na wartości delta, zanim sam węzeł będzie mógł ją wykorzystać.
W fazie konsumpcji węzła sam węzeł będzie używać różnicy, której nie wykorzystały jego elementy nadrzędne. Wtedy ruch przewijania jest widoczny.
W tej fazie dziecko może wykorzystać całą lub część pozostałego czasu. Pozostałe elementy zostaną przesłane z powrotem, aby przejść przez fazę po przewinięciu.
Wreszcie, w fazie po przewinięciu wszystko, czego nie zużył sam węzeł, zostanie ponownie wysłane do swoich przodków do wykorzystania.
Faza po przewinięciu działa podobnie jak faza przed przewinięciem, w której każdy z elementów nadrzędnych może zdecydować, czy chce go użyć.
Podobnie jak w przypadku przewijania, gdy gest przeciągania zostanie zakończony, zamiar użytkownika może zostać przekształcony w szybkość, która służy do przewijania (animacji) przewijalnego kontenera. Rzut jest też częścią ścieżki przewijania zagnieżdżonego, a prędkości generowane przez zdarzenie przeciągania przechodzą przez podobne fazy: przed rzutem, zużycie węzła i po rzucie. Pamiętaj, że animacja gestu przesunięcia jest powiązana tylko z gesturem dotykowym i nie zostanie wywołana przez inne zdarzenia, takie jak a11y czy przewijanie za pomocą sprzętu.
Uczestniczyć w zagnieżdżonym cyklu przewijania
Udział w cyklu oznacza przechwytywanie, wykorzystywanie i raportowanie zużycia delt w hierarchii. Compose udostępnia zestaw narzędzi, które umożliwiają wpływanie na działanie systemu przewijania zagnieżdżonego i bezpośrednie z nim współpracowanie, np. gdy trzeba coś zrobić z wartościami delta przewijania, zanim komponent z możliwością przewijania zacznie się przewijać.
Jeśli zagnieżdżony cykl przewijania jest systemem działającym na łańcuchu węzłów, modyfikator nestedScroll
umożliwia przechwytywanie i wstawianie zmian oraz wpływanie na dane (różnice w przewijaniu), które są propagowane w łańcuchu. Ten modyfikator może być umieszczony w dowolnym miejscu w hierarchii i komunikuje się z zagnieżdżonymi instancjami modyfikatora przewijania w drzewie, aby udostępniać informacje przez ten kanał. Elementy składowe tego modyfikatora to NestedScrollConnection
i NestedScrollDispatcher
.
NestedScrollConnection
pozwala reagować na fazy cyklu przewijania zagnieżdżonego i wpływać na system przewijania zagnieżdżonego. Składa się z 4 metod wywołania, z których każda reprezentuje jedną z faz konsumpcji: przed przewijaniem i po nim oraz przed i po przenoszeniu:
val nestedScrollConnection = object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { println("Received onPreScroll callback.") return Offset.Zero } override fun onPostScroll( consumed: Offset, available: Offset, source: NestedScrollSource ): Offset { println("Received onPostScroll callback.") return Offset.Zero } }
Każde wywołanie zwrotne zawiera też informacje o przekazywanym delta: available
delta dla danego etapu i consumed
delta zużyte w poprzednich fazach. Jeśli w dowolnym momencie chcesz zatrzymać propagowanie różnic w hierarchii, możesz użyć zagnieżdżonego połączenia przewijania:
val disabledNestedScrollConnection = remember { object : NestedScrollConnection { override fun onPostScroll( consumed: Offset, available: Offset, source: NestedScrollSource ): Offset { return if (source == NestedScrollSource.SideEffect) { available } else { Offset.Zero } } } }
Wszystkie wywołania zwrotne zawierają informacje o typie NestedScrollSource
.
NestedScrollDispatcher
inicjuje zagnieżdżony cykl przewijania. Użycie rozsyłacza i wywołanie jego metod powoduje uruchomienie cyklu. Kontenery, które można przewijać, mają wbudowany moduł rozsyłający, który wysyła do systemu różnice uchwycone podczas wykonywania gestów. Z tego powodu większość przypadków użycia dostosowywania przewijania zagnieżdżonego wymaga użycia NestedScrollConnection
zamiast rozsyłarki, aby reagować na istniejące różnice, a nie wysyłać nowych.
Więcej informacji znajdziesz w sekcji NestedScrollDispatcherSample
.
Zmienianie rozmiaru obrazu podczas przewijania
Podczas przewijania przez użytkownika możesz tworzyć dynamiczne efekty wizualne, w których obraz zmienia rozmiar w zależności od pozycji przewijania.
Zmienianie rozmiaru obrazu na podstawie pozycji przewijania
Ten fragment kodu demonstruje zmianę rozmiaru obrazu w elementach LazyColumn
na podstawie pozycji przewijania pionowego. Obraz zmniejsza się, gdy użytkownik przewija w dół, a powiększa się, gdy przewija w górę, pozostając w określonym zakresie minimalnego i maksymalnego rozmiaru:
@Composable fun ImageResizeOnScrollExample( modifier: Modifier = Modifier, maxImageSize: Dp = 300.dp, minImageSize: Dp = 100.dp ) { var currentImageSize by remember { mutableStateOf(maxImageSize) } var imageScale by remember { mutableFloatStateOf(1f) } val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { // Calculate the change in image size based on scroll delta val delta = available.y val newImageSize = currentImageSize + delta.dp val previousImageSize = currentImageSize // Constrain the image size within the allowed bounds currentImageSize = newImageSize.coerceIn(minImageSize, maxImageSize) val consumed = currentImageSize - previousImageSize // Calculate the scale for the image imageScale = currentImageSize / maxImageSize // Return the consumed scroll amount return Offset(0f, consumed.value) } } } Box(Modifier.nestedScroll(nestedScrollConnection)) { LazyColumn( Modifier .fillMaxWidth() .padding(15.dp) .offset { IntOffset(0, currentImageSize.roundToPx()) } ) { // Placeholder list items items(100, key = { it }) { Text( text = "Item: $it", style = MaterialTheme.typography.bodyLarge ) } } Image( painter = ColorPainter(Color.Red), contentDescription = "Red color image", Modifier .size(maxImageSize) .align(Alignment.TopCenter) .graphicsLayer { scaleX = imageScale scaleY = imageScale // Center the image vertically as it scales translationY = -(maxImageSize.toPx() - currentImageSize.toPx()) / 2f } ) } }
Najważniejsze informacje o kodzie
- Ten kod używa funkcji
NestedScrollConnection
do przechwytywania zdarzeń przewijania. onPreScroll
oblicza zmianę rozmiaru obrazu na podstawie przesunięcia osi Y.- Zmienna stanu
currentImageSize
przechowuje bieżący rozmiar obrazu, który jest ograniczony do wartości odminImageSize
domaxImageSize. imageScale
i wynika z wartościcurrentImageSize
. - Odsunięcia
LazyColumn
na podstawiecurrentImageSize
. - Funkcja
Image
używa modyfikatoragraphicsLayer
, aby zastosować obliczony współczynnik skali. - Element
translationY
w elementziegraphicsLayer
zapewnia, że obraz pozostaje wyśrodkowany w pionowej podczas skalowania.
Wynik
Powyższy fragment kodu powoduje efekt skalowania obrazu podczas przewijania:
Interoperacyjność zagnieżdżonego przewijania
Jeśli spróbujesz zagnieżdżać elementy przewijalne View
w komponentach przewijalnych lub odwrotnie, możesz napotkać problemy. Najbardziej zauważalne są te, które występują, gdy przewijasz element potomny i docierasz do jego początku lub końca, a oczekujesz, że element nadrzędny przejmie przewijanie. Jednak może się okazać, że nie będzie ono możliwe lub nie będzie działać zgodnie z oczekiwaniami.
Ten problem jest spowodowany oczekiwaniami związanymi z komponowalnymi komponentami z możliwością przewijania.
Składowe przewijalne podlegają regułom „domyślnego przewijania w ramach zagnieżdżonego elementu”. Oznacza to, że każdy przewijalny kontener musi uczestniczyć w łańcuchu zagnieżdżonego przewijania, zarówno jako element nadrzędny za pomocą NestedScrollConnection
, jak i jako element podrzędny za pomocą NestedScrollDispatcher
.
Gdy element podrzędny znajduje się na granicy, powoduje przewijanie elementu nadrzędnego. Na przykład ta reguła umożliwia prawidłowe działanie akcji Compose Pager
i Compose LazyRow
. Jednak podczas przewijania w celu zapewnienia interoperacyjności za pomocą ViewPager2
lub RecyclerView
, ponieważ te nie implementują NestedScrollingParent3
, ciągłe przewijanie z elementu podrzędnego do nadrzędnego nie jest możliwe.
Aby włączyć interfejs API do obsługi sterowania przewijaniem w elementach View
i komponentach przewijanych, które są w obu kierunkach zagnieżdżone, możesz użyć interfejsu API do obsługi sterowania przewijaniem w elementach zagnieżdżonych, aby rozwiązać te problemy w tych scenariuszach:
Współpracujący rodzic View
zawiera element podrzędny dziecko ComposeView
Współdziałający komponent nadrzędny View
to taki, który już implementuje NestedScrollingParent3
, a zatem może odbierać różnice przewijania z współdziałającego zagnieżdżonego komponentu podrzędnego. ComposeView
działa w tym przypadku jako usługa podrzędna i musi (pośrednio) zaimplementować NestedScrollingChild3
.
Przykładem współpracującego rodzica jest androidx.coordinatorlayout.widget.CoordinatorLayout
.
Jeśli potrzebujesz możliwości korzystania z przewijania zagnieżdżonego między przewijalnymi kontenerami nadrzędnymi View
a zagnieżdżonymi przewijalnymi komponentami podrzędnymi, możesz użyć rememberNestedScrollInteropConnection()
.
rememberNestedScrollInteropConnection()
pozwala i pamięta NestedScrollConnection
, która umożliwia współdziałanie zagnieżdżonego przewijania między elementem nadrzędnym View
, który implementuje NestedScrollingParent3
, a elementem podrzędnym Compose. Należy go używać w połączeniu z modyfikatorem nestedScroll
. Ponieważ przewijanie zagnieżchłe jest domyślnie włączone po stronie Compose, możesz użyć tego połączenia, aby włączyć przewijanie zagnieżchłe po stronie View
i dodać niezbędną logikę łączącą Views
i komponenty.
Często używane są komponenty CoordinatorLayout
, CollapsingToolbarLayout
i komponent potomny, jak w tym przykładzie:
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="100dp" android:fitsSystemWindows="true"> <com.google.android.material.appbar.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <!--...--> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.compose.ui.platform.ComposeView android:id="@+id/compose_view" app:layout_behavior="@string/appbar_scrolling_view_behavior" android:layout_width="match_parent" android:layout_height="match_parent"/> </androidx.coordinatorlayout.widget.CoordinatorLayout>
W aktywności lub fragmencie musisz skonfigurować składankę podrzędną i wymagane komponenty:
NestedScrollConnection
open class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) findViewById<ComposeView>(R.id.compose_view).apply { setContent { val nestedScrollInterop = rememberNestedScrollInteropConnection() // Add the nested scroll connection to your top level @Composable element // using the nestedScroll modifier. LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop)) { items(20) { item -> Box( modifier = Modifier .padding(16.dp) .height(56.dp) .fillMaxWidth() .background(Color.Gray), contentAlignment = Alignment.Center ) { Text(item.toString()) } } } } } } }
Element nadrzędny zawierający element podrzędny AndroidView
Ten scenariusz obejmuje implementację interfejsu API do interoperacyjności w ramach sterowania złożonym w komponencie, gdy masz komponent nadrzędny zawierający komponent podrzędny AndroidView
. AndroidView
implementuje NestedScrollDispatcher
, ponieważ działa jako element podrzędny względem elementu nadrzędnego Compose scrolling, a także NestedScrollingParent3
, ponieważ działa jako element nadrzędny względem elementu podrzędnego View
. Element nadrzędny kompozytora będzie mógł otrzymywać zagnieżdżone wartości delta przewijania z zagnieżdżonego elementu podrzędnego, który można przewijać.View
Ten przykład pokazuje, jak w tym scenariuszu uzyskać interoperacyjność przewijania zagnieżdżonego wraz z zwijanym paskiem narzędzi w edytorze:
@Composable
private fun NestedScrollInteropComposeParentWithAndroidChildExample() {
val toolbarHeightPx = with(LocalDensity.current) { ToolbarHeight.roundToPx().toFloat() }
val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }
// Sets up the nested scroll connection between the Box composable parent
// and the child AndroidView containing the RecyclerView
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
// Updates the toolbar offset based on the scroll to enable
// collapsible behaviour
val delta = available.y
val newOffset = toolbarOffsetHeightPx.value + delta
toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
return Offset.Zero
}
}
}
Box(
Modifier
.fillMaxSize()
.nestedScroll(nestedScrollConnection)
) {
TopAppBar(
modifier = Modifier
.height(ToolbarHeight)
.offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) }
)
AndroidView(
{ context ->
LayoutInflater.from(context)
.inflate(R.layout.view_in_compose_nested_scroll_interop, null).apply {
with(findViewById<RecyclerView>(R.id.main_list)) {
layoutManager = LinearLayoutManager(context, VERTICAL, false)
adapter = NestedScrollInteropAdapter()
}
}.also {
// Nested scrolling interop is enabled when
// nested scroll is enabled for the root View
ViewCompat.setNestedScrollingEnabled(it, true)
}
},
// ...
)
}
}
private class NestedScrollInteropAdapter :
Adapter<NestedScrollInteropAdapter.NestedScrollInteropViewHolder>() {
val items = (1..10).map { it.toString() }
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): NestedScrollInteropViewHolder {
return NestedScrollInteropViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, false)
)
}
override fun onBindViewHolder(holder: NestedScrollInteropViewHolder, position: Int) {
// ...
}
class NestedScrollInteropViewHolder(view: View) : ViewHolder(view) {
fun bind(item: String) {
// ...
}
}
// ...
}
Ten przykład pokazuje, jak używać interfejsu API z modyfikatorem scrollable
:
@Composable
fun ViewInComposeNestedScrollInteropExample() {
Box(
Modifier
.fillMaxSize()
.scrollable(rememberScrollableState {
// View component deltas should be reflected in Compose
// components that participate in nested scrolling
it
}, Orientation.Vertical)
) {
AndroidView(
{ context ->
LayoutInflater.from(context)
.inflate(android.R.layout.list_item, null)
.apply {
// Nested scrolling interop is enabled when
// nested scroll is enabled for the root View
ViewCompat.setNestedScrollingEnabled(this, true)
}
}
)
}
}
Na koniec przykład pokazujący, jak za pomocą interfejsu API do interoperacyjności przewijania zagnieżdżonego BottomSheetDialogFragment
można uzyskać pożądane zachowanie przeciągania i zamykania:
class BottomSheetFragment : BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val rootView: View = inflater.inflate(R.layout.fragment_bottom_sheet, container, false)
rootView.findViewById<ComposeView>(R.id.compose_view).apply {
setContent {
val nestedScrollInterop = rememberNestedScrollInteropConnection()
LazyColumn(
Modifier
.nestedScroll(nestedScrollInterop)
.fillMaxSize()
) {
item {
Text(text = "Bottom sheet title")
}
items(10) {
Text(
text = "List item number $it",
modifier = Modifier.fillMaxWidth()
)
}
}
}
return rootView
}
}
}
Pamiętaj, że funkcja rememberNestedScrollInteropConnection()
zainstaluje element NestedScrollConnection
w elemencie, do którego go dodasz. NestedScrollConnection
odpowiada za przesyłanie różnic z poziomu Compose na poziom View
. Umożliwia to elementowi udział w przewijaniu zagnieżdżonym, ale nie powoduje automatycznego przewijania elementów. W przypadku komponentów, które nie są przewijane automatycznie, takich jak Box
czy Column
, różnice w przewijaniu takich komponentów nie będą się rozprzestrzeniać w ramach zaimplementowanego systemu przewijania i nie dotrą do komponentu nadrzędnego View
.NestedScrollConnection
rememberNestedScrollInteropConnection()
Aby rozwiązać ten problem,
upewnij się, że dla tych typów zagnieżdżonych komponentów są też ustawione modyfikatory przewijania. Więcej informacji znajdziesz w poprzedniej sekcji dotyczącej zagnieżdżonego przewijania.
rodzic, który nie współpracuje View
, z dzieckiem ComposeView
Widok niewspółpracujący to taki, który nie implementuje niezbędnych interfejsów NestedScrolling
po stronie View
. Oznacza to, że wbudowana obsługa przewijania zagnieżdżonego w tych elementach Views
nie działa od razu po zainstalowaniu. Niewspółpracujące Views
to RecyclerView
i ViewPager2
.
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy obsługa JavaScript jest wyłączona
- Omówienie gestów
- Migracja
CoordinatorLayout
do tworzenia wiadomości - Korzystanie z widoków w sekcji Tworzenie wiadomości