Verschachtelte Scroll-Modifikatoren

Verschachteltes Scrollen (Nested Scrolling) ist ein System, bei dem mehrere ineinander verschachtelte scrollbare Komponenten zusammenarbeiten, indem sie auf eine einzelne Scroll-Geste reagieren und ihre Scroll-Deltas (Änderungen) untereinander kommunizieren.

Das System für verschachteltes Scrollen ermöglicht die Koordination zwischen Komponenten, die scrollbar und hierarchisch verknüpft sind (meistens durch die gemeinsame Nutzung desselben übergeordneten Elements). Dieses System verknüpft Scrollcontainer und ermöglicht die Interaktion mit den Scroll-Deltas, die zwischen ihnen weitergegeben und geteilt werden.

Compose bietet mehrere Möglichkeiten, verschachteltes Scrollen zwischen Composables zu verarbeiten. Ein typisches Beispiel für verschachteltes Scrollen ist eine Liste in einer anderen Liste. Ein komplexerer Fall ist eine minimierbare Symbolleiste.

Automatisches verschachteltes Scrollen

Für einfaches verschachteltes Scrollen ist keine Aktion Ihrerseits erforderlich. Gesten, die eine Scrollaktion initiieren, werden automatisch von untergeordneten an übergeordnete Elemente weitergegeben. Wenn das untergeordnete Element nicht weiter scrollen kann, wird die Geste vom übergeordneten Element verarbeitet.

Automatisches verschachteltes Scrollen wird von einigen Compose-Komponenten und ‑Modifikatoren standardmäßig unterstützt: verticalScroll, horizontalScroll, scrollable, Lazy-APIs und TextField. Das bedeutet, dass beim Scrollen eines inneren untergeordneten Elements verschachtelter Komponenten die vorherigen Modifikatoren die Scroll-Deltas an die übergeordneten Elemente weitergeben, die verschachteltes Scrollen unterstützen.

Das folgende Beispiel zeigt Elemente mit einem verticalScroll-Modifikator in einem Container, auf den ebenfalls ein verticalScroll-Modifikator angewendet wurde.

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

Zwei verschachtelte UI-Elemente mit vertikalem Scrollen, die auf Gesten innerhalb und außerhalb des inneren Elements reagieren
Abbildung 1. Zwei verschachtelte vertikal scrollbare UI-Elemente, die auf Gesten innerhalb und außerhalb des inneren Elements reagieren.

Modifikator nestedScroll verwenden

Wenn Sie ein erweitertes koordiniertes Scrollen zwischen mehreren Elementen erstellen möchten, bietet der Modifikator nestedScroll mehr Flexibilität, da Sie eine verschachtelte Scrollhierarchie definieren können. Wie im vorherigen Abschnitt erwähnt, unterstützen einige Komponenten das verschachtelte Scrollen. Bei nicht automatisch scrollbaren Composables wie Box oder Column werden Scroll-Deltas jedoch nicht im verschachtelten Scrollsystem weitergegeben und erreichen weder NestedScrollConnection noch die übergeordnete Komponente. Um dieses Problem zu beheben, können Sie nestedScroll verwenden, um diese Unterstützung anderen Komponenten, einschließlich benutzerdefinierter Komponenten, zu gewähren.

Verschachtelter Scrollzyklus

Der verschachtelte Scrollzyklus ist der Fluss von Scroll-Deltas, die über alle Komponenten (oder Knoten) des verschachtelten Scrollsystems in der Hierarchie nach oben und unten gesendet werden, z. B. mithilfe von scrollbaren Komponenten und Modifikatoren oder nestedScroll.

Phasen des verschachtelten Scrollzyklus

Wenn eine scrollbare Komponente ein Triggerereignis (z. B. eine Geste) erkennt, werden die generierten Deltas an das verschachtelte Scrollsystem gesendet, bevor die eigentliche Scrollaktion ausgelöst wird. Sie durchlaufen drei Phasen: Pre-Scroll, Knotenverbrauch und Post-Scroll.

Phasen des verschachtelten Scrollzyklus
Abbildung 2: Phasen des verschachtelten Scrollzyklus.

In der ersten Phase, vor dem Scrollen, sendet die Komponente, die die Deltawerte des Triggerereignisses empfangen hat, diese Ereignisse über die Hierarchie nach oben an das oberste übergeordnete Element. Die Delta-Ereignisse werden dann nach unten weitergegeben. Das bedeutet, dass Deltas vom obersten übergeordneten Element nach unten zum untergeordneten Element weitergegeben werden, das den verschachtelten Scrollzyklus gestartet hat.

