Verschachtelte Scroll-Modifikatoren

Beim verschachtelten Scrollen arbeiten mehrere ineinander verschachtelte scrollbare Komponenten zusammen, indem sie auf eine einzelne Scroll-Geste reagieren und ihre Scroll-Deltas (Änderungen) kommunizieren.

Das System für verschachteltes Scrollen ermöglicht die Koordination zwischen scrollbaren und hierarchisch verknüpften Komponenten (meistens durch gemeinsame übergeordnete Elemente). Dieses System verknüpft scrollbare Container 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 komplexeres Beispiel ist eine einklappbare Symbolleiste.

Automatisches verschachteltes Scrollen

Für einfaches verschachteltes Scrollen ist keine Aktion erforderlich. Gesten, die eine Scroll-Aktion auslösen, 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 Komponenten und Modifikatoren von Compose unterstützt und ist standardmäßig aktiviert: verticalScroll, horizontalScroll, scrollable, Lazy APIs und TextField. Wenn der Nutzer also ein untergeordnetes Element verschachtelter Komponenten scrollt, geben die vorherigen Modifikatoren die Scroll-Deltas an die übergeordneten Elemente weiter, die verschachteltes Scrollen unterstützen.

Im folgenden Beispiel sind Elemente mit einem verticalScroll -Modifikator in einem Container zu sehen, 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 vertikale Scrolling-UI-Elemente, die auf Touch-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 einen erweiterten koordinierten Scroll zwischen mehreren Elementen erstellen müssen, der nestedScroll Modifikator bietet mehr Flexibilität, da er eine Hierarchie für verschachteltes Scrollen definiert. Wie im vorherigen Abschnitt erwähnt, unterstützen einige Komponenten verschachteltes Scrollen. Bei Composables, die nicht automatisch scrollbar sind, z. B. Box oder Column, werden Scroll-Deltas für solche Komponenten jedoch nicht im System für verschachteltes Scrollen weitergegeben und erreichen weder NestedScrollConnection noch die übergeordnete Komponente. Um dieses Problem zu beheben, können Sie nestedScroll verwenden, um diese Unterstützung auf andere Komponenten zu übertragen, einschließlich benutzerdefinierter Komponenten.

Zyklus für verschachteltes Scrollen

Der Zyklus für verschachteltes Scrollen ist der Fluss von Scroll-Deltas, die über alle Komponenten (oder Knoten) hinweg, die Teil des Systems für verschachteltes Scrollen sind, im Hierarchiebaum nach oben und unten weitergegeben werden. Dazu können beispielsweise scrollbare Komponenten und Modifikatoren oder nestedScroll verwendet werden.

Phasen des Zyklus für verschachteltes Scrollen

Wenn ein Triggerereignis (z. B. eine Geste) von einer scrollbaren Komponente erkannt wird, werden die generierten Deltas an das System für verschachteltes Scrollen gesendet und durchlaufen drei Phasen: Pre-Scroll, Knotennutzung und Post-Scroll.

Phasen des verschachtelten Scrollzyklus
Abbildung 2. Phasen des Zyklus für verschachteltes Scrollen.

In der ersten Phase, der Pre-Scroll-Phase, gibt die Komponente, die die Deltas des Triggerereignisses empfangen hat, diese Ereignisse im Hierarchiebaum nach oben an das oberste übergeordnete Element weiter. Die Delta-Ereignisse werden dann nach unten weitergegeben, d. h. Deltas werden vom obersten übergeordneten Element nach unten zum untergeordneten Element weitergegeben, das den Zyklus für verschachteltes Scrollen gestartet hat.

Phase vor dem Scrollen – Dispatching nach oben
Abbildung 3. Pre-Scroll-Phase: Weitergabe nach oben.

So können die übergeordneten Elemente für verschachteltes Scrollen (Composables mit nestedScroll oder scrollbaren Modifikatoren) etwas mit dem Delta tun, bevor es vom Knoten selbst genutzt werden kann.

Phase vor dem Scrollen – Bubbling Down
Abbildung 4. Pre-Scroll-Phase: Weitergabe nach unten.

In der Phase der Knotennutzung verwendet der Knoten selbst alle Deltas, die nicht von den übergeordneten Elementen verwendet wurden. Hier wird die Scrollbewegung tatsächlich ausgeführt und ist sichtbar.

