Scorri

Modificatori di scorrimento

I modificatori verticalScroll e horizontalScroll offrono il modo più semplice per consentire all'utente di scorrere un elemento quando i limiti dei suoi contenuti sono maggiori dei vincoli di dimensione massima. Con i modificatori verticalScroll e horizontalScroll non è necessario tradurre o compensare i contenuti.

@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))
        }
    }
}

Un semplice elenco verticale che risponde ai gesti di scorrimento

ScrollState consente di modificare la posizione di scorrimento o ottenere il suo stato attuale. Per crearlo con i parametri predefiniti, utilizza 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))
        }
    }
}

Modificatore scorrevole

Il modificatore scrollable è diverso dai modificatori di scorrimento in quanto scrollable rileva i gesti di scorrimento e acquisisce i delta, ma non compensa automaticamente i contenuti. Questa operazione viene invece delegata all'utente tramite ScrollableState , che è necessario per il corretto funzionamento di questo modificatore.

Quando crei ScrollableState, devi fornire una funzione consumeScrollDelta che verrà richiamata a ogni passaggio di scorrimento (tramite input tramite gesto, scorrimento fluido o scorrimento rapido) con il delta in pixel. Questa funzione deve restituire la quantità di distanza di scorrimento consumata, per garantire che l'evento venga propagato correttamente nei casi in cui sono presenti elementi nidificati con il modificatore scrollable.

Il seguente snippet rileva i gesti e mostra un valore numerico per un offset, ma non sposta alcun elemento:

@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())
    }
}

Un elemento UI che rileva la pressione del dito e mostra il valore numerico per
la posizione
del dito

Scorrimento nidificato

Lo scorrimento nidificato è un sistema in cui più componenti di scorrimento contenuti l'uno all'interno dell'altro funzionano insieme reagendo a un singolo gesto di scorrimento e comunicando le loro variazioni di scorrimento.

Il sistema di scorrimento nidificato consente il coordinamento tra i componenti scorrevoli e collegati gerarchicamente (il più delle volte condividendo lo stesso elemento principale). Questo sistema collega i contenitori scorrevoli e consente l'interazione con i delta di scorrimento che vengono propagati e condivisi tra i contenitori.

Compose offre diversi modi per gestire lo scorrimento nidificato tra i composable. Un esempio tipico di scorrimento nidificato è un elenco all'interno di un altro elenco, mentre un caso più complesso è una barra degli strumenti comprimibile.

Scorrimento nidificato automatico

Lo scorrimento nidificato semplice non richiede alcun intervento da parte tua. I gesti che avviano un'azione di scorrimento vengono propagati automaticamente dai figli ai genitori, in modo che quando il figlio non può scorrere ulteriormente, il gesto venga gestito dal suo elemento padre.

Lo scorrimento nidificato automatico è supportato e fornito immediatamente da alcuni componenti e modificatori di Compose: verticalScroll, horizontalScroll, scrollable, API Lazy e TextField. Ciò significa che quando l'utente scorre un elemento figlio interno di componenti nidificati, i modificatori precedenti propagano i delta di scorrimento ai genitori che supportano lo scorrimento nidificato.

L'esempio seguente mostra elementi con un modificatore verticalScroll applicato al loro interno in un contenitore a cui è stato applicato anche un modificatore 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)
                    )
                }
            }
        }
    }
}

Due elementi UI con scorrimento verticale nidificati, che rispondono ai gesti all'interno e
all'esterno dell'elemento
interno

Utilizzo del modificatore nestedScroll

Se devi creare uno scorrimento coordinato avanzato tra più elementi, il modificatore nestedScroll ti offre maggiore flessibilità definendo una gerarchia di scorrimento nidificata. Come menzionato nella sezione precedente, alcuni componenti supportano lo scorrimento nidificato integrato. Tuttavia, per i composable che non sono scorrevoli automaticamente, come Box o Column, i delta di scorrimento su questi componenti non si propagano nel sistema di scorrimento nidificato e non raggiungono NestedScrollConnection né il componente principale. Per risolvere il problema, puoi utilizzare nestedScroll per estendere questo supporto ad altri componenti, inclusi quelli personalizzati.

Ciclo di scorrimento nidificato