Phase vor dem Scrollen – nach oben senden
Abbildung 3: Pre-scroll phase: dispatching up.

Dadurch können die übergeordneten Elemente des verschachtelten Scrollens (Kompositionen, die nestedScroll oder scrollbare Modifikatoren verwenden) etwas mit dem Delta tun, bevor der Knoten selbst es nutzen kann.

Phase vor dem Scrollen – nach unten bewegen
Abbildung 4: Pre-scroll phase - bubbling down.

In der Knotenverbrauchsphase verwendet der Knoten selbst das Delta, das nicht von seinen übergeordneten Elementen verwendet wurde. Hier wird die Scrollbewegung tatsächlich ausgeführt und ist sichtbar.

Knotenverbrauchsphase
Abbildung 5. Knotenverbrauchsphase.

In dieser Phase kann das Kind die verbleibende Scroll-Menge ganz oder teilweise verbrauchen. Alles, was übrig bleibt, wird zurückgeschickt, um die Phase nach dem Scrollen zu durchlaufen.

In der Phase nach dem Scrollen wird alles, was der Knoten selbst nicht verbraucht hat, wieder an seine Ancestors gesendet.

Phase nach dem Scrollen – nach oben senden
Abbildung 6: Phase nach dem Scrollen – nach oben senden.

Die Phase nach dem Scrollen funktioniert ähnlich wie die Phase vor dem Scrollen. Jedes übergeordnete Element kann die Bewegung verarbeiten oder nicht.

Phase nach dem Scrollen – Bubbling nach unten
Abbildung 7. Phase nach dem Scrollen – Bubbling nach unten.

Ähnlich wie beim Scrollen kann die Absicht des Nutzers nach Abschluss einer Ziehgeste in eine Geschwindigkeit umgewandelt werden, die zum schnellen Scrollen (Scrollen mit einer Animation) des scrollbaren Containers verwendet wird. Das Fling ist auch Teil des verschachtelten Scrollzyklus und die durch das Drag-Ereignis generierten Geschwindigkeiten durchlaufen ähnliche Phasen: Pre-Fling, Knotenverbrauch und Post-Fling. Die Wisch-Animation ist nur mit Touch-Gesten verknüpft und wird nicht durch andere Ereignisse wie Bedienungshilfen oder Hardware-Scrollen ausgelöst.

Am verschachtelten Scrollzyklus teilnehmen

Die Teilnahme am Zyklus bedeutet, dass Deltas entlang der Hierarchie abgefangen, genutzt und gemeldet werden. Compose bietet eine Reihe von Tools, mit denen Sie beeinflussen können, wie das verschachtelte Scrollsystem funktioniert und wie Sie direkt damit interagieren können, z. B. wenn Sie etwas mit den Scroll-Deltas tun müssen, bevor eine scrollbare Komponente überhaupt mit dem Scrollen beginnt.

Wenn der verschachtelte Scrollzyklus ein System ist, das auf eine Kette von Knoten einwirkt, ist der Modifikator nestedScroll eine Möglichkeit, diese Änderungen abzufangen und einzufügen und die Daten (Scroll-Deltas) zu beeinflussen, die in der Kette weitergegeben werden. Dieser Modifikator kann an einer beliebigen Stelle in der Hierarchie platziert werden und kommuniziert mit verschachtelten Scroll-Modifikator-Instanzen im Baum, um Informationen über diesen Kanal zu teilen. Die Bausteine dieses Modifikators sind NestedScrollConnection und NestedScrollDispatcher.

NestedScrollConnection bietet eine Möglichkeit, auf die Phasen des verschachtelten Scrollzyklus zu reagieren und das verschachtelte Scrollsystem zu beeinflussen. Sie besteht aus vier Callback-Methoden, die jeweils eine der Verbrauchsphasen darstellen: vor/nach dem Scrollen und vor/nach dem Fling:

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

Jeder Callback liefert auch Informationen über das Delta, das weitergegeben wird: available-Delta für diese bestimmte Phase und consumed-Delta, das in den vorherigen Phasen verbraucht wurde. Wenn Sie die Weitergabe von Deltas in der Hierarchie irgendwann beenden möchten, können Sie dazu die verschachtelte Scrollverbindung verwenden:

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

Alle Callbacks enthalten Informationen zum Typ NestedScrollSource.