Phase der Knotennutzung
Abbildung 5. Phase der Knotennutzung.

In dieser Phase kann das untergeordnete Element den gesamten oder einen Teil des verbleibenden Scrolls nutzen. Alles, was übrig bleibt, wird wieder nach oben gesendet, um die Post-Scroll-Phase zu durchlaufen.

In der Post-Scroll-Phase wird alles, was der Knoten selbst nicht genutzt hat, wieder an seine Ancestors zur Nutzung weitergegeben.

Phase nach dem Scrollen – Dispatching
wird ausgeführt
Abbildung 6. Post-Scroll-Phase: Weitergabe nach oben.

Die Post-Scroll-Phase funktioniert ähnlich wie die Pre-Scroll-Phase. Alle übergeordneten Elemente können wählen, ob sie das Delta nutzen oder nicht.

Phase nach dem Scrollen – Bubbling Down
Abbildung 7. Post-Scroll-Phase: Weitergabe nach unten.

Ähnlich wie beim Scrollen kann die Absicht des Nutzers nach Abschluss einer Drag-Geste in eine Geschwindigkeit umgewandelt werden, mit der der scrollbare Container mit einer Animation gescrollt wird. Das Fling ist auch Teil des Zyklus für verschachteltes Scrollen. Die durch das Drag-Ereignis generierten Geschwindigkeiten durchlaufen ähnliche Phasen: Pre-Fling, Knotennutzung und Post-Fling. Die Fling-Animation ist nur mit der Touch-Geste verknüpft und wird nicht durch andere Ereignisse wie A11y oder Hardware-Scroll ausgelöst.

Am Zyklus für verschachteltes Scrollen teilnehmen

Die Teilnahme am Zyklus bedeutet, die Nutzung von Deltas entlang der Hierarchie abzufangen, zu nutzen und zu melden. Compose bietet eine Reihe von Tools, mit denen Sie die Funktionsweise des Systems für verschachteltes Scrollen beeinflussen und 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 Zyklus für verschachteltes Scrollen ein System ist, das auf eine Kette von Knoten wirkt, ist der nestedScroll Modifikator 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 Instanzen des Modifikators für verschachteltes Scrollen im Baum, sodass Informationen über diesen Kanal geteilt werden können. Die Bausteine dieses Modifikators sind NestedScrollConnection und NestedScrollDispatcher.

NestedScrollConnection bietet eine Möglichkeit, auf die Phasen des Zyklus für verschachteltes Scrollen zu reagieren und das System für verschachteltes Scrollen zu beeinflussen. Es besteht aus vier Callback-Methoden, die jeweils eine der Nutzungsphasen darstellen: Pre-Scroll, Post-Scroll, Pre-Fling und Post-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 enthält auch Informationen zum weitergegebenen Delta: das available-Delta für diese bestimmte Phase und das consumed-Delta, das in den vorherigen Phasen genutzt wurde. Wenn Sie die Weitergabe von Deltas in der Hierarchie nach oben beenden möchten, können Sie dazu die Verbindung für verschachteltes Scrollen 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 NestedScrollSource Typ.

NestedScrollDispatcher initialisiert den Zyklus für verschachteltes Scrollen. 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 wurden, an das System sendet. Aus diesem Grund wird in den meisten Anwendungsfällen zum Anpassen 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.

Bild beim Scrollen verkleinern

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

Bildgröße basierend auf der Scrollposition ändern

In diesem Snippet wird gezeigt, wie Sie die Größe eines Bildes in einer LazyColumn basierend auf vertikalen Scrollposition ändern. Das Bild wird kleiner, wenn der Nutzer nach unten scrollt, und größer, wenn er nach oben scrollt. Dabei bleibt es 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 Informationen zum Code

  • In diesem Code wird eine 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 der currentImageSize abgeleitet wird.
  • Die LazyColumn werden basierend auf der currentImageSize verschoben.
  • Für das Image wird ein graphicsLayer-Modifikator verwendet, um die berechnete Skalierung anzuwenden.
  • Mit translationY in graphicsLayer wird dafür gesorgt, dass das Bild beim Skalieren vertikal zentriert bleibt.

Ergebnis

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

Abbildung 8. Ein Skalierungseffekt für das Bild 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 das untergeordnete 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 oder nicht wie erwartet auf.