Il ciclo di scorrimento nidificato è il flusso di delta di scorrimento inviati su e giù nell'albero della gerarchia attraverso tutti i componenti (o nodi) che fanno parte del sistema di scorrimento nidificato, ad esempio utilizzando componenti e modificatori scorrevoli o nestedScroll.

Fasi del ciclo di scorrimento nidificato

Quando un evento trigger (ad esempio un gesto) viene rilevato da un componente scorrevole, prima ancora che venga attivata l'azione di scorrimento effettiva, i delta generati vengono inviati al sistema di scorrimento nidificato e attraversano tre fasi: pre-scorrimento, consumo dei nodi e post-scorrimento.

Fasi del ciclo di scorrimento
nidificato

Nella prima fase di pre-scorrimento, il componente che ha ricevuto i delta dell'evento trigger li invierà verso l'alto, attraverso l'albero della gerarchia, fino al genitore di primo livello. Gli eventi delta verranno quindi propagati verso il basso, il che significa che i delta verranno propagati dalla radice principale verso l'elemento secondario che ha avviato il ciclo di scorrimento nidificato.

Fase di pre-scorrimento - invio

In questo modo, i genitori dello scorrimento nidificato (i composable che utilizzano nestedScroll o modificatori scorrevoli) hanno la possibilità di fare qualcosa con il delta prima che il nodo stesso possa utilizzarlo.

Fase di pre-scorrimento - propagazione
verso il basso

Nella fase di consumo dei nodi, il nodo stesso utilizzerà il delta non utilizzato dai relativi nodi principali. Questo è il momento in cui il movimento di scorrimento viene effettivamente eseguito ed è visibile.

Fase di consumo del nodo

Durante questa fase, il ragazzo può scegliere di consumare tutto o parte dello scroll rimanente. Tutto ciò che rimane verrà inviato di nuovo per essere sottoposto alla fase post-scorrimento.

Infine, nella fase post-scorrimento, tutto ciò che il nodo stesso non ha consumato verrà inviato di nuovo ai suoi antenati per il consumo.

Fase post-scorrimento - invio
verso l'alto

La fase post-scroll funziona in modo simile alla fase pre-scroll, in cui uno qualsiasi dei genitori può scegliere di consumare o meno.

Fase post-scorrimento - propagazione
verso il basso

Analogamente allo scorrimento, quando un gesto di trascinamento termina, l'intenzione dell'utente può essere tradotta in una velocità utilizzata per scorrere rapidamente (scorrere utilizzando un'animazione) il contenitore scorrevole. Lo scorrimento rapido fa parte anche del ciclo di scorrimento nidificato e le velocità generate dall'evento di trascinamento passano attraverso fasi simili: pre-scorrimento rapido, consumo dei nodi e post-scorrimento rapido. Tieni presente che l'animazione di scorrimento è associata solo al gesto di tocco e non viene attivata da altri eventi, come l'accessibilità o lo scorrimento hardware.

Partecipare al ciclo di scorrimento nidificato

La partecipazione al ciclo comporta l'intercettazione, il consumo e la segnalazione del consumo dei delta lungo la gerarchia. Compose fornisce un insieme di strumenti per influenzare il funzionamento del sistema di scorrimento nidificato e il modo di interagire direttamente con esso, ad esempio quando devi fare qualcosa con i delta di scorrimento prima che un componente scorrevole inizi a scorrere.

Se il ciclo di scorrimento nidificato è un sistema che agisce su una catena di nodi, il modificatore nestedScroll è un modo per intercettare e inserire queste modifiche e influenzare i dati (delta di scorrimento) che vengono propagati nella catena. Questo modificatore può essere posizionato ovunque nella gerarchia e comunica con le istanze del modificatore di scorrimento nidificato verso l'alto dell'albero, in modo da poter condividere informazioni tramite questo canale. Gli elementi costitutivi di questo modificatore sono NestedScrollConnection e NestedScrollDispatcher.

NestedScrollConnection offre un modo per rispondere alle fasi del ciclo di scorrimento nidificato e influenzare il sistema di scorrimento nidificato. È composto da quattro metodi di callback, ognuno dei quali rappresenta una delle fasi di consumo: pre/post-scorrimento e pre/post-trascinamento:

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
    }
}