NestedScrollDispatcher initialisiert den verschachtelten Scrollzyklus. Wenn Sie einen Dispatcher verwenden und seine Methoden aufrufen, wird der Zyklus ausgelöst. Scrollbare Container haben einen integrierten Dispatcher, der Deltas, die während Gesten erfasst werden, an das System sendet. Aus diesem Grund wird bei den meisten Anwendungsfällen für die Anpassung des verschachtelten Scrollens NestedScrollConnection anstelle eines Dispatchers verwendet, um auf bereits vorhandene Deltas zu reagieren, anstatt neue zu senden. Weitere Informationen finden Sie unter NestedScrollDispatcherSample.

Bildgröße beim Scrollen anpassen

Wenn der Nutzer scrollt, können Sie einen dynamischen visuellen Effekt erstellen, bei dem sich die Größe des Bildes je nach Scrollposition ändert.

Größe eines Bildes basierend auf der Scrollposition anpassen

In diesem Snippet wird gezeigt, wie die Größe eines Bildes in einem LazyColumn basierend auf der vertikalen Scrollposition angepasst wird. Das Bild wird kleiner, wenn der Nutzer nach unten scrollt, und größer, wenn er nach oben scrollt. Es bleibt dabei innerhalb der definierten Mindest- und Höchstgröße:

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

Wichtige Punkte zum Code

  • In diesem Code wird ein NestedScrollConnection verwendet, um Scroll-Ereignisse abzufangen.
  • onPreScroll berechnet die Änderung der Bildgröße basierend auf dem Scroll-Delta.
  • Die Statusvariable currentImageSize speichert die aktuelle Größe des Bildes, die zwischen minImageSize und maxImageSize. imageScale liegt und von currentImageSize abgeleitet wird.
  • Die LazyColumn-Offsets basieren auf dem currentImageSize.
  • Der Image verwendet einen graphicsLayer-Modifikator, um die berechnete Skalierung anzuwenden.
  • Das translationY innerhalb des graphicsLayer sorgt dafür, dass das Bild beim Skalieren vertikal zentriert bleibt.

Ergebnis

Das obige Snippet führt beim Scrollen zu einem Skalierungseffekt für das Bild:

Abbildung 8. Ein Skalierungseffekt für Bilder beim Scrollen.

Interoperabilität für verschachteltes Scrollen

Wenn Sie versuchen, scrollbare View-Elemente in scrollbaren Composables zu verschachteln oder umgekehrt, können Probleme auftreten. Die auffälligsten Probleme treten auf, wenn Sie im untergeordneten Element scrollen und die Start- oder Endgrenzen erreichen und erwarten, dass das übergeordnete Element das Scrollen übernimmt. Dieses erwartete Verhalten tritt jedoch möglicherweise nicht auf oder funktioniert nicht wie erwartet.

Dieses Problem ist auf die Erwartungen zurückzuführen, die in scrollbaren Composables eingebaut sind. Scrollbare Composables haben eine „Standardregel für verschachteltes Scrollen“. Das bedeutet, dass jeder scrollbare Container an der verschachtelten Scrollkette teilnehmen muss, sowohl als übergeordnetes Element über NestedScrollConnection als auch als untergeordnetes Element über NestedScrollDispatcher. Das untergeordnete Element würde dann ein verschachteltes Scrollen für das übergeordnete Element auslösen, wenn das untergeordnete Element an der Grenze ist. Diese Regel ermöglicht beispielsweise, dass Compose Pager und Compose LazyRow gut zusammenarbeiten. Wenn jedoch Interoperabilitäts-Scrolling mit ViewPager2 oder RecyclerView durchgeführt wird, ist das kontinuierliche Scrollen vom untergeordneten zum übergeordneten Element nicht möglich, da diese NestedScrollingParent3 nicht implementieren.

Wenn Sie die Interop API für verschachteltes Scrollen zwischen scrollbaren View-Elementen und scrollbaren Composables aktivieren möchten, die in beide Richtungen verschachtelt sind, können Sie die Interop API für verschachteltes Scrollen verwenden, um diese Probleme in den folgenden Szenarien zu beheben.

Ein kooperierendes übergeordnetes Element View, das ein untergeordnetes Element ComposeView enthält

Ein kooperierendes übergeordnetes Element View ist ein Element, das bereits NestedScrollingParent3 implementiert und daher Scroll-Deltas von einem kooperierenden verschachtelten untergeordneten Compose-Element empfangen kann. ComposeView würde in diesem Fall als untergeordnetes Element fungieren und müsste (indirekt) NestedScrollingChild3 implementieren. Ein Beispiel für ein kooperierendes übergeordnetes Element ist androidx.coordinatorlayout.widget.CoordinatorLayout.