Dieses Problem ist auf die Erwartungen zurückzuführen, die in scrollbaren Composables enthalten sind. Für scrollbare Composables gilt die Regel „Verschachteltes Scrollen standardmäßig“, d. h. jeder scrollbare Container muss am Zyklus für verschachteltes Scrollen teilnehmen, sowohl als übergeordnetes Element über NestedScrollConnection, als auch als untergeordnetes Element über NestedScrollDispatcher. Das untergeordnete Element würde dann einen verschachtelten Scroll für das übergeordnete Element auslösen, wenn es die Grenze erreicht. Mit dieser Regel können beispielsweise Pager und LazyRow von Compose gut zusammenarbeiten. Wenn jedoch Interoperabilitäts-Scrollen erfolgt mit ViewPager2 oder RecyclerView, ist das kontinuierliche Scrollen vom untergeordneten zum übergeordneten Element nicht möglich, da diese NestedScrollingParent3 nicht implementieren.

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

Ein kooperierendes übergeordnetes View-Element mit einem untergeordneten ComposeView-Element

Ein kooperierendes übergeordnetes View implementiert bereits NestedScrollingParent3 und kann daher Scroll-Deltas von einem kooperierenden verschachtelten untergeordneten Composable empfangen. ComposeView fungiert in diesem Fall als untergeordnetes Element und muss NestedScrollingChild3 (indirekt) implementieren. Ein Beispiel für ein kooperierendes übergeordnetes Element ist androidx.coordinatorlayout.widget.CoordinatorLayout.

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

rememberNestedScrollInteropConnection() ermöglicht und speichert die NestedScrollConnection , die die Interoperabilität für verschachteltes Scrollen zwischen einem übergeordneten View-Element, das implementiert, NestedScrollingParent3 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 einem untergeordneten Composable, 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 Composable 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 Composable mit einem untergeordneten AndroidView-Element

Dieses Szenario behandelt die Implementierung der Interoperabilitäts-API für verschachteltes Scrollen auf der Compose-Seite, wenn Sie ein übergeordnetes Composable mit einem untergeordneten AndroidView-Element haben. Das AndroidView implementiert NestedScrollDispatcher, da es als untergeordnetes Element eines scrollbaren übergeordneten Compose-Elements fungiert, sowie NestedScrollingParent3 , da es als übergeordnetes Element eines scrollbaren untergeordneten View-Elements fungiert. Das übergeordnete Compose-Element kann dann verschachtelte Scroll-Deltas von einem verschachtelten scrollbaren untergeordneten View-Element empfangen.

Im folgenden Beispiel wird gezeigt, wie Sie in diesem Szenario die Interoperabilität für verschachteltes Scrollen zusammen mit einer einklappbaren Symbolleiste von 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(
            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(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)
                    }
            }
        )
    }
}

Und schließlich zeigt dieses Beispiel, wie die Interoperabilitäts-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() eine NestedScrollConnection in dem Element installiert, an das Sie sie anhängen. NestedScrollConnection ist für die Übertragung der Deltas von der Compose-Ebene zur View-Ebene verantwortlich. So kann das Element am verschachtelten Scrollen teilnehmen, aber das Scrollen von Elementen wird nicht automatisch aktiviert. Bei Composables, die nicht automatisch scrollbar sind, z. B. Box oder Column, werden Scroll-Deltas für solche Komponenten nicht im System für verschachteltes Scrollen weitergegeben und erreichen nicht die NestedScrollConnection, die von rememberNestedScrollInteropConnection() bereitgestellt wird. Daher erreichen diese Deltas nicht die übergeordnete View-Komponente. Um dieses Problem zu beheben, müssen Sie auch scrollbare Modifikatoren für diese Arten von verschachtelten Composables festlegen. Weitere Informationen finden Sie im vorherigen Abschnitt zum verschachtelten Scrollen für weitere Informationen.

Ein nicht kooperierendes übergeordnetes View-Element mit einem untergeordneten ComposeView-Element

Ein nicht kooperierendes View-Element implementiert die erforderlichen NestedScrolling-Schnittstellen nicht auf der View-Seite. Das bedeutet, dass die Interoperabilität für verschachteltes Scrollen mit diesen Views nicht standardmäßig funktioniert. Nicht kooperierende Views sind RecyclerView und ViewPager2.

Zusätzliche Ressourcen