Ogni callback fornisce anche informazioni sul delta propagato: il delta available per quella fase specifica e il delta consumed utilizzato nelle fasi precedenti. Se in qualsiasi momento vuoi interrompere la propagazione dei delta nella gerarchia, puoi utilizzare la connessione di scorrimento nidificata:

val disabledNestedScrollConnection = remember {
    object : NestedScrollConnection {
        override fun onPostScroll(
            consumed: Offset,
            available: Offset,
            source: NestedScrollSource
        ): Offset {
            return if (source == NestedScrollSource.SideEffect) {
                available
            } else {
                Offset.Zero
            }
        }
    }
}

Tutti i callback forniscono informazioni sul tipo di NestedScrollSource.

NestedScrollDispatcher inizializza il ciclo di scorrimento nidificato. L'utilizzo di un dispatcher e la chiamata dei relativi metodi attivano il ciclo. I contenitori scorrevoli hanno un dispatcher integrato che invia al sistema i delta acquisiti durante i gesti. Per questo motivo, la maggior parte dei casi d'uso della personalizzazione dello scorrimento nidificato prevede l'utilizzo di NestedScrollConnection anziché di un dispatcher, per reagire ai delta già esistenti anziché inviarne di nuovi. Per altri utilizzi, consulta NestedScrollDispatcherSample.

Ridimensionare un'immagine durante lo scorrimento

Man mano che l'utente scorre, puoi creare un effetto visivo dinamico in cui le dimensioni dell'immagine cambiano in base alla posizione di scorrimento.

Ridimensionare un'immagine in base alla posizione di scorrimento

Questo snippet mostra il ridimensionamento di un'immagine all'interno di un LazyColumn in base alla posizione di scorrimento verticale. L'immagine si rimpicciolisce man mano che l'utente scorre verso il basso e si ingrandisce man mano che scorre verso l'alto, rimanendo entro i limiti di dimensione minima e massima definiti:

@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
                }
        )
    }
}

Punti chiave del codice

  • Questo codice utilizza un NestedScrollConnection per intercettare gli eventi di scorrimento.
  • onPreScroll calcola la variazione delle dimensioni dell'immagine in base allo scorrimento delta.
  • La variabile di stato currentImageSize memorizza le dimensioni attuali dell'immagine, vincolate tra minImageSize e maxImageSize. imageScale, derivate da currentImageSize.
  • I compensi LazyColumn si basano su currentImageSize.
  • Image utilizza un modificatore graphicsLayer per applicare la scala calcolata.
  • Il translationY all'interno di graphicsLayer assicura che l'immagine rimanga centrata verticalmente durante il ridimensionamento.

Risultato

Lo snippet precedente genera un effetto di scalabilità dell'immagine durante lo scorrimento:

Figura 1. Un effetto di ridimensionamento dell'immagine durante lo scorrimento.

Interoperabilità dello scorrimento annidato

Quando provi a nidificare elementi View scorrevoli in composable scorrevoli o viceversa, potresti riscontrare problemi. I più evidenti si verificano quando scorri l'elemento secondario e raggiungi i limiti iniziali o finali e ti aspetti che l'elemento principale prenda il controllo dello scorrimento. Tuttavia, questo comportamento previsto potrebbe non verificarsi o non funzionare come previsto.

Questo problema è il risultato delle aspettative integrate nei composable scorrevoli. I composable scorrevoli hanno una regola "nested-scroll-by-default", il che significa che qualsiasi contenitore scorrevole deve partecipare alla catena di scorrimento nidificato, sia come elemento principale tramite NestedScrollConnection, sia come elemento secondario tramite NestedScrollDispatcher. Il componente secondario scorre in modo nidificato per il componente principale quando si trova al limite. Ad esempio, questa regola consente a Compose Pager e Compose LazyRow di funzionare bene insieme. Tuttavia, quando lo scorrimento dell'interoperabilità viene eseguito con ViewPager2 o RecyclerView, poiché questi non implementano NestedScrollingParent3, lo scorrimento continuo da figlio a genitore non è possibile.

