From 95e6d36d8c4726a1fa514d835d64190f0ff38fbd Mon Sep 17 00:00:00 2001 From: jakeroseman Date: Mon, 9 Dec 2024 12:24:09 +0000 Subject: [PATCH 1/4] Add Swipe to reveal examples --- .../snippets/components/SwipeToDismissBox.kt | 304 ++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt b/compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt new file mode 100644 index 00000000..021379ac --- /dev/null +++ b/compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt @@ -0,0 +1,304 @@ +package com.example.compose.snippets.components + +import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CheckBox +import androidx.compose.material.icons.filled.CheckBoxOutlineBlank +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material3.Icon +import androidx.compose.material3.ListItem +import androidx.compose.material3.OutlinedCard +import androidx.compose.material3.SwipeToDismissBox +import androidx.compose.material3.SwipeToDismissBoxValue +import androidx.compose.material3.Text +import androidx.compose.material3.rememberSwipeToDismissBoxState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.lerp +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp + +// [START android_compose_components_todoitem] +data class TodoItem( + var isItemDone: Boolean, + var itemDescription: String +) +// [END android_compose_components_todoitem] + +// [START android_compose_components_swipeitem] +@Composable +fun SwipeItem( + value: TodoItem, + startToEndAction: (TodoItem) -> Unit, + endToStartAction: (TodoItem) -> Unit, + modifier: Modifier = Modifier, + content: @Composable (TodoItem) -> Unit +) { + val swipeToDismissBoxState = rememberSwipeToDismissBoxState( + confirmValueChange = { + when (it) { + SwipeToDismissBoxValue.StartToEnd -> { + startToEndAction(value) + // Do not dismiss this item. + false + } + SwipeToDismissBoxValue.EndToStart -> { + endToStartAction(value) + true + } + SwipeToDismissBoxValue.Settled -> { + false + } + } + } + ) + + SwipeToDismissBox( + state = swipeToDismissBoxState, + modifier = modifier + .fillMaxSize(), + backgroundContent = { + Row( + modifier = Modifier + .background( + when (swipeToDismissBoxState.dismissDirection) { + SwipeToDismissBoxValue.StartToEnd -> { + Color.Blue + } + SwipeToDismissBoxValue.EndToStart -> { + Color.Red + } + SwipeToDismissBoxValue.Settled -> { + Color.LightGray + } + } + ) + .fillMaxSize(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + when (swipeToDismissBoxState.dismissDirection) { + SwipeToDismissBoxValue.StartToEnd -> { + if (value.isItemDone) { + Icon( + imageVector = Icons.Default.CheckBox, + contentDescription = "Item done", + tint = Color.White, + modifier = Modifier + .padding(12.dp) + ) + } else { + Icon( + imageVector = Icons.Default.CheckBoxOutlineBlank, + contentDescription = "Item not done", + tint = Color.White, + modifier = Modifier + .padding(12.dp) + ) + } + } + + SwipeToDismissBoxValue.EndToStart -> { + Spacer(modifier = Modifier) + Icon( + imageVector = Icons.Default.Delete, + contentDescription = "Remove item", + tint = Color.White, + modifier = Modifier + .padding(12.dp) + ) + } + + SwipeToDismissBoxValue.Settled -> {} + } + } + } + ) { + content(value) + } +} +// [END android_compose_components_swipeitem] + +@Preview(showBackground = true) +// [START android_compose_components_swipeitemexample] +@Composable +private fun SwipeItemExample() { + val todoItems = remember { + mutableStateListOf( + TodoItem(isItemDone = false, itemDescription = "Pay bills"), + TodoItem(isItemDone = false, itemDescription = "Buy groceries"), + TodoItem(isItemDone = false, itemDescription = "Go to gym"), + TodoItem(isItemDone = false, itemDescription = "Get dinner") + ) + } + + LazyColumn { + items( + items = todoItems, + key = { it.itemDescription } + ) { todoItem -> + SwipeItem( + value = todoItem, + startToEndAction = { + todoItem.isItemDone = !todoItem.isItemDone + }, + endToStartAction = { + todoItems -= todoItem + } + ) { + ListItem( + headlineContent = { Text(text = todoItem.itemDescription) }, + supportingContent = { Text(text = "swipe me to update or remove.") } + ) + } + } + } +} +// [END android_compose_components_swipeitemexample] + +// [START android_compose_components_swipecarditem] +@Composable +fun SwipeCardItem( + value: TodoItem, + startToEndAction: (TodoItem) -> Unit, + endToStartAction: (TodoItem) -> Unit, + modifier: Modifier = Modifier, + content: @Composable (TodoItem) -> Unit +) { + val swipeToDismissState = rememberSwipeToDismissBoxState( + positionalThreshold = { totalDistance -> totalDistance * 0.25f }, + confirmValueChange = { + when (it) { + SwipeToDismissBoxValue.StartToEnd -> { + startToEndAction(value) + // Do not dismiss this item. + false + } + SwipeToDismissBoxValue.EndToStart -> { + endToStartAction(value) + true + } + SwipeToDismissBoxValue.Settled -> { + false + } + } + } + ) + + SwipeToDismissBox( + modifier = Modifier, + state = swipeToDismissState, + backgroundContent = { + // Cross-fade the background color as the drag gesture progresses. + val color by animateColorAsState( + when (swipeToDismissState.targetValue) { + SwipeToDismissBoxValue.Settled -> Color.LightGray + SwipeToDismissBoxValue.StartToEnd -> + lerp(Color.LightGray, Color.Blue, swipeToDismissState.progress) + + + SwipeToDismissBoxValue.EndToStart -> + lerp(Color.LightGray, Color.Red, swipeToDismissState.progress) + }, label = "swipeable card item background color" + ) + Row( + modifier = Modifier + .background(color) + .fillMaxSize(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + when (swipeToDismissState.dismissDirection) { + SwipeToDismissBoxValue.StartToEnd -> { + if (value.isItemDone) { + Icon( + imageVector = Icons.Default.CheckBox, + contentDescription = "Item done", + tint = Color.White, + modifier = Modifier + .padding(12.dp) + ) + } else { + Icon( + imageVector = Icons.Default.CheckBoxOutlineBlank, + contentDescription = "Item not done", + tint = Color.White, + modifier = Modifier + .padding(12.dp) + ) + } + } + + + SwipeToDismissBoxValue.EndToStart -> { + Spacer(modifier = Modifier) + Icon( + imageVector = Icons.Default.Delete, + contentDescription = "Remove item", + tint = Color.White, + modifier = Modifier + .padding(12.dp) + ) + } + + + SwipeToDismissBoxValue.Settled -> {} + } + } + }) { + content(value) + } +} +// [END android_compose_components_swipecarditem] + +// [START android_compose_components_swipecarditemexample] +@Preview +@Composable +private fun SwipeCardItemExample() { + val todoItems = remember { + mutableStateListOf( + TodoItem(isItemDone = false, itemDescription = "Pay bills"), + TodoItem(isItemDone = false, itemDescription = "Buy groceries"), + TodoItem(isItemDone = false, itemDescription = "Go to gym"), + TodoItem(isItemDone = false, itemDescription = "Get dinner") + ) + } + + LazyColumn { + items( + items = todoItems, + key = { it.itemDescription } + ) { todoItem -> + SwipeCardItem( + value = todoItem, + startToEndAction = { + todoItem.isItemDone = !todoItem.isItemDone + }, + endToStartAction = { + todoItems -= todoItem + } + ) { + OutlinedCard(shape = RectangleShape) { + ListItem( + headlineContent = { Text(todoItem.itemDescription) }, + supportingContent = { Text("swipe me to update or remove.") }) + } + } + } + } +} +// [END android_compose_components_swipecarditemexample] \ No newline at end of file From c85963d9d4dd94536c82fe3f09af183a9dd28ec4 Mon Sep 17 00:00:00 2001 From: jakeroseman Date: Mon, 9 Dec 2024 12:27:30 +0000 Subject: [PATCH 2/4] Apply Spotless --- .../snippets/components/SwipeToDismissBox.kt | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt b/compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt index 021379ac..09072c46 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2024 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.components import androidx.compose.animation.animateColorAsState @@ -210,10 +226,10 @@ fun SwipeCardItem( SwipeToDismissBoxValue.StartToEnd -> lerp(Color.LightGray, Color.Blue, swipeToDismissState.progress) - SwipeToDismissBoxValue.EndToStart -> lerp(Color.LightGray, Color.Red, swipeToDismissState.progress) - }, label = "swipeable card item background color" + }, + label = "swipeable card item background color" ) Row( modifier = Modifier @@ -243,7 +259,6 @@ fun SwipeCardItem( } } - SwipeToDismissBoxValue.EndToStart -> { Spacer(modifier = Modifier) Icon( @@ -255,11 +270,11 @@ fun SwipeCardItem( ) } - SwipeToDismissBoxValue.Settled -> {} } } - }) { + } + ) { content(value) } } @@ -295,10 +310,11 @@ private fun SwipeCardItemExample() { OutlinedCard(shape = RectangleShape) { ListItem( headlineContent = { Text(todoItem.itemDescription) }, - supportingContent = { Text("swipe me to update or remove.") }) + supportingContent = { Text("swipe me to update or remove.") } + ) } } } } } -// [END android_compose_components_swipecarditemexample] \ No newline at end of file +// [END android_compose_components_swipecarditemexample] From fe4f3f29f39d3985c8955d25b7434ecfdf4bc805 Mon Sep 17 00:00:00 2001 From: jakeroseman Date: Fri, 13 Dec 2024 16:28:01 +0000 Subject: [PATCH 3/4] Adding swipe to dismiss examples to the top compose examples. --- .../compose/snippets/SnippetsActivity.kt | 2 ++ .../snippets/components/SwipeToDismissBox.kt | 19 +++++++++++++++++++ .../snippets/navigation/Destination.kt | 3 ++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/SnippetsActivity.kt b/compose/snippets/src/main/java/com/example/compose/snippets/SnippetsActivity.kt index f07c4613..31553e40 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/SnippetsActivity.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/SnippetsActivity.kt @@ -48,6 +48,7 @@ import com.example.compose.snippets.components.ProgressIndicatorExamples import com.example.compose.snippets.components.ScaffoldExample import com.example.compose.snippets.components.SegmentedButtonExamples import com.example.compose.snippets.components.SliderExamples +import com.example.compose.snippets.components.SwipeToDismissBoxExamples import com.example.compose.snippets.components.SwitchExamples import com.example.compose.snippets.components.TimePickerExamples import com.example.compose.snippets.components.TooltipExamples @@ -121,6 +122,7 @@ class SnippetsActivity : ComponentActivity() { TopComponentsDestination.TooltipExamples -> TooltipExamples() TopComponentsDestination.NavigationDrawerExamples -> NavigationDrawerExamples() TopComponentsDestination.SegmentedButtonExamples -> SegmentedButtonExamples() + TopComponentsDestination.SwipeToDismissBoxExamples -> SwipeToDismissBoxExamples() } } } diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt b/compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt index 021379ac..2b6bb04c 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/components/SwipeToDismissBox.kt @@ -3,6 +3,7 @@ package com.example.compose.snippets.components import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -29,9 +30,27 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.lerp +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +@Preview +@Composable +fun SwipeToDismissBoxExamples() { + Column( + modifier = Modifier + .padding(16.dp) + .fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text("Swipe to dismiss with change of background", fontWeight = FontWeight.Bold) + SwipeItemExample() + Text("Swipe to dismiss with a cross-fade animation", fontWeight = FontWeight.Bold) + SwipeCardItemExample() + } +} + // [START android_compose_components_todoitem] data class TodoItem( var isItemDone: Boolean, diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation/Destination.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation/Destination.kt index 78396390..ce99f8b8 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation/Destination.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation/Destination.kt @@ -49,5 +49,6 @@ enum class TopComponentsDestination(val route: String, val title: String) { MenusExample("menusExamples", "Menus"), TooltipExamples("tooltipExamples", "Tooltips"), NavigationDrawerExamples("navigationDrawerExamples", "Navigation drawer"), - SegmentedButtonExamples("segmentedButtonExamples", "Segmented button") + SegmentedButtonExamples("segmentedButtonExamples", "Segmented button"), + SwipeToDismissBoxExamples("swipeToDismissBoxExamples", "Swipe to dismiss box examples") } From e0a89426a2187ff374056a124a1a55ad99d6e48e Mon Sep 17 00:00:00 2001 From: jakeroseman Date: Fri, 20 Dec 2024 17:00:02 +0000 Subject: [PATCH 4/4] Tidy up merge conflict errors --- .../java/com/example/compose/snippets/navigation/Destination.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation/Destination.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation/Destination.kt index b87a2766..8d86b28b 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation/Destination.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation/Destination.kt @@ -50,6 +50,6 @@ enum class TopComponentsDestination(val route: String, val title: String) { TooltipExamples("tooltipExamples", "Tooltips"), NavigationDrawerExamples("navigationDrawerExamples", "Navigation drawer"), SegmentedButtonExamples("segmentedButtonExamples", "Segmented button"), - SwipeToDismissBoxExamples("swipeToDismissBoxExamples", "Swipe to dismiss box examples") + SwipeToDismissBoxExamples("swipeToDismissBoxExamples", "Swipe to dismiss box examples"), SearchBarExamples("searchBarExamples", "Search bar") }