From a59caf2b233ccd2749dd7467ea047653cb16c86d Mon Sep 17 00:00:00 2001 From: jakeroseman Date: Fri, 11 Oct 2024 09:55:42 +0100 Subject: [PATCH 1/5] basic menu examples --- compose/snippets/build.gradle.kts | 1 + .../compose/snippets/SnippetsActivity.kt | 2 + .../compose/snippets/components/Menus.kt | 201 ++++++++++++++++++ .../snippets/navigation/Destination.kt | 3 +- gradle/libs.versions.toml | 2 + 5 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt diff --git a/compose/snippets/build.gradle.kts b/compose/snippets/build.gradle.kts index 8b644b694..2175f4dfe 100644 --- a/compose/snippets/build.gradle.kts +++ b/compose/snippets/build.gradle.kts @@ -77,6 +77,7 @@ android { dependencies { implementation(libs.androidx.work.runtime.ktx) + implementation(libs.androidx.core) val composeBom = platform(libs.androidx.compose.bom) implementation(composeBom) androidTestImplementation(composeBom) 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 357cf556f..645cd9e54 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 @@ -41,6 +41,7 @@ import com.example.compose.snippets.components.DatePickerExamples import com.example.compose.snippets.components.DialogExamples import com.example.compose.snippets.components.DividerExamples import com.example.compose.snippets.components.FloatingActionButtonExamples +import com.example.compose.snippets.components.MenusExamples import com.example.compose.snippets.components.PartialBottomSheet import com.example.compose.snippets.components.ProgressIndicatorExamples import com.example.compose.snippets.components.ScaffoldExample @@ -111,6 +112,7 @@ class SnippetsActivity : ComponentActivity() { TopComponentsDestination.TimePickerExamples -> TimePickerExamples() TopComponentsDestination.DatePickerExamples -> DatePickerExamples() TopComponentsDestination.CarouselExamples -> CarouselExamples() + TopComponentsDestination.MenusExample -> MenusExamples() } } } diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt b/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt new file mode 100644 index 000000000..8c6a4e7bf --- /dev/null +++ b/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt @@ -0,0 +1,201 @@ +package com.example.compose.snippets.components + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.Help +import androidx.compose.material.icons.automirrored.filled.OpenInNew +import androidx.compose.material.icons.automirrored.filled.Send +import androidx.compose.material.icons.automirrored.outlined.Help +import androidx.compose.material.icons.automirrored.outlined.OpenInNew +import androidx.compose.material.icons.automirrored.outlined.Send +import androidx.compose.material.icons.filled.Feedback +import androidx.compose.material.icons.filled.Info +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.filled.Settings +import androidx.compose.material3.Button +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp + +@Composable +fun MenusExamples() { + var currentExample by remember { mutableStateOf<(@Composable () -> Unit)?>(null) } + + Box(modifier = Modifier.fillMaxSize()) { + currentExample?.let { + it() + FloatingActionButton( + onClick = { currentExample = null }, + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(16.dp) + ) { + Text(text = "Close example", modifier = Modifier.padding(16.dp)) + } + return + } + + Column(modifier = Modifier.padding(16.dp)) { + Button(onClick = { currentExample = { MinimalDropdownMenu() } }) { + Text("Minimal dropdown menu") + } + Button(onClick = { currentExample = { LongBasicDropdownMenu() } }) { + Text("Dropdown menu with many items") + } + Button(onClick = { currentExample = { DropdownMenuWithDetails() } }) { + Text("Dropdown menu with sections and icons") + } + } + } +} + +// [START android_compose_components_minimaldropdownmenu] +@Composable +fun MinimalDropdownMenu() { + var expanded by remember { mutableStateOf(false) } + Box( + modifier = Modifier + .padding(16.dp) + ) { + IconButton(onClick = { expanded = !expanded }) { + Icon(Icons.Default.MoreVert, contentDescription = "More options") + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DropdownMenuItem( + text = { Text("Option 1") }, + onClick = { /* Do something... */ } + ) + DropdownMenuItem( + text = { Text("Option 2") }, + onClick = { /* Do something... */ } + ) + } + } +} +// [END android_compose_components_minimaldropdownmenu] + +@Preview +@Composable +fun MinimalDropdownMenuPreview() { + MinimalDropdownMenu() +} + +// [START android_compose_components_longbasicdropdownmenu] +@Composable +fun LongBasicDropdownMenu() { + var expanded by remember { mutableStateOf(false) } + // Placeholder list of 100 strings for demonstration + val menuItemData = List(100) { "Option ${it + 1}" } + + Box( + modifier = Modifier + .padding(16.dp) + ) { + IconButton(onClick = { expanded = !expanded }) { + Icon(Icons.Default.MoreVert, contentDescription = "More options") + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + menuItemData.forEach { option -> + DropdownMenuItem( + text = { Text(option) }, + onClick = { /* Do something... */ } + ) + } + } + } +} +// [END android_compose_components_longbasicdropdownmenu] + +@Preview +@Composable +fun LongBasicDropdownMenuPreview() { + LongBasicDropdownMenu() +} + +// [START android_compose_components_dropdownmenuwithdetails] +@Composable +fun DropdownMenuWithDetails() { + var expanded by remember { mutableStateOf(false) } + + Box( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + IconButton(onClick = { expanded = true }) { + Icon(Icons.Default.MoreVert, contentDescription = "More options") + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + // First section + DropdownMenuItem( + text = { Text("Profile") }, + leadingIcon = { Icon(Icons.Default.Person, contentDescription = null) }, + onClick = { /* Do something... */ } + ) + DropdownMenuItem( + text = { Text("Settings") }, + leadingIcon = { Icon(Icons.Default.Settings, contentDescription = null) }, + onClick = { /* Do something... */ } + ) + + HorizontalDivider() + + // Second section + DropdownMenuItem( + text = { Text("Send Feedback") }, + leadingIcon = { Icon(Icons.Default.Feedback, contentDescription = null) }, + trailingIcon = { Icon(Icons.AutoMirrored.Outlined.Send, contentDescription = null) }, + onClick = { /* Do something... */ } + ) + + HorizontalDivider() + + // Third section + DropdownMenuItem( + text = { Text("About") }, + leadingIcon = { Icon(Icons.Default.Info, contentDescription = null) }, + onClick = { /* Do something... */ } + ) + DropdownMenuItem( + text = { Text("Help") }, + leadingIcon = { Icon(Icons.AutoMirrored.Outlined.Help, contentDescription = null) }, + trailingIcon = { Icon(Icons.AutoMirrored.Outlined.OpenInNew, contentDescription = null) }, + onClick = { /* Do something... */ } + ) + } + } +} +// [END android_compose_components_dropdownmenuwithdetails] + +@Preview +@Composable +fun DropdownMenuWithDetailsPreview() { + DropdownMenuWithDetails() +} \ No newline at end of file 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 20753d365..0bce08f0d 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 @@ -44,5 +44,6 @@ enum class TopComponentsDestination(val route: String, val title: String) { PartialBottomSheet("partialBottomSheets", "Partial Bottom Sheet"), TimePickerExamples("timePickerExamples", "Time Pickers"), DatePickerExamples("datePickerExamples", "Date Pickers"), - CarouselExamples("carouselExamples", "Carousel") + CarouselExamples("carouselExamples", "Carousel"), + MenusExample("menusExamples", "Menus") } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6aed96f52..cb8ae8448 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -50,6 +50,7 @@ targetSdk = "34" version-catalog-update = "0.8.4" wearComposeFoundation = "1.4.0" wearComposeMaterial = "1.4.0" +core = "1.13.0" [libraries] accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", version.ref = "accompanist" } @@ -127,6 +128,7 @@ kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutine kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } play-services-wearable = { module = "com.google.android.gms:play-services-wearable", version.ref = "playServicesWearable" } +androidx-core = { group = "androidx.core", name = "core", version.ref = "core" } [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } From 937730d43c655c55f6a2127d0ec4e4d4b8a109d5 Mon Sep 17 00:00:00 2001 From: jakeroseman Date: Fri, 11 Oct 2024 09:59:40 +0100 Subject: [PATCH 2/5] Make DropdownMenuWithDetails toggle expanded on click --- .../main/java/com/example/compose/snippets/components/Menus.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt b/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt index 8c6a4e7bf..ddba6e5d9 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt @@ -146,7 +146,7 @@ fun DropdownMenuWithDetails() { .fillMaxWidth() .padding(16.dp) ) { - IconButton(onClick = { expanded = true }) { + IconButton(onClick = { expanded = !expanded }) { Icon(Icons.Default.MoreVert, contentDescription = "More options") } DropdownMenu( From dcb8e50c48f381783b98f8e547563e80d53585ce Mon Sep 17 00:00:00 2001 From: jakeroseman Date: Fri, 11 Oct 2024 09:03:09 +0000 Subject: [PATCH 3/5] Apply Spotless --- .../compose/snippets/components/Menus.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt b/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt index ddba6e5d9..9afcf2348 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.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.foundation.layout.Box @@ -198,4 +214,4 @@ fun DropdownMenuWithDetails() { @Composable fun DropdownMenuWithDetailsPreview() { DropdownMenuWithDetails() -} \ No newline at end of file +} From 56e88c8bde975ef26dbde8b5286d2f9f21f2d0f0 Mon Sep 17 00:00:00 2001 From: jakeroseman Date: Fri, 11 Oct 2024 10:15:38 +0100 Subject: [PATCH 4/5] Remove unneeded dependencies --- compose/snippets/build.gradle.kts | 1 - gradle/libs.versions.toml | 2 -- 2 files changed, 3 deletions(-) diff --git a/compose/snippets/build.gradle.kts b/compose/snippets/build.gradle.kts index 2175f4dfe..8b644b694 100644 --- a/compose/snippets/build.gradle.kts +++ b/compose/snippets/build.gradle.kts @@ -77,7 +77,6 @@ android { dependencies { implementation(libs.androidx.work.runtime.ktx) - implementation(libs.androidx.core) val composeBom = platform(libs.androidx.compose.bom) implementation(composeBom) androidTestImplementation(composeBom) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cb8ae8448..6aed96f52 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -50,7 +50,6 @@ targetSdk = "34" version-catalog-update = "0.8.4" wearComposeFoundation = "1.4.0" wearComposeMaterial = "1.4.0" -core = "1.13.0" [libraries] accompanist-adaptive = { module = "com.google.accompanist:accompanist-adaptive", version.ref = "accompanist" } @@ -128,7 +127,6 @@ kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutine kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } play-services-wearable = { module = "com.google.android.gms:play-services-wearable", version.ref = "playServicesWearable" } -androidx-core = { group = "androidx.core", name = "core", version.ref = "core" } [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } From bf58090208a8ee8e94a1cd37b20518128d44ca5d Mon Sep 17 00:00:00 2001 From: jakeroseman Date: Fri, 11 Oct 2024 10:21:03 +0100 Subject: [PATCH 5/5] Remove unneeded imports --- .../compose/snippets/components/Menus.kt | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt b/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt index 9afcf2348..c355f9489 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/components/Menus.kt @@ -22,17 +22,14 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.Help -import androidx.compose.material.icons.automirrored.filled.OpenInNew -import androidx.compose.material.icons.automirrored.filled.Send import androidx.compose.material.icons.automirrored.outlined.Help import androidx.compose.material.icons.automirrored.outlined.OpenInNew import androidx.compose.material.icons.automirrored.outlined.Send -import androidx.compose.material.icons.filled.Feedback -import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.MoreVert -import androidx.compose.material.icons.filled.Person -import androidx.compose.material.icons.filled.Settings +import androidx.compose.material.icons.outlined.Feedback +import androidx.compose.material.icons.outlined.Info +import androidx.compose.material.icons.outlined.Person +import androidx.compose.material.icons.outlined.Settings import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem @@ -172,12 +169,12 @@ fun DropdownMenuWithDetails() { // First section DropdownMenuItem( text = { Text("Profile") }, - leadingIcon = { Icon(Icons.Default.Person, contentDescription = null) }, + leadingIcon = { Icon(Icons.Outlined.Person, contentDescription = null) }, onClick = { /* Do something... */ } ) DropdownMenuItem( text = { Text("Settings") }, - leadingIcon = { Icon(Icons.Default.Settings, contentDescription = null) }, + leadingIcon = { Icon(Icons.Outlined.Settings, contentDescription = null) }, onClick = { /* Do something... */ } ) @@ -186,7 +183,7 @@ fun DropdownMenuWithDetails() { // Second section DropdownMenuItem( text = { Text("Send Feedback") }, - leadingIcon = { Icon(Icons.Default.Feedback, contentDescription = null) }, + leadingIcon = { Icon(Icons.Outlined.Feedback, contentDescription = null) }, trailingIcon = { Icon(Icons.AutoMirrored.Outlined.Send, contentDescription = null) }, onClick = { /* Do something... */ } ) @@ -196,7 +193,7 @@ fun DropdownMenuWithDetails() { // Third section DropdownMenuItem( text = { Text("About") }, - leadingIcon = { Icon(Icons.Default.Info, contentDescription = null) }, + leadingIcon = { Icon(Icons.Outlined.Info, contentDescription = null) }, onClick = { /* Do something... */ } ) DropdownMenuItem(