Cuộn

Đối tượng sửa đổi cho thao tác cuộn

Đối tượng sửa đổi verticalScrollhorizontalScroll cung cấp cách đơn giản nhất để người dùng cuộn một phần tử khi nội dung của phần tử đó lớn hơn giới hạn kích thước tối đa. Với phương thức sửa đổi verticalScrollhorizontalScroll, bạn không cần dịch hoặc bù trừ phần nội dung.

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

Một danh sách dọc đơn giản phản hồi các cử chỉ cuộn

ScrollState cho phép bạn thay đổi vị trí cuộn hoặc xem trạng thái hiện tại. Để tạo lớp này với các tham số mặc định, hãy sử dụng 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))
        }
    }
}

Đối tượng sửa đổi cho các mục cuộn được (scrollable)

Đối tượng sửa đổi scrollable khác với đối tượng sửa đổi cuộn ở chỗ scrollable phát hiện cử chỉ cuộn và nắm bắt các delta, nhưng không tự động bù trừ nội dung của nó. Thay vào đó, tính năng này được uỷ quyền cho người dùng thông qua ScrollableState. Đây là điều kiện bắt buộc để đối tượng sửa đổi này hoạt động chính xác.

Khi tạo ScrollableState, bạn phải cung cấp một hàm consumeScrollDelta sẽ được gọi trong mỗi bước cuộn (bằng phương thức nhập bằng cử chỉ, cuộn hoặc hất mượt mà) với delta tính bằng pixel. Hàm này phải trả về khoảng cách cuộn đã sử dụng để đảm bảo sự kiện được truyền đúng cách trong trường hợp có các phần tử lồng nhau có đối tượng sửa đổi scrollable.

Đoạn mã sau đây phát hiện các cử chỉ và cho thấy một giá trị dạng số cho một phần bù trừ, nhưng không bù trừ phần tử nào:

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

Một phần tử trên giao diện người dùng phát hiện thao tác nhấn ngón tay và hiển thị giá trị số cho vị trí của ngón tay

Cuộn dạng lồng

Cuộn lồng nhau là một hệ thống trong đó nhiều thành phần cuộn nằm trong nhau hoạt động cùng nhau bằng cách phản ứng với một cử chỉ cuộn duy nhất và truyền đạt các delta cuộn (thay đổi).

Hệ thống cuộn lồng nhau cho phép phối hợp giữa các thành phần có thể cuộn và liên kết theo phân cấp (thường là bằng cách chia sẻ cùng một thành phần mẹ). Hệ thống này liên kết các vùng chứa cuộn và cho phép tương tác với các delta cuộn đang được truyền và chia sẻ.

Compose cung cấp nhiều cách xử lý thao tác cuộn lồng giữa các thành phần kết hợp. Một ví dụ điển hình về tính năng cuộn lồng nhau là một danh sách bên trong một danh sách khác, và một trường hợp phức tạp hơn là thanh công cụ có thể thu gọn.

Tự động lồng tính năng cuộn trong lớp khác

Bạn không cần làm gì khi chỉ đơn giản lồng tính năng cuộn trong lớp khác. Những cử chỉ khởi tạo thao tác cuộn sẽ tự động được truyền tải từ phần tử con đến phần tử mẹ, chẳng hạn như khi phần tử con không thể cuộn thêm nữa thì cử chỉ sẽ do phần tử mẹ xử lý.

Một số thành phần và đối tượng sửa đổi của Compose hỗ trợ và cung cấp tính năng cuộn lồng tự động: verticalScroll, horizontalScroll, scrollable, Lazy API và TextField. Điều này có nghĩa là khi người dùng cuộn một thành phần con bên trong của các thành phần lồng nhau, các đối tượng sửa đổi trước đó sẽ truyền các delta cuộn đến các thành phần mẹ có hỗ trợ tính năng cuộn lồng nhau.

Ví dụ sau đây cho thấy các phần tử có đối tượng sửa đổi verticalScroll được áp dụng bên trong một vùng chứa cũng có đối tượng sửa đổi 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)
                    )
                }
            }
        }
    }
}

Hai phần tử trên giao diện người dùng cuộn theo chiều dọc lồng nhau, phản hồi các cử chỉ bên trong và
bên ngoài phần tử
bên trong

Sử dụng đối tượng sửa đổi nestedScroll