Per abilitare l'API di interoperabilità dello scorrimento nidificato tra elementi View scorrevoli e composable scorrevoli, nidificati in entrambe le direzioni, puoi utilizzare l'API di interoperabilità dello scorrimento nidificato per mitigare questi problemi nei seguenti scenari.

Un genitore che collabora View contenente un figlio ComposeView

Un genitore cooperativo View è uno che implementa già NestedScrollingParent3 e pertanto è in grado di ricevere delta di scorrimento da un elemento componibile figlio nidificato cooperativo. ComposeView fungerebbe da figlio in questo caso e dovrebbe implementare (indirettamente) NestedScrollingChild3. Un esempio di genitore collaborativo è androidx.coordinatorlayout.widget.CoordinatorLayout.

Se hai bisogno dell'interoperabilità dello scorrimento nidificato tra i contenitori View principali scorrevoli e i composable secondari scorrevoli nidificati, puoi utilizzare rememberNestedScrollInteropConnection().

rememberNestedScrollInteropConnection() consente e memorizza NestedScrollConnection che attiva l'interoperabilità dello scorrimento nidificato tra un elemento principale View che implementa NestedScrollingParent3 e un elemento secondario Compose. Deve essere utilizzato insieme a un modificatore nestedScroll. Poiché lo scorrimento nidificato è attivato per impostazione predefinita sul lato Compose, puoi utilizzare questa connessione per attivare lo scorrimento nidificato sul lato View e aggiungere la logica di collegamento necessaria tra Views e i composable.

Un caso d'uso frequente è l'utilizzo di CoordinatorLayout, CollapsingToolbarLayout e un composable secondario, come mostrato in questo esempio:

<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>

Nell'attività o nel fragment, devi configurare il composable figlio e il NestedScrollConnection richiesto:

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())
                        }
                    }
                }
            }
        }
    }
}

Un composable principale contenente un composable secondario AndroidView

Questo scenario riguarda l'implementazione dell'API di interoperabilità dello scorrimento nidificato sul lato Compose, quando hai un composable principale contenente un composable secondario AndroidView. AndroidView implementa NestedScrollDispatcher, in quanto funge da elemento secondario di un elemento principale di scorrimento di Compose, nonché NestedScrollingParent3 , in quanto funge da elemento principale di un elemento secondario di scorrimento View. Il componente padre potrà quindi ricevere i delta di scorrimento nidificati da un componente secondario scorrevole nidificato View.

L'esempio seguente mostra come ottenere l'interoperabilità dello scorrimento nidificato in questo scenario, insieme a una barra degli strumenti comprimibile di Compose:

@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) {
            // ...
        }
    }
    // ...
}

Questo esempio mostra come utilizzare l'API con un modificatore 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)
                    }
            }
        )
    }
}

Infine, questo esempio mostra come viene utilizzata l'API di interoperabilità dello scorrimento nidificato con BottomSheetDialogFragment per ottenere un comportamento di trascinamento e chiusura riuscito:

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
        }
    }
}

Tieni presente che rememberNestedScrollInteropConnection() installerà un NestedScrollConnection nell'elemento a cui lo colleghi. NestedScrollConnection è responsabile della trasmissione delle differenze dal livello di composizione al livello View. In questo modo l'elemento può partecipare allo scorrimento nidificato, ma non consente lo scorrimento automatico degli elementi. Per i composable non scorrevoli automaticamente, come Box o Column, i delta di scorrimento su questi componenti non si propagheranno nel sistema di scorrimento nidificato e i delta non raggiungeranno NestedScrollConnection fornito da rememberNestedScrollInteropConnection(), pertanto non raggiungeranno il componente View principale. Per risolvere il problema, assicurati di impostare anche i modificatori scorrevoli per questi tipi di componenti componibili nidificati. Per informazioni più dettagliate, puoi fare riferimento alla sezione precedente sullo scorrimento nidificato.

Un genitore non collaborativo View contenente un bambino ComposeView

Una visualizzazione non cooperativa è una visualizzazione che non implementa le interfacce NestedScrolling necessarie sul lato View. Tieni presente che ciò significa che l'interoperabilità dello scorrimento nidificato con questi Views non funziona immediatamente. I Views non cooperanti sono RecyclerView e ViewPager2.

Risorse aggiuntive