From 84ca43d0b8d97ea7a07da49af2218d486a4732eb Mon Sep 17 00:00:00 2001 From: Katherine Kuan <843428+kkuan2011@users.noreply.github.com> Date: Sat, 22 Aug 2026 06:06:59 +0000 Subject: [PATCH 1/7] Add code snippets for Compose state, image optimization, card, scrollable grid, and nested scroll modifiers --- .../compose/snippets/components/Card.kt | 9 + .../images/ImageOptimizationSnippets.kt | 38 ++++ .../snippets/lists/LazyListSnippets.kt | 13 ++ .../touchinput/gestures/GesturesSnippets.kt | 165 ++++++++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 compose/snippets/src/main/java/com/example/compose/snippets/images/ImageOptimizationSnippets.kt diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/components/Card.kt b/compose/snippets/src/main/java/com/example/compose/snippets/components/Card.kt index 1b755819b..495f8f078 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/components/Card.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/components/Card.kt @@ -80,6 +80,15 @@ fun CustomCardExample(event: () -> Unit) { } // [END android_compose_components_customcard] +// [START android_compose_components_card_minimal] +@Composable +fun CardMinimalExample() { + Card() { + Text(text = "Hello, world!") + } +} +// [END android_compose_components_card_minimal] + @Preview // [START android_compose_components_elevatedcard] @Composable diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/images/ImageOptimizationSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/images/ImageOptimizationSnippets.kt new file mode 100644 index 000000000..e612cdb4e --- /dev/null +++ b/compose/snippets/src/main/java/com/example/compose/snippets/images/ImageOptimizationSnippets.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.compose.snippets.images + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.painter.Painter + +// [START android_compose_graphics_images_optimization_url] +// Prefer this: +@Composable +fun MyImage(url: String) { + +} +// [END android_compose_graphics_images_optimization_url] + +private object PainterParameterSnippet { + // [START android_compose_graphics_images_optimization_painter] + // Over this: + @Composable + fun MyImage(painter: Painter) { + + } + // [END android_compose_graphics_images_optimization_painter] +} diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt index 7f221bb56..7a3be4483 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt @@ -854,3 +854,16 @@ fun ScrollingGrid() { } } // [END android_compose_layout_scrollable_grid] + +@Composable +private fun LazyVerticalGridAdaptiveSample() { + // [START android_compose_layout_scrollable_grid_adaptive] + LazyVerticalGrid( + columns = GridCells.Adaptive(minSize = 20.dp) + // [START_EXCLUDE silent] + , content = {} + // [END_EXCLUDE] + ) + // [END android_compose_layout_scrollable_grid_adaptive] +} + diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt index 0e3d687e9..15b146f03 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt @@ -19,6 +19,9 @@ package com.example.compose.snippets.touchinput.gestures import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import androidx.activity.ComponentActivity import androidx.compose.foundation.MutatePriority import androidx.compose.foundation.background @@ -87,6 +90,13 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.util.fastCoerceIn import androidx.compose.ui.util.fastRoundToInt +import androidx.compose.ui.viewinterop.AndroidView +import androidx.core.view.ViewCompat +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.Adapter +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.google.android.material.bottomsheet.BottomSheetDialogFragment import kotlin.math.abs import kotlin.math.roundToInt @@ -423,13 +433,168 @@ private object NestedScrollInterop { } // [END android_compose_touchinput_gestures_nested_scroll_interop_activity] + private val ToolbarHeight = 56.dp + + @Composable + private fun TopAppBar(modifier: Modifier = Modifier) {} + + // [START android_compose_touchinput_gestures_nested_scroll_interop_compose_parent_android_child] + @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(R.id.main_list)) { + layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) + adapter = NestedScrollInteropAdapter() + } + }.also { + // Nested scrolling interop is enabled when + // nested scroll is enabled for the root View + ViewCompat.setNestedScrollingEnabled(it, true) + } + }, + // ... + // [START_EXCLUDE silent] + modifier = Modifier.fillMaxSize() + // [END_EXCLUDE] + ) + } + } + + private class NestedScrollInteropAdapter : + Adapter() { + 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) { + // ... + } + } + // ... + // [START_EXCLUDE silent] + override fun getItemCount(): Int = items.size + // [END_EXCLUDE] + } + // [END android_compose_touchinput_gestures_nested_scroll_interop_compose_parent_android_child] + + // [START android_compose_touchinput_gestures_nested_scroll_interop_scrollable_view_in_compose] + @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) + } + } + ) + } + } + // [END android_compose_touchinput_gestures_nested_scroll_interop_scrollable_view_in_compose] + + // [START android_compose_touchinput_gestures_nested_scroll_interop_bottom_sheet_fragment] + 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(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 + } + } + } + // [END android_compose_touchinput_gestures_nested_scroll_interop_bottom_sheet_fragment] + object R { object id { val compose_view = 1 + val main_list = 2 } object layout { val activity_main = 0 + val view_in_compose_nested_scroll_interop = 1 + val list_item = 2 + val fragment_bottom_sheet = 3 } } } From f525f27bcbd971c345c9b49a8fd6f228ed990364 Mon Sep 17 00:00:00 2001 From: Ed Williams Date: Mon, 14 Sep 2026 19:30:46 -0400 Subject: [PATCH 2/7] Review fixes for the Compose state and gestures extraction - The adaptive grid snippet published GridCells.Adaptive(minSize = 20.dp) with an empty content lambda behind an exclude. The page prints 128.dp and a real items body, and its prose names 128.dp explicitly, so both are restored and the exclude is no longer needed. - LinearLayoutManager took RecyclerView.VERTICAL where the page prints a bare VERTICAL. Imported the constant so the page's form compiles. --- .../compose/snippets/lists/LazyListSnippets.kt | 11 ++++++----- .../snippets/touchinput/gestures/GesturesSnippets.kt | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt index 7a3be4483..fe8b0a10e 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt @@ -859,11 +859,12 @@ fun ScrollingGrid() { private fun LazyVerticalGridAdaptiveSample() { // [START android_compose_layout_scrollable_grid_adaptive] LazyVerticalGrid( - columns = GridCells.Adaptive(minSize = 20.dp) - // [START_EXCLUDE silent] - , content = {} - // [END_EXCLUDE] - ) + columns = GridCells.Adaptive(minSize = 128.dp) + ) { + items(photos) { photo -> + PhotoItem(photo) + } + } // [END android_compose_layout_scrollable_grid_adaptive] } diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt index 15b146f03..8920bd47f 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt @@ -95,6 +95,7 @@ import androidx.core.view.ViewCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.Adapter +import androidx.recyclerview.widget.RecyclerView.VERTICAL import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.google.android.material.bottomsheet.BottomSheetDialogFragment import kotlin.math.abs @@ -475,7 +476,7 @@ private object NestedScrollInterop { LayoutInflater.from(context) .inflate(R.layout.view_in_compose_nested_scroll_interop, null).apply { with(findViewById(R.id.main_list)) { - layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) + layoutManager = LinearLayoutManager(context, VERTICAL, false) adapter = NestedScrollInteropAdapter() } }.also { From dc62dd2994bf2348ad803660b229813195bcaed0 Mon Sep 17 00:00:00 2001 From: Ed Williams Date: Wed, 16 Sep 2026 15:10:41 -0400 Subject: [PATCH 3/7] Drop the minimal card snippet, it belongs to the card page work android_compose_components_card_minimal publishes to develop/ui/compose/components/card, which Ramon is migrating in StellarElements/android-dac-snippets#25 along with time-pickers. Two pull requests adding the same tag to the same file would collide, so this branch leaves the card page alone. --- .../java/com/example/compose/snippets/components/Card.kt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/components/Card.kt b/compose/snippets/src/main/java/com/example/compose/snippets/components/Card.kt index 495f8f078..1b755819b 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/components/Card.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/components/Card.kt @@ -80,15 +80,6 @@ fun CustomCardExample(event: () -> Unit) { } // [END android_compose_components_customcard] -// [START android_compose_components_card_minimal] -@Composable -fun CardMinimalExample() { - Card() { - Text(text = "Hello, world!") - } -} -// [END android_compose_components_card_minimal] - @Preview // [START android_compose_components_elevatedcard] @Composable From 96d5bac86df787f6d5357b9e802d585052c1c6e0 Mon Sep 17 00:00:00 2001 From: Ed Williams Date: Wed, 16 Sep 2026 21:14:20 -0400 Subject: [PATCH 4/7] Back the interop snippets with real layout resources Ivan's review: GesturesSnippets.kt carried a hand-written object R where layout ids were plain integers. It compiled and stayed outside the region tags, so no page published it, but any reader running the snippet would have inflated nothing. Adds the four layouts the file actually needs and removes the stand-in, so R resolves to the generated one. activity_main also declares emoji_text_view, which EmojiSnippets.kt inflates from the same layout name; lint could not check that reference until a real layout existed, and flags it as MissingInflatedId once one does. --- .../touchinput/gestures/GesturesSnippets.kt | 15 +-------- .../src/main/res/layout/activity_main.xml | 31 +++++++++++++++++++ .../main/res/layout/fragment_bottom_sheet.xml | 26 ++++++++++++++++ .../src/main/res/layout/list_item.xml | 20 ++++++++++++ .../view_in_compose_nested_scroll_interop.xml | 21 +++++++++++++ 5 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 compose/snippets/src/main/res/layout/activity_main.xml create mode 100644 compose/snippets/src/main/res/layout/fragment_bottom_sheet.xml create mode 100644 compose/snippets/src/main/res/layout/list_item.xml create mode 100644 compose/snippets/src/main/res/layout/view_in_compose_nested_scroll_interop.xml diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt index 8920bd47f..fc8232470 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt @@ -97,6 +97,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.Adapter import androidx.recyclerview.widget.RecyclerView.VERTICAL import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.example.compose.snippets.R import com.google.android.material.bottomsheet.BottomSheetDialogFragment import kotlin.math.abs import kotlin.math.roundToInt @@ -584,20 +585,6 @@ private object NestedScrollInterop { } } // [END android_compose_touchinput_gestures_nested_scroll_interop_bottom_sheet_fragment] - - object R { - object id { - val compose_view = 1 - val main_list = 2 - } - - object layout { - val activity_main = 0 - val view_in_compose_nested_scroll_interop = 1 - val list_item = 2 - val fragment_bottom_sheet = 3 - } - } } // [START android_compose_touchinput_gestures_draggable] diff --git a/compose/snippets/src/main/res/layout/activity_main.xml b/compose/snippets/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..a1b38bea4 --- /dev/null +++ b/compose/snippets/src/main/res/layout/activity_main.xml @@ -0,0 +1,31 @@ + + + + + + + + + diff --git a/compose/snippets/src/main/res/layout/fragment_bottom_sheet.xml b/compose/snippets/src/main/res/layout/fragment_bottom_sheet.xml new file mode 100644 index 000000000..bd194635f --- /dev/null +++ b/compose/snippets/src/main/res/layout/fragment_bottom_sheet.xml @@ -0,0 +1,26 @@ + + + + + + + diff --git a/compose/snippets/src/main/res/layout/list_item.xml b/compose/snippets/src/main/res/layout/list_item.xml new file mode 100644 index 000000000..dffb0a599 --- /dev/null +++ b/compose/snippets/src/main/res/layout/list_item.xml @@ -0,0 +1,20 @@ + + + diff --git a/compose/snippets/src/main/res/layout/view_in_compose_nested_scroll_interop.xml b/compose/snippets/src/main/res/layout/view_in_compose_nested_scroll_interop.xml new file mode 100644 index 000000000..0e55e5ad4 --- /dev/null +++ b/compose/snippets/src/main/res/layout/view_in_compose_nested_scroll_interop.xml @@ -0,0 +1,21 @@ + + + From 42f8b967a6a9c8a6e81e2fa831f0b9d9c0719ba8 Mon Sep 17 00:00:00 2001 From: Ed Williams Date: Wed, 30 Sep 2026 14:08:26 +0000 Subject: [PATCH 5/7] Address pre-review audit findings for the Compose extraction - Optimization: one android_compose_graphics_images_optimization region around both top-level MyImage overloads, which is the tag the DAC macro uses. The overloads take different parameter types and don't collide, so the PainterParameterSnippet wrapper and the orphan _url/_painter tags go. - Adaptive grid: restore the 20.dp form with the required content hidden in a silent exclude. The tag is used only by the create-scrollable-grid quick guide, whose prose explains 20.dp. The earlier 128.dp "fix" was checked against lists.md, which uses android_compose_layouts_lazy_grid_adaptive, and duplicated that tag. - Nested scroll interop: drop the TopAppBar stub that shadowed the Material API and call the real Material3 TopAppBar with a visible title = {}. The page omits the required title. @OptIn(ExperimentalMaterial3Api::class) is on the enclosing private object. - Replace the hand-written "// ..." plus silent exclude forms: the AndroidView elision is a plain comment (the trailing comma compiles), and getItemCount() sits in a plain exclude. - Add full stops to five comments (D29). --- .../images/ImageOptimizationSnippets.kt | 15 ++++-------- .../snippets/lists/LazyListSnippets.kt | 11 ++++----- .../touchinput/gestures/GesturesSnippets.kt | 23 ++++++++----------- 3 files changed, 20 insertions(+), 29 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/images/ImageOptimizationSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/images/ImageOptimizationSnippets.kt index e612cdb4e..7675746f5 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/images/ImageOptimizationSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/images/ImageOptimizationSnippets.kt @@ -19,20 +19,15 @@ package com.example.compose.snippets.images import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.painter.Painter -// [START android_compose_graphics_images_optimization_url] +// [START android_compose_graphics_images_optimization] // Prefer this: @Composable fun MyImage(url: String) { } -// [END android_compose_graphics_images_optimization_url] - -private object PainterParameterSnippet { - // [START android_compose_graphics_images_optimization_painter] - // Over this: - @Composable - fun MyImage(painter: Painter) { +// Over this: +@Composable +fun MyImage(painter: Painter) { - } - // [END android_compose_graphics_images_optimization_painter] } +// [END android_compose_graphics_images_optimization] diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt index fe8b0a10e..7a3be4483 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt @@ -859,12 +859,11 @@ fun ScrollingGrid() { private fun LazyVerticalGridAdaptiveSample() { // [START android_compose_layout_scrollable_grid_adaptive] LazyVerticalGrid( - columns = GridCells.Adaptive(minSize = 128.dp) - ) { - items(photos) { photo -> - PhotoItem(photo) - } - } + columns = GridCells.Adaptive(minSize = 20.dp) + // [START_EXCLUDE silent] + , content = {} + // [END_EXCLUDE] + ) // [END android_compose_layout_scrollable_grid_adaptive] } diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt index fc8232470..94e4c7ff5 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt @@ -55,7 +55,9 @@ import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.FractionalThreshold import androidx.compose.material.rememberSwipeableState import androidx.compose.material.swipeable +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.annotation.FrequentlyChangingValue @@ -404,6 +406,7 @@ private fun AutomaticNestedScroll() { } // [END android_compose_touchinput_gestures_nested_scroll] +@OptIn(ExperimentalMaterial3Api::class) private object NestedScrollInterop { // [START android_compose_touchinput_gestures_nested_scroll_interop_activity] open class MainActivity : ComponentActivity() { @@ -437,9 +440,6 @@ private object NestedScrollInterop { private val ToolbarHeight = 56.dp - @Composable - private fun TopAppBar(modifier: Modifier = Modifier) {} - // [START android_compose_touchinput_gestures_nested_scroll_interop_compose_parent_android_child] @Composable private fun NestedScrollInteropComposeParentWithAndroidChildExample() { @@ -447,12 +447,12 @@ private object NestedScrollInterop { val toolbarOffsetHeightPx = remember { mutableStateOf(0f) } // Sets up the nested scroll connection between the Box composable parent - // and the child AndroidView containing the RecyclerView + // 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 + // collapsible behaviour. val delta = available.y val newOffset = toolbarOffsetHeightPx.value + delta toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f) @@ -467,6 +467,7 @@ private object NestedScrollInterop { .nestedScroll(nestedScrollConnection) ) { TopAppBar( + title = {}, modifier = Modifier .height(ToolbarHeight) .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) } @@ -482,14 +483,11 @@ private object NestedScrollInterop { } }.also { // Nested scrolling interop is enabled when - // nested scroll is enabled for the root View + // nested scroll is enabled for the root View. ViewCompat.setNestedScrollingEnabled(it, true) } }, // ... - // [START_EXCLUDE silent] - modifier = Modifier.fillMaxSize() - // [END_EXCLUDE] ) } } @@ -517,8 +515,7 @@ private object NestedScrollInterop { // ... } } - // ... - // [START_EXCLUDE silent] + // [START_EXCLUDE] override fun getItemCount(): Int = items.size // [END_EXCLUDE] } @@ -532,7 +529,7 @@ private object NestedScrollInterop { .fillMaxSize() .scrollable(rememberScrollableState { // View component deltas should be reflected in Compose - // components that participate in nested scrolling + // components that participate in nested scrolling. it }, Orientation.Vertical) ) { @@ -542,7 +539,7 @@ private object NestedScrollInterop { .inflate(R.layout.list_item, null) .apply { // Nested scrolling interop is enabled when - // nested scroll is enabled for the root View + // nested scroll is enabled for the root View. ViewCompat.setNestedScrollingEnabled(this, true) } } From 6b940c296b6882bccdae7b81b4a2f05009168a4c Mon Sep 17 00:00:00 2001 From: Ed Williams Date: Mon, 5 Oct 2026 19:37:12 +0000 Subject: [PATCH 6/7] Remove the nested scroll modifiers page, migrated in #1118 The nested-scroll-modifiers page was queued twice, and it's already migrated in android/snippets#1118. Both pull requests add the same layout files, so this branch leaves the page to #1118: - GesturesSnippets.kt goes back to main. The three new interop region tags, their imports, and the @OptIn on NestedScrollInterop are removed, and the stand-in object R that backs the existing interop_activity region is restored. - The four layouts that were added only for those snippets are removed: activity_main, fragment_bottom_sheet, list_item, and view_in_compose_nested_scroll_interop. --- .../touchinput/gestures/GesturesSnippets.kt | 160 +----------------- .../src/main/res/layout/activity_main.xml | 31 ---- .../main/res/layout/fragment_bottom_sheet.xml | 26 --- .../src/main/res/layout/list_item.xml | 20 --- .../view_in_compose_nested_scroll_interop.xml | 21 --- 5 files changed, 5 insertions(+), 253 deletions(-) delete mode 100644 compose/snippets/src/main/res/layout/activity_main.xml delete mode 100644 compose/snippets/src/main/res/layout/fragment_bottom_sheet.xml delete mode 100644 compose/snippets/src/main/res/layout/list_item.xml delete mode 100644 compose/snippets/src/main/res/layout/view_in_compose_nested_scroll_interop.xml diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt index 94e4c7ff5..0e3d687e9 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/touchinput/gestures/GesturesSnippets.kt @@ -19,9 +19,6 @@ package com.example.compose.snippets.touchinput.gestures import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup import androidx.activity.ComponentActivity import androidx.compose.foundation.MutatePriority import androidx.compose.foundation.background @@ -55,9 +52,7 @@ import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.FractionalThreshold import androidx.compose.material.rememberSwipeableState import androidx.compose.material.swipeable -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.annotation.FrequentlyChangingValue @@ -92,15 +87,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.util.fastCoerceIn import androidx.compose.ui.util.fastRoundToInt -import androidx.compose.ui.viewinterop.AndroidView -import androidx.core.view.ViewCompat -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.RecyclerView.Adapter -import androidx.recyclerview.widget.RecyclerView.VERTICAL -import androidx.recyclerview.widget.RecyclerView.ViewHolder -import com.example.compose.snippets.R -import com.google.android.material.bottomsheet.BottomSheetDialogFragment import kotlin.math.abs import kotlin.math.roundToInt @@ -406,7 +392,6 @@ private fun AutomaticNestedScroll() { } // [END android_compose_touchinput_gestures_nested_scroll] -@OptIn(ExperimentalMaterial3Api::class) private object NestedScrollInterop { // [START android_compose_touchinput_gestures_nested_scroll_interop_activity] open class MainActivity : ComponentActivity() { @@ -438,150 +423,15 @@ private object NestedScrollInterop { } // [END android_compose_touchinput_gestures_nested_scroll_interop_activity] - private val ToolbarHeight = 56.dp - - // [START android_compose_touchinput_gestures_nested_scroll_interop_compose_parent_android_child] - @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(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() { - 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) { - // ... - } + object R { + object id { + val compose_view = 1 } - // [START_EXCLUDE] - override fun getItemCount(): Int = items.size - // [END_EXCLUDE] - } - // [END android_compose_touchinput_gestures_nested_scroll_interop_compose_parent_android_child] - - // [START android_compose_touchinput_gestures_nested_scroll_interop_scrollable_view_in_compose] - @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) - } - } - ) - } - } - // [END android_compose_touchinput_gestures_nested_scroll_interop_scrollable_view_in_compose] - - // [START android_compose_touchinput_gestures_nested_scroll_interop_bottom_sheet_fragment] - 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(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 - } + object layout { + val activity_main = 0 } } - // [END android_compose_touchinput_gestures_nested_scroll_interop_bottom_sheet_fragment] } // [START android_compose_touchinput_gestures_draggable] diff --git a/compose/snippets/src/main/res/layout/activity_main.xml b/compose/snippets/src/main/res/layout/activity_main.xml deleted file mode 100644 index a1b38bea4..000000000 --- a/compose/snippets/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - diff --git a/compose/snippets/src/main/res/layout/fragment_bottom_sheet.xml b/compose/snippets/src/main/res/layout/fragment_bottom_sheet.xml deleted file mode 100644 index bd194635f..000000000 --- a/compose/snippets/src/main/res/layout/fragment_bottom_sheet.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - diff --git a/compose/snippets/src/main/res/layout/list_item.xml b/compose/snippets/src/main/res/layout/list_item.xml deleted file mode 100644 index dffb0a599..000000000 --- a/compose/snippets/src/main/res/layout/list_item.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - diff --git a/compose/snippets/src/main/res/layout/view_in_compose_nested_scroll_interop.xml b/compose/snippets/src/main/res/layout/view_in_compose_nested_scroll_interop.xml deleted file mode 100644 index 0e55e5ad4..000000000 --- a/compose/snippets/src/main/res/layout/view_in_compose_nested_scroll_interop.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - From 87645e13b8af348c470275b85e5b9dac1c36737f Mon Sep 17 00:00:00 2001 From: Ed Williams Date: Mon, 5 Oct 2026 22:30:16 +0000 Subject: [PATCH 7/7] Show the trailing content lambda in the adaptive grid snippet Drop the silent exclude around content = {} so the published snippet shows the required empty trailing lambda, ) {}, per review. --- .../com/example/compose/snippets/lists/LazyListSnippets.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt index 7a3be4483..231a45f58 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/lists/LazyListSnippets.kt @@ -860,10 +860,7 @@ private fun LazyVerticalGridAdaptiveSample() { // [START android_compose_layout_scrollable_grid_adaptive] LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 20.dp) - // [START_EXCLUDE silent] - , content = {} - // [END_EXCLUDE] - ) + ) {} // [END android_compose_layout_scrollable_grid_adaptive] }