From 6e22540b725988f5ea132a976a438d2da536f7c1 Mon Sep 17 00:00:00 2001 From: ezhoon Date: Tue, 27 Feb 2024 21:59:48 +0900 Subject: [PATCH] =?UTF-8?q?#3=20Custom=20Layout=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Column / Row / Grid 간단하게만 구현 --- .../navigation/PlayGroundNavigation.kt | 33 +++++++- .../navigation/PlaygroundMainScreen.kt | 22 +++-- feature/compose-layout/build.gradle.kts | 1 + .../compose_layout/LayoutSampleModifiyRow.kt | 55 +++++++++++++ .../LayoutSampleModifyColumn.kt | 49 ++++++++++++ .../compose_layout/LayoutSampleModifyGrid.kt | 80 +++++++++++++++++++ .../compose_layout/LayoutSampleScreen.kt | 23 +++++- 7 files changed, 253 insertions(+), 10 deletions(-) create mode 100644 feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleModifiyRow.kt create mode 100644 feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleModifyColumn.kt create mode 100644 feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleModifyGrid.kt diff --git a/app/src/main/java/com/sample/playground/navigation/PlayGroundNavigation.kt b/app/src/main/java/com/sample/playground/navigation/PlayGroundNavigation.kt index fa6d535..641c74c 100644 --- a/app/src/main/java/com/sample/playground/navigation/PlayGroundNavigation.kt +++ b/app/src/main/java/com/sample/playground/navigation/PlayGroundNavigation.kt @@ -9,9 +9,13 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import com.sample.feature.compose_layout.LayoutSampleModifyColumn +import com.sample.feature.compose_layout.LayoutSampleModifyGrid +import com.sample.feature.compose_layout.LayoutSampleModifyRow import com.sample.feature.compose_layout.LayoutSampleScreen import com.sample.feature.window.WindowSampleActivity @@ -43,10 +47,35 @@ fun PlaygroundNavigation() { } } - composable(route = PlaygroundNavigation.Layout.route) { - LayoutSampleScreen() + layoutSampleGraph(navController::navigate) + } +} + +fun NavGraphBuilder.layoutSampleGraph(onNavigate: (route: String) -> Unit) { + composable( + route = "${PlaygroundNavigation.Layout}" + ) { + LayoutSampleScreen{ + onNavigate("${PlaygroundNavigation.Layout}/$it") } } + composable( + route = "${PlaygroundNavigation.Layout}/row" + ) { + LayoutSampleModifyRow() + } + + composable( + route = "${PlaygroundNavigation.Layout}/column" + ) { + LayoutSampleModifyColumn() + } + + composable( + route = "${PlaygroundNavigation.Layout}/grid" + ) { + LayoutSampleModifyGrid() + } } sealed interface PlaygroundNavigation { diff --git a/app/src/main/java/com/sample/playground/navigation/PlaygroundMainScreen.kt b/app/src/main/java/com/sample/playground/navigation/PlaygroundMainScreen.kt index 05da0f1..ca4ca47 100644 --- a/app/src/main/java/com/sample/playground/navigation/PlaygroundMainScreen.kt +++ b/app/src/main/java/com/sample/playground/navigation/PlaygroundMainScreen.kt @@ -15,7 +15,7 @@ fun PlaygroundMainScreen( Column( modifier = modifier.fillMaxSize() ) { - playgroundItems.forEach { + playGroundStates.forEach { PlaygroundItem( name = it.name, description = it.description, @@ -26,21 +26,29 @@ fun PlaygroundMainScreen( } } -private val playgroundItems = listOf( - PlaygroundItem( +private val playGroundStates = listOf( + PlayGroundState( name = "Window", description = "Cutout, Immersive, SystemBars에 대한 예제입니다.", navigationRoute = PlaygroundNavigation.Window ), - PlaygroundItem( + PlayGroundState( name = "Layout", description = "Layout에 대한 예제입니다.", - navigationRoute = PlaygroundNavigation.Layout + navigationRoute = PlaygroundNavigation.Layout, + subItems = listOf( + PlayGroundState( + name = "Layout/", + description = "Custom Layout Row 예제", + navigationRoute = PlaygroundNavigation.Layout, + ) + ) ), ) -data class PlaygroundItem( +data class PlayGroundState( val name: String, val description: String, - val navigationRoute: PlaygroundNavigation + val navigationRoute: PlaygroundNavigation, + val subItems: List = emptyList() ) \ No newline at end of file diff --git a/feature/compose-layout/build.gradle.kts b/feature/compose-layout/build.gradle.kts index 621e2b0..2aa140d 100644 --- a/feature/compose-layout/build.gradle.kts +++ b/feature/compose-layout/build.gradle.kts @@ -9,4 +9,5 @@ android { } dependencies { + implementation(project(":base")) } \ No newline at end of file diff --git a/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleModifiyRow.kt b/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleModifiyRow.kt new file mode 100644 index 0000000..afcb3fa --- /dev/null +++ b/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleModifiyRow.kt @@ -0,0 +1,55 @@ +package com.sample.feature.compose_layout + +import android.util.Log +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.dp + +@Composable +fun LayoutSampleModifyRow( + modifier: Modifier = Modifier, + content: @Composable () -> Unit = { + Text(text = "first") + Spacer(modifier = Modifier.width(20.dp)) + Text(text = "second") + Spacer(modifier = Modifier.width(20.dp)) + Text(text = "third") + } +) { + Layout( + modifier = modifier + .fillMaxSize() + .background(Color.Gray), + content = content + ) { measureable: List, constraints: Constraints -> + val placeables = measureable.map { it.measure(constraints.copy(minWidth = 0, minHeight = 0)) } + + layout(width = constraints.maxWidth, height = constraints.maxHeight) { + var x = 0 + placeables.forEach { + Log.d(TAG, "place x $x ") + it.placeRelative(x, y = 0) + x += it.width + } + } + } +} + +@Preview(showBackground = true) +@Composable +fun LayoutSampleModifyRowPreview() { + LayoutSampleModifyRow() +} + +val TAG = "TAG" \ No newline at end of file diff --git a/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleModifyColumn.kt b/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleModifyColumn.kt new file mode 100644 index 0000000..25c9b23 --- /dev/null +++ b/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleModifyColumn.kt @@ -0,0 +1,49 @@ +package com.sample.feature.compose_layout + +import android.util.Log +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Constraints + +@Composable +fun LayoutSampleModifyColumn( + modifier: Modifier = Modifier, + content: @Composable () -> Unit = { + Text(text = "first", modifier = Modifier.background(Color.Black)) + Text(text = "second", modifier = Modifier.background(Color.Blue)) + Text(text = "third", modifier = Modifier.background(Color.Cyan)) + } +) { + Layout( + modifier = modifier + .fillMaxSize() + .background(Color.Gray), + content = content + ) { measureable: List, constraints: Constraints -> + val minWidthHeightZeroConstraint = constraints.copy(minWidth = 0, minHeight = 0) + val placeables = measureable.map { it.measure(minWidthHeightZeroConstraint) } + + layout(width = constraints.maxWidth, height = constraints.maxHeight) { + var y = 0 + placeables.forEach { placeable -> + Log.d(TAG, "y $y") + placeable.placeRelative(x = 0, y = y) + y += placeable.height + } + } + } +} + +@Preview(showBackground = true) +@Composable +private fun LayoutSampleModifyColumnPreview() { + LayoutSampleModifyColumn() +} \ No newline at end of file diff --git a/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleModifyGrid.kt b/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleModifyGrid.kt new file mode 100644 index 0000000..7b957c7 --- /dev/null +++ b/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleModifyGrid.kt @@ -0,0 +1,80 @@ +package com.sample.feature.compose_layout + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.Placeable +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import kotlin.random.Random + +private fun getRandomColor(): Color { + val randomColorValue = Random.nextLong(from = 0xFF000000, until = 0xFFFFFFFF) + return Color(randomColorValue) +} + +@Composable +fun LayoutSampleModifyGrid( + modifier: Modifier = Modifier, + columns: Int = 2, + itemPadding: PaddingValues = PaddingValues(10.dp), + content: @Composable () -> Unit = { + val sizes = listOf(150.dp, 200.dp, 300.dp, 150.dp, 100.dp, 100.dp) + val createBox: @Composable (Dp) -> Unit = { size -> + Box( + modifier = Modifier + .size(size) + .background(getRandomColor()) + ) + } + sizes.forEach { size -> + createBox(size) + } + } +) { + Layout( + modifier = modifier, + content = content + ) { measurables: List, constraints: Constraints -> + val columnWidth = constraints.maxWidth / columns + val itemConstraints = constraints.copy(maxWidth = columnWidth) + val left = itemPadding.calculateLeftPadding(LayoutDirection.Ltr).roundToPx() + val right = itemPadding.calculateRightPadding(LayoutDirection.Ltr).roundToPx() + val top = itemPadding.calculateTopPadding().roundToPx() + val bottom = itemPadding.calculateBottomPadding().roundToPx() + + val placeables: List> = measurables.map { measurable -> + measurable.measure(itemConstraints) + }.chunked(columns) + + val gridHeight = placeables.sumOf { gridPlaceables -> gridPlaceables.maxOf { it.height } } + + layout(constraints.maxWidth, gridHeight) { + var y = top + placeables.forEach { currentColumnPlaceables -> + val currentColumnMaxHeight = currentColumnPlaceables.maxOf { it.height } + var x = left + currentColumnPlaceables.forEach { placeable -> + placeable.place(x, y) + x += placeable.width + right + } + y += currentColumnMaxHeight + bottom + } + } + } +} + +@Preview +@Composable +private fun LayoutSampleModifyGridPreview() { + LayoutSampleModifyGrid() +} \ No newline at end of file diff --git a/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleScreen.kt b/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleScreen.kt index 15e3f95..f9ad9d4 100644 --- a/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleScreen.kt +++ b/feature/compose-layout/src/main/java/com/sample/feature/compose_layout/LayoutSampleScreen.kt @@ -4,12 +4,33 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import com.sample.base.PlaygroundItem @Composable fun LayoutSampleScreen( - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + onNavigate: (route: String) -> Unit ) { Column(modifier.fillMaxSize()) { + PlaygroundItem( + name = "Row", + description = "Layout으로 직접 만든 Row입니다.", + destinationRoute = "row", + onNavigate = onNavigate::invoke + ) + PlaygroundItem( + name = "Column", + description = "Layout으로 직접 만든 Column입니다.", + destinationRoute = "column", + onNavigate = onNavigate::invoke + ) + + PlaygroundItem( + name = "Grid", + description = "Layout으로 직접 만든 Column입니다.", + destinationRoute = "grid", + onNavigate = onNavigate::invoke + ) } } \ No newline at end of file