Nếu bạn cần tạo một thao tác cuộn phối hợp nâng cao giữa nhiều phần tử, đối tượng sửa đổi nestedScroll sẽ giúp bạn tăng tính linh hoạt bằng cách xác định một hệ thống phân cấp cuộn khi lồng ghép vào nhau. Như đã đề cập trong phần trước, một số thành phần đã tích hợp sẵn tính năng hỗ trợ cuộn lồng. Tuy nhiên, đối với các thành phần kết hợp không thể tự động cuộn, chẳng hạn như Box hoặc Column, các delta cuộn trên các thành phần như vậy sẽ không lan truyền trong hệ thống cuộn lồng nhau và delta sẽ không đạt đến NestedScrollConnection hay thành phần mẹ. Để giải quyết vấn đề này, bạn có thể sử dụng nestedScroll để trao sự hỗ trợ như vậy cho các thành phần khác, bao gồm cả các thành phần tuỳ chỉnh.

Chu kỳ cuộn lồng

Chu kỳ cuộn lồng nhau là luồng các delta cuộn được điều phối lên và xuống cây phân cấp thông qua tất cả các thành phần (hoặc nút) thuộc hệ thống cuộn lồng nhau, chẳng hạn như bằng cách sử dụng các thành phần và đối tượng sửa đổi có thể cuộn, hoặc nestedScroll.

Các giai đoạn của chu kỳ cuộn lồng nhau

Khi một sự kiện kích hoạt (ví dụ: cử chỉ) được phát hiện bằng một thành phần có thể cuộn, trước khi hành động cuộn thực tế được kích hoạt, các delta đã tạo sẽ được gửi đến hệ thống cuộn lồng nhau và trải qua 3 giai đoạn: cuộn trước, tiêu thụ nút và cuộn sau.

Các giai đoạn trong chu kỳ cuộn lồng nhau

Trong giai đoạn đầu tiên là cuộn trước, thành phần đã nhận được các delta sự kiện kích hoạt sẽ điều phối các sự kiện đó lên thông qua cây phân cấp, đến thành phần mẹ trên cùng. Sau đó, các sự kiện delta sẽ xuất hiện dạng bong bóng trò chuyện, có nghĩa là delta sẽ được truyền từ phần tử mẹ ở gốc cao nhất xuống phần tử con bắt đầu chu kỳ cuộn lồng nhau.

Giai đoạn cuộn trước – điều phối

Điều này mang lại cho các thành phần mẹ cuộn lồng (thành phần kết hợp sử dụng nestedScroll hoặc đối tượng sửa đổi có thể cuộn) cơ hội để thực hiện một số thao tác với delta trước khi chính nút đó có thể sử dụng nút này.

Giai đoạn cuộn trước – từ tiếng nổ xuống

Trong giai đoạn sử dụng nút, chính nút đó sẽ sử dụng bất kỳ delta nào không được phần tử mẹ sử dụng. Đây là khi chuyển động cuộn thực sự hoàn tất và hiển thị.

Giai đoạn sử dụng nút

Trong giai đoạn này, trẻ có thể chọn tiêu thụ toàn bộ hoặc một phần của thao tác cuộn còn lại. Mọi phần còn lại sẽ được gửi lại để trải qua giai đoạn sau khi cuộn.

Cuối cùng, trong giai đoạn cuộn sau, mọi nội dung mà chính nút đó không sử dụng sẽ được gửi lại cho đối tượng cấp trên để sử dụng.

Giai đoạn sau khi cuộn – điều phối

Giai đoạn sau cuộn hoạt động theo cách tương tự như giai đoạn cuộn trước, trong đó bất kỳ thành phần mẹ nào cũng có thể chọn sử dụng hoặc không sử dụng.

Giai đoạn sau khi cuộn - xả hơi
xuống

Tương tự như cuộn, khi cử chỉ kéo kết thúc, ý định của người dùng có thể được chuyển thành tốc độ dùng để hất (cuộn bằng ảnh động) vùng chứa có thể cuộn. Thao tác hất cũng là một phần của chu kỳ cuộn lồng nhau và vận tốc do sự kiện kéo tạo ra cũng trải qua các giai đoạn tương tự: hất trước, sử dụng nút và sau hất. Lưu ý rằng ảnh động hất chỉ liên kết với cử chỉ chạm và sẽ không được kích hoạt bằng các sự kiện khác, chẳng hạn như a11y hoặc cuộn phần cứng.

Tham gia vào chu kỳ cuộn lồng ghép

Tham gia vào chu trình có nghĩa là chặn, sử dụng và báo cáo việc tiêu thụ delta cùng hệ phân cấp. Compose cung cấp một bộ công cụ để ảnh hưởng đến cách hoạt động của hệ thống cuộn lồng nhau và cách tương tác trực tiếp với hệ thống đó, chẳng hạn như khi bạn cần thực hiện tác vụ với delta cuộn trước khi một thành phần có thể cuộn thậm chí bắt đầu cuộn.

Nếu chu kỳ cuộn lồng nhau là một hệ thống hoạt động trên một chuỗi nút, thì đối tượng sửa đổi nestedScroll là cách chặn và chèn vào các thay đổi này, đồng thời ảnh hưởng đến dữ liệu (delta cuộn) được truyền trong chuỗi. Đối tượng sửa đổi này có thể được đặt ở bất kỳ đâu trong hệ phân cấp và giao tiếp với các thực thể sửa đổi cuộn lồng trên cây để có thể chia sẻ thông tin thông qua kênh này. Các thành phần của đối tượng sửa đổi này là NestedScrollConnectionNestedScrollDispatcher.

NestedScrollConnection cung cấp cách phản hồi các giai đoạn trong chu kỳ cuộn lồng nhau và tác động đến hệ thống cuộn lồng nhau. Bộ chuyển đổi này bao gồm 4 phương thức gọi lại, mỗi phương thức đại diện cho một trong các giai đoạn tiêu thụ: trước/sau khi cuộn và trước/sau hất:

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

Mỗi lệnh gọi lại cũng cung cấp thông tin về delta đang được truyền: available delta cho giai đoạn cụ thể đó và consumed delta được tiêu thụ trong các giai đoạn trước. Nếu tại một thời điểm bất kỳ muốn dừng việc lan truyền delta lên hệ phân cấp, bạn có thể sử dụng kết nối cuộn lồng nhau để thực hiện việc này:

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

Tất cả các lệnh gọi lại đều cung cấp thông tin về loại NestedScrollSource.

NestedScrollDispatcher khởi động chu kỳ cuộn lồng nhau. Việc sử dụng trình điều phối và gọi các phương thức của trình điều phối đó sẽ kích hoạt chu kỳ. Các vùng chứa có thể cuộn được tích hợp sẵn một trình điều phối sẽ gửi các delta được thu thập trong quá trình thực hiện cử chỉ vào hệ thống. Vì lý do này, hầu hết các trường hợp sử dụng tuỳ chỉnh tính năng cuộn lồng nhau đều liên quan đến việc sử dụng NestedScrollConnection thay vì trình điều phối, để phản ứng với các delta hiện có thay vì gửi các delta mới. Hãy xem NestedScrollDispatcherSample để biết thêm cách sử dụng.

Khả năng tương tác cuộn dạng lồng

Khi cố gắng lồng các phần tử View có thể cuộn trong các thành phần kết hợp có thể cuộn hoặc ngược lại, bạn có thể gặp vấn đề. Các lỗi đáng chú ý nhất sẽ xảy ra khi bạn cuộn thành phần con và đạt đến giới hạn bắt đầu hoặc kết thúc, đồng thời mong đợi thành phần mẹ sẽ di chuyển qua. Tuy nhiên, hành vi dự kiến này có thể không xảy ra hoặc có thể không hoạt động như mong đợi.

Vấn đề này là do việc kỳ vọng các thành phần kết hợp có thể cuộn được mà ra. Các thành phần kết hợp có thể cuộn đều có quy tắc "nested-scroll-by-default", nghĩa là bất kỳ vùng chứa nào có thể cuộn cũng đều phải tham gia vào chuỗi cuộn lồng, cả ở dạng thành phần mẹ thông qua NestedScrollConnection và thành phần con thông qua NestedScrollDispatcher. Sau đó thành phần con sẽ đẩy hoạt động cuộn lồng cho cha mẹ khi nó tiếp cận ranh giới. Ví dụ: quy tắc này cho phép Compose Pager và Compose LazyRow hoạt động hiệu quả cùng nhau. Tuy nhiên, khi thực hiện thao tác cuộn tương tác với ViewPager2 hoặc RecyclerView, vì các thao tác này không triển khai được NestedScrollingParent3 nên không thể cuộn liên tục từ thành phần con sang thành phần mẹ.

Để bật API tương tác cuộn lồng giữa các phần tử View có thể cuộn và thành phần kết hợp có thể cuộn đồng thời lồng theo cả hai hướng, bạn có thể sử dụng API tương tác có thể cuộn lồng để giảm thiểu những vấn đề này trong các trường hợp sau.

Một tài khoản cha mẹ hợp tác View có con ComposeView

Thành phần mẹ hợp tác View là một thành phần đã triển khai NestedScrollingParent3 và do đó có thể nhận các delta cuộn được từ một thành phần kết hợp con hợp tác lồng ghép. ComposeView sẽ đóng vai trò là phần tử con trong trường hợp này và cần (gián tiếp) triển khai NestedScrollingChild3. Một ví dụ về vai trò cha mẹ hợp tác là androidx.coordinatorlayout.widget.CoordinatorLayout.

Nếu cần khả năng tương tác cuộn lồng giữa vùng chứa thành phần mẹ View cuộn được và các thành phần kết hợp con có thể cuộn lồng, bạn có thể sử dụng rememberNestedScrollInteropConnection().

rememberNestedScrollInteropConnection() nhận và ghi nhớ NestedScrollConnection, cho phép bật khả năng tương tác cuộn lồng giữa một View mẹ có thể triển khai NestedScrollingParent3 và một Compose con. Bạn nên dùng thuộc tính này cùng với công cụ sửa đổi nestedScroll. Vì tính năng cuộn lồng được bật theo mặc định ở phía Compose, nên bạn có thể sử dụng kết nối này để cho phép cả tính năng cuộn lồng ở phía View và thêm logic kết dính cần thiết giữa Views và các thành phần kết hợp.

Một trường hợp sử dụng thường xuyên là sử dụng CoordinatorLayout, CollapsingToolbarLayout và một thành phần kết hợp con, như trong ví dụ sau:

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

Trong Hoạt động hoặc Mảnh, bạn cần thiết lập thành phần kết hợp con và NestedScrollConnection bắt buộc:

open class MainActivity : ComponentActivity() {
    @OptIn(ExperimentalComposeUiApi::class)
    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())
                        }
                    }
                }
            }
        }
    }
}

Một thành phần kết hợp mẹ chứa AndroidView con

Tình huống này bao gồm việc triển khai API tương tác cuộn lồng ở phía Compose – khi bạn có một thành phần kết hợp mẹ chứa AndroidView con. AndroidView triển khai NestedScrollDispatcher, vì nó hoạt động như một phần tử con đối với thành phần mẹ đang cuộn trong Compose, cũng như NestedScrollingParent3 vì nó hoạt động như một thành phần mẹ của View con đang cuộn. Sau đó, thành phần mẹ trong Compose sẽ có thể nhận được các delta cuộn lồng từ một View con có thể cuộn lồng.

Ví dụ sau đây cho thấy cách bạn có thể đạt được khả năng tương tác cuộn lồng trong trường hợp này, cùng với thanh công cụ thu gọn 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) {
            // ...
        }
    }
    // ...
}

Ví dụ này cho thấy cách bạn có thể sử dụng API với công cụ sửa đổi 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)
                    }
            }
        )
    }
}

Sau cùng, ví dụ này cho thấy cách API tương tác cuộn lồng được sử dụng với BottomSheetDialogFragment để đạt được hành vi kéo và loại bỏ thành công:

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

Xin lưu ý rằng rememberNestedScrollInteropConnection() sẽ cài đặt NestedScrollConnection trong phần tử mà bạn đính kèm. NestedScrollConnection chịu trách nhiệm truyền các delta từ cấp Compose sang cấp View. Điều này cho phép phần tử tham gia vào quá trình cuộn lồng nhau, nhưng không tự động bật tính năng cuộn các phần tử. Đối với các thành phần kết hợp không tự động cuộn được, chẳng hạn như Box hoặc Column, delta cuộn trên các thành phần như vậy sẽ không lan truyền trong hệ thống cuộn lồng nhau và delta sẽ không đạt đến NestedScrollConnection do rememberNestedScrollInteropConnection() cung cấp. Do đó, delta đó sẽ không tiếp cận thành phần View mẹ. Để giải quyết vấn đề này, hãy đảm bảo bạn cũng đặt đối tượng sửa đổi có thể cuộn cho các loại thành phần kết hợp lồng nhau này. Bạn có thể tham khảo phần trước về tính năng Cuộn lồng để biết thêm thông tin chi tiết.

Cha mẹ không hợp tác View có con ComposeView

Chế độ xem không hợp tác là chế độ không triển khai các giao diệnNestedScrolling cần thiết ở phía View. Vui lòng lưu ý điều này có nghĩa là khả năng tương tác cuộn lồng với các Views này không hoạt động hiệu quả. Views không hợp tác là RecyclerViewViewPager2.