Wenn Sie eine Interoperabilität für verschachteltes Scrollen zwischen scrollbaren übergeordneten View-Containern und verschachtelten scrollbaren untergeordneten Composables benötigen, können Sie rememberNestedScrollInteropConnection() verwenden.

rememberNestedScrollInteropConnection() lässt die NestedScrollConnection zu und merkt sie sich. Dadurch wird die Interoperabilität des verschachtelten Scrollens zwischen einem übergeordneten View-Element, das NestedScrollingParent3 implementiert, und einem untergeordneten Compose-Element ermöglicht. Dies sollte in Verbindung mit einem nestedScroll-Modifikator verwendet werden. Da verschachteltes Scrollen auf der Compose-Seite standardmäßig aktiviert ist, können Sie diese Verbindung verwenden, um verschachteltes Scrollen auf der View-Seite zu aktivieren und die erforderliche Glue-Logik zwischen Views und Composables hinzuzufügen.

Ein häufiger Anwendungsfall ist die Verwendung von CoordinatorLayout, CollapsingToolbarLayout und einer untergeordneten zusammensetzbaren Funktion, wie in diesem Beispiel gezeigt:

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

In Ihrer Aktivität oder Ihrem Fragment müssen Sie das untergeordnete Compose-Element und die erforderliche NestedScrollConnection einrichten:

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

Ein übergeordnetes Compose-Element, das ein untergeordnetes AndroidView enthält

In diesem Szenario wird die Implementierung der Interop API für verschachteltes Scrollen auf der Compose-Seite behandelt, wenn Sie ein übergeordnetes Compose-Element haben, das ein untergeordnetes AndroidView enthält. AndroidView implementiert NestedScrollDispatcher, da es als untergeordnetes Element eines übergeordneten Compose-Scrollelements fungiert, sowie NestedScrollingParent3, da es als übergeordnetes Element eines untergeordneten View-Scrollelements fungiert. Das übergeordnete Compose-Element kann dann verschachtelte Scroll-Deltas von einem verschachtelten scrollbaren untergeordneten Element View empfangen.

Das folgende Beispiel zeigt, wie Sie in diesem Szenario die Interoperabilität für verschachteltes Scrollen zusammen mit einer minimierbaren Symbolleiste in Compose erreichen können:

@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(
            title = {},
            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) {
            // ...
        }
    }
    // ...
}

In diesem Beispiel wird gezeigt, wie Sie die API mit einem scrollable-Modifikator verwenden können:

@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(R.layout.list_item, null)
                    .apply {
                        // Nested scrolling interop is enabled when
                        // nested scroll is enabled for the root View
                        ViewCompat.setNestedScrollingEnabled(this, true)
                    }
            }
        )
    }
}

In diesem Beispiel wird gezeigt, wie die Interop-API für verschachteltes Scrollen mit BottomSheetDialogFragment verwendet wird, um ein erfolgreiches Drag-and-Dismiss-Verhalten zu erzielen:

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

Beachten Sie, dass rememberNestedScrollInteropConnection() ein NestedScrollConnection in dem Element installiert, an das Sie es anhängen. NestedScrollConnection ist für die Übertragung der Deltas von der Compose-Ebene auf die View-Ebene verantwortlich. Dadurch kann das Element am verschachtelten Scrollen teilnehmen, aber das Scrollen von Elementen wird nicht automatisch aktiviert. Bei nicht automatisch scrollbaren Composables wie Box oder Column werden Scroll-Deltas nicht im verschachtelten Scrollsystem weitergegeben und erreichen nicht die von rememberNestedScrollInteropConnection() bereitgestellte NestedScrollConnection. Daher erreichen diese Deltas nicht die übergeordnete View-Komponente. Achten Sie darauf, dass Sie auch für diese Arten von verschachtelten Composables scrollbare Modifikatoren festlegen. Weitere Informationen finden Sie im vorherigen Abschnitt zum verschachtelten Scrollen.

Ein nicht kooperierendes übergeordnetes Element View, das ein untergeordnetes Element ComposeView enthält

Eine nicht kooperierende Ansicht ist eine Ansicht, die die erforderlichen NestedScrolling-Schnittstellen auf der View-Seite nicht implementiert. Das bedeutet, dass die Interoperabilität des verschachtelten Scrollens mit diesen Views nicht standardmäßig funktioniert. Nicht kooperierende Views sind RecyclerView und ViewPager2.

Zusätzliche Ressourcen