├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── kotlinc.xml ├── misc.xml └── vcs.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── customlayout │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── customlayout │ │ │ ├── AutoWidthSortColumnActivity.kt │ │ │ ├── BoxAsDividerNegativePaddingBehaviorActivity.kt │ │ │ ├── DividerColumnExperimentActivity.kt │ │ │ ├── DividerLazyColumnExperimentActivity.kt │ │ │ ├── DividerLikeColumnActivity.kt │ │ │ ├── DividerLikeLazyColumnActivity.kt │ │ │ ├── DividerLikeOnlyColumnActivity.kt │ │ │ ├── LayoutModifierSingleExperimentActivity.kt │ │ │ ├── LayoutModifierSizeExperimentActivity.kt │ │ │ ├── LayoutSizeExperimentActivity.kt │ │ │ ├── MainActivity.kt │ │ │ ├── NegativePaddingExperimentActivity.kt │ │ │ ├── Util.kt │ │ │ └── ui │ │ │ └── theme │ │ │ ├── Color.kt │ │ │ ├── Theme.kt │ │ │ └── Type.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── example │ └── customlayout │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.example.customlayout' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "com.example.customlayout" 12 | minSdk 24 13 | targetSdk 33 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | vectorDrawables { 19 | useSupportLibrary true 20 | } 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | compileOptions { 30 | sourceCompatibility JavaVersion.VERSION_1_8 31 | targetCompatibility JavaVersion.VERSION_1_8 32 | } 33 | kotlinOptions { 34 | jvmTarget = '1.8' 35 | } 36 | buildFeatures { 37 | compose true 38 | } 39 | composeOptions { 40 | kotlinCompilerExtensionVersion '1.3.2' 41 | } 42 | packagingOptions { 43 | resources { 44 | excludes += '/META-INF/{AL2.0,LGPL2.1}' 45 | } 46 | } 47 | } 48 | 49 | dependencies { 50 | 51 | implementation 'androidx.core:core-ktx:1.8.0' 52 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' 53 | implementation 'androidx.activity:activity-compose:1.5.1' 54 | implementation platform('androidx.compose:compose-bom:2022.10.00') 55 | implementation 'androidx.compose.ui:ui' 56 | implementation 'androidx.compose.ui:ui-graphics' 57 | implementation 'androidx.compose.ui:ui-tooling-preview' 58 | implementation 'androidx.compose.material3:material3' 59 | testImplementation 'junit:junit:4.13.2' 60 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 61 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 62 | androidTestImplementation platform('androidx.compose:compose-bom:2022.10.00') 63 | androidTestImplementation 'androidx.compose.ui:ui-test-junit4' 64 | debugImplementation 'androidx.compose.ui:ui-tooling' 65 | debugImplementation 'androidx.compose.ui:ui-test-manifest' 66 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/customlayout/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.customlayout", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/AutoWidthSortColumnActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.border 8 | import androidx.compose.foundation.layout.Arrangement 9 | import androidx.compose.foundation.layout.Box 10 | import androidx.compose.foundation.layout.Column 11 | import androidx.compose.foundation.layout.fillMaxSize 12 | import androidx.compose.foundation.layout.height 13 | import androidx.compose.foundation.layout.size 14 | import androidx.compose.foundation.layout.width 15 | import androidx.compose.material3.MaterialTheme 16 | import androidx.compose.material3.Surface 17 | import androidx.compose.material3.Text 18 | import androidx.compose.runtime.Composable 19 | import androidx.compose.ui.Alignment 20 | import androidx.compose.ui.Modifier 21 | import androidx.compose.ui.graphics.Color 22 | import androidx.compose.ui.layout.Layout 23 | import androidx.compose.ui.unit.dp 24 | import com.example.customlayout.ui.theme.CustomLayoutTheme 25 | 26 | class AutoWidthSortColumnActivity : ComponentActivity() { 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | setContent { 30 | CustomLayoutTheme { 31 | // A surface container using the 'background' color from the theme 32 | Surface( 33 | modifier = Modifier.fillMaxSize(), 34 | color = MaterialTheme.colorScheme.background 35 | ) { 36 | Greeting() 37 | } 38 | } 39 | } 40 | } 41 | 42 | @Composable 43 | fun Greeting(modifier: Modifier = Modifier) { 44 | Column( 45 | horizontalAlignment = Alignment.CenterHorizontally, 46 | verticalArrangement = Arrangement.Center 47 | ) { 48 | CustomLayout(Modifier.size(150.dp).border(1.dp, Color.Green)) { 49 | Text("Hello There") 50 | Text("Good") 51 | Text("Longer please") 52 | Text("More Text") 53 | Box(Modifier.height(10.dp).width(100.dp).background(Color.Cyan)) 54 | Box(Modifier.height(10.dp).width(10.dp).background(Color.Red)) 55 | Box(Modifier.height(10.dp).width(50.dp).background(Color.Magenta)) 56 | } 57 | } 58 | } 59 | 60 | @Composable 61 | fun CustomLayout(modifier: Modifier = Modifier, content: @Composable () -> Unit) { 62 | Layout( 63 | modifier = modifier, 64 | content = content 65 | ) { measurables, constraints -> 66 | val looseConstraints = constraints.copy( 67 | minWidth = 0, 68 | minHeight = 0, 69 | ) 70 | val placaebles = measurables.map { measurable -> 71 | measurable.measure(constraints = looseConstraints) 72 | }.sortedBy { it.width } 73 | 74 | layout(constraints.maxWidth, constraints.maxHeight) { 75 | var y = 0 76 | placaebles.forEach { placeable -> 77 | placeable.place(0, y) 78 | y += placeable.height 79 | } 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/BoxAsDividerNegativePaddingBehaviorActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.border 8 | import androidx.compose.foundation.layout.Arrangement 9 | import androidx.compose.foundation.layout.Box 10 | import androidx.compose.foundation.layout.BoxScope 11 | import androidx.compose.foundation.layout.Column 12 | import androidx.compose.foundation.layout.Row 13 | import androidx.compose.foundation.layout.fillMaxSize 14 | import androidx.compose.foundation.layout.size 15 | import androidx.compose.foundation.layout.width 16 | import androidx.compose.material3.Checkbox 17 | import androidx.compose.material3.ExperimentalMaterial3Api 18 | import androidx.compose.material3.MaterialTheme 19 | import androidx.compose.material3.Slider 20 | import androidx.compose.material3.Surface 21 | import androidx.compose.material3.Text 22 | import androidx.compose.runtime.Composable 23 | import androidx.compose.runtime.getValue 24 | import androidx.compose.runtime.mutableStateOf 25 | import androidx.compose.runtime.remember 26 | import androidx.compose.runtime.setValue 27 | import androidx.compose.ui.Alignment 28 | import androidx.compose.ui.Modifier 29 | import androidx.compose.ui.graphics.Color 30 | import androidx.compose.ui.layout.layout 31 | import androidx.compose.ui.unit.Dp 32 | import androidx.compose.ui.unit.dp 33 | import androidx.compose.ui.unit.offset 34 | import com.example.customlayout.ui.theme.CustomLayoutTheme 35 | 36 | class BoxAsDividerNegativePaddingBehaviorActivity :ComponentActivity() { 37 | override fun onCreate(savedInstanceState: Bundle?) { 38 | super.onCreate(savedInstanceState) 39 | setContent { 40 | CustomLayoutTheme { 41 | // A surface container using the 'background' color from the theme 42 | Surface( 43 | modifier = Modifier.fillMaxSize(), 44 | color = MaterialTheme.colorScheme.background 45 | ) { 46 | Greeting() 47 | } 48 | } 49 | } 50 | } 51 | 52 | @Composable 53 | fun Greeting() { 54 | Column( 55 | horizontalAlignment = Alignment.CenterHorizontally, 56 | ) { 57 | val textWidth = 180.dp 58 | var containerSize by remember { mutableStateOf(150) } 59 | Row(verticalAlignment = Alignment.CenterVertically) { 60 | Text(text = "Container Size: $containerSize", modifier = Modifier.width(textWidth)) 61 | Slider( 62 | value = containerSize.toFloat(), 63 | onValueChange = { containerSize = it.toInt() }, 64 | valueRange = 0f..300f 65 | ) 66 | } 67 | var boxSize by remember { mutableStateOf(150) } 68 | var useBoxSize by remember { mutableStateOf(false) } 69 | Row(verticalAlignment = Alignment.CenterVertically) { 70 | Row(modifier = Modifier.width(textWidth), 71 | verticalAlignment = Alignment.CenterVertically 72 | ) { 73 | Checkbox( 74 | checked = useBoxSize, 75 | onCheckedChange = { useBoxSize = it } 76 | ) 77 | Text(text = "Box Size: $boxSize") 78 | } 79 | Slider( 80 | value = boxSize.toFloat(), 81 | onValueChange = { boxSize = it.toInt() }, 82 | valueRange = 0f..300f, 83 | enabled = useBoxSize 84 | ) 85 | } 86 | var constraintOffSet by remember { mutableStateOf(0) } 87 | 88 | Row(verticalAlignment = Alignment.CenterVertically) { 89 | Text( 90 | text = "Constraint Offset: $constraintOffSet", 91 | modifier = Modifier.width(textWidth) 92 | ) 93 | Slider( 94 | value = constraintOffSet.toFloat(), 95 | onValueChange = { constraintOffSet = it.toInt() }, 96 | valueRange = -150f..150f 97 | ) 98 | } 99 | var layoutSizeChange by remember { mutableStateOf(0) } 100 | Row(verticalAlignment = Alignment.CenterVertically) { 101 | Text( 102 | text = "Layout's Change: $layoutSizeChange", 103 | modifier = Modifier.width(textWidth) 104 | ) 105 | Slider( 106 | value = layoutSizeChange.toFloat(), 107 | onValueChange = { layoutSizeChange = it.toInt() }, 108 | valueRange = -200f..200f 109 | ) 110 | } 111 | var placement by remember { mutableStateOf(0) } 112 | Row(verticalAlignment = Alignment.CenterVertically) { 113 | Text(text = "Placement: $placement", modifier = Modifier.width(textWidth)) 114 | Slider( 115 | value = placement.toFloat(), 116 | onValueChange = { placement = it.toInt() }, 117 | valueRange = -200f..200f 118 | ) 119 | } 120 | var verticalCentered by remember { mutableStateOf(false) } 121 | var horizontalCentered by remember { mutableStateOf(false) } 122 | Row { 123 | Row(modifier = Modifier.weight(1f), 124 | horizontalArrangement = Arrangement.Center, 125 | verticalAlignment = Alignment.CenterVertically 126 | ) { 127 | Checkbox( 128 | checked = verticalCentered, 129 | onCheckedChange = { verticalCentered = it } 130 | ) 131 | Text("Vertical Centered") 132 | } 133 | Row(modifier = Modifier.weight(1f), 134 | horizontalArrangement = Arrangement.Center, 135 | verticalAlignment = Alignment.CenterVertically 136 | ) { 137 | Checkbox( 138 | checked = horizontalCentered, 139 | onCheckedChange = { horizontalCentered = it } 140 | ) 141 | Text("Horizontal Centered") 142 | } 143 | } 144 | Column( 145 | horizontalAlignment = Alignment.CenterHorizontally, 146 | verticalArrangement = Arrangement.Center, 147 | modifier = Modifier.fillMaxSize() 148 | ) { 149 | val horizontalAlignment = if (verticalCentered) 150 | Alignment.CenterHorizontally else Alignment.Start 151 | val verticalArrangement = if (horizontalCentered) 152 | Arrangement.Center else Arrangement.Top 153 | Column( 154 | horizontalAlignment = horizontalAlignment, 155 | verticalArrangement = verticalArrangement, 156 | modifier = Modifier 157 | .size(containerSize.dp) 158 | .background(Color.Yellow) 159 | ) { 160 | BoxLayout( 161 | constraintOffSet.dp, 162 | layoutSizeChange.dp, 163 | placement.dp, 164 | boxSize.dp, 165 | useBoxSize 166 | ) { 167 | Box(modifier = Modifier.fillMaxSize()) 168 | } 169 | } 170 | } 171 | } 172 | } 173 | 174 | @Composable 175 | private fun BoxLayout( 176 | constraintOffSet: Dp, 177 | layoutSizeChange: Dp, 178 | placement: Dp, 179 | boxSize: Dp, 180 | useBoxSize: Boolean, 181 | content: @Composable BoxScope.() -> Unit = {} 182 | ) { 183 | Box(modifier = Modifier 184 | .conditional(useBoxSize) { 185 | size(boxSize) 186 | } 187 | .background(GrayAlpha) 188 | .layout { measurable, constraints -> 189 | // Measure 190 | val placeable = measurable.measure( 191 | constraints.offset( 192 | constraintOffSet.roundToPx(), 193 | constraintOffSet.roundToPx() 194 | ) 195 | ) 196 | 197 | // Layout 198 | layout( 199 | placeable.width + layoutSizeChange.roundToPx(), 200 | placeable.height + layoutSizeChange.roundToPx() 201 | ) { 202 | placeable.place(placement.roundToPx(), placement.roundToPx()) 203 | } 204 | } 205 | .border(1.dp, Color.Red), 206 | content = content 207 | ) 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/DividerColumnExperimentActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.layout.Arrangement 8 | import androidx.compose.foundation.layout.Column 9 | import androidx.compose.foundation.layout.Row 10 | import androidx.compose.foundation.layout.fillMaxHeight 11 | import androidx.compose.foundation.layout.fillMaxSize 12 | import androidx.compose.foundation.layout.height 13 | import androidx.compose.foundation.layout.padding 14 | import androidx.compose.foundation.layout.width 15 | import androidx.compose.material3.Divider 16 | import androidx.compose.material3.MaterialTheme 17 | import androidx.compose.material3.Slider 18 | import androidx.compose.material3.Surface 19 | import androidx.compose.material3.Text 20 | import androidx.compose.runtime.Composable 21 | import androidx.compose.runtime.getValue 22 | import androidx.compose.runtime.mutableStateOf 23 | import androidx.compose.runtime.remember 24 | import androidx.compose.runtime.setValue 25 | import androidx.compose.ui.Alignment 26 | import androidx.compose.ui.Modifier 27 | import androidx.compose.ui.graphics.Color 28 | import androidx.compose.ui.layout.layout 29 | import androidx.compose.ui.unit.dp 30 | import androidx.compose.ui.unit.offset 31 | import com.example.customlayout.ui.theme.CustomLayoutTheme 32 | 33 | class DividerColumnExperimentActivity : ComponentActivity() { 34 | override fun onCreate(savedInstanceState: Bundle?) { 35 | super.onCreate(savedInstanceState) 36 | setContent { 37 | CustomLayoutTheme { 38 | // A surface container using the 'background' color from the theme 39 | Surface( 40 | modifier = Modifier.fillMaxSize(), 41 | color = MaterialTheme.colorScheme.background 42 | ) { 43 | Greeting() 44 | } 45 | } 46 | } 47 | } 48 | 49 | @Composable 50 | fun Greeting() { 51 | Column( 52 | horizontalAlignment = Alignment.CenterHorizontally, 53 | ) { 54 | val textWidth = 180.dp 55 | var parentPadding by remember { mutableStateOf(0) } 56 | Row(verticalAlignment = Alignment.CenterVertically) { 57 | Text(text = "Parent Padding: $parentPadding", modifier = Modifier.width(textWidth)) 58 | Slider( 59 | value = parentPadding.toFloat(), 60 | onValueChange = { parentPadding = it.toInt() }, 61 | valueRange = 0f..64f 62 | ) 63 | } 64 | var parentWidth by remember { mutableStateOf(150) } 65 | Row(verticalAlignment = Alignment.CenterVertically) { 66 | Text(text = "Parent Width: $parentWidth", modifier = Modifier.width(textWidth)) 67 | Slider( 68 | value = parentWidth.toFloat(), 69 | onValueChange = { parentWidth = it.toInt() }, 70 | valueRange = 0f..300f 71 | ) 72 | } 73 | var layoutSizeChange by remember { mutableStateOf(0) } 74 | Row(verticalAlignment = Alignment.CenterVertically) { 75 | Text( 76 | text = "Layout's Change: $layoutSizeChange", 77 | modifier = Modifier.width(textWidth) 78 | ) 79 | Slider( 80 | value = layoutSizeChange.toFloat(), 81 | onValueChange = { layoutSizeChange = it.toInt() }, 82 | valueRange = -250f..250f 83 | ) 84 | } 85 | var constraintOffSet by remember { mutableStateOf(0) } 86 | Row(verticalAlignment = Alignment.CenterVertically) { 87 | Text( 88 | text = "Constraint Offset: $constraintOffSet", 89 | modifier = Modifier.width(textWidth) 90 | ) 91 | Slider( 92 | value = constraintOffSet.toFloat(), 93 | onValueChange = { constraintOffSet = it.toInt() }, 94 | valueRange = -250f..250f 95 | ) 96 | } 97 | var placementX by remember { mutableStateOf(0) } 98 | Row(verticalAlignment = Alignment.CenterVertically) { 99 | Text(text = "Placement X: $placementX", modifier = Modifier.width(textWidth)) 100 | Slider( 101 | value = placementX.toFloat(), 102 | onValueChange = { placementX = it.toInt() }, 103 | valueRange = -150f..150f 104 | ) 105 | } 106 | var placementY by remember { mutableStateOf(0) } 107 | Row(verticalAlignment = Alignment.CenterVertically) { 108 | Text(text = "Placement Y: $placementY", modifier = Modifier.width(textWidth)) 109 | Slider( 110 | value = placementY.toFloat(), 111 | onValueChange = { placementY = it.toInt() }, 112 | valueRange = -150f..150f 113 | ) 114 | } 115 | Column( 116 | verticalArrangement = Arrangement.spacedBy(16.dp), 117 | modifier = Modifier 118 | .width(parentWidth.dp) 119 | .fillMaxHeight() 120 | .background(Color.Yellow) 121 | .padding(parentPadding.dp) 122 | ) { 123 | Divider(modifier = Modifier.height(20.dp)) 124 | Divider(modifier = Modifier 125 | .height(20.dp) 126 | .layout { measurable, constraints -> 127 | val placeable = 128 | measurable.measure(constraints.offset(constraintOffSet.dp.roundToPx())) 129 | layout( 130 | placeable.width + layoutSizeChange.dp.roundToPx(), 131 | placeable.height 132 | ) { placeable.place(placementX.dp.roundToPx(), placementY.dp.roundToPx()) } 133 | } 134 | ) 135 | } 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/DividerLazyColumnExperimentActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.layout.Arrangement 8 | import androidx.compose.foundation.layout.Column 9 | import androidx.compose.foundation.layout.Row 10 | import androidx.compose.foundation.layout.fillMaxHeight 11 | import androidx.compose.foundation.layout.fillMaxSize 12 | import androidx.compose.foundation.layout.height 13 | import androidx.compose.foundation.layout.padding 14 | import androidx.compose.foundation.layout.width 15 | import androidx.compose.foundation.lazy.LazyColumn 16 | import androidx.compose.material3.Divider 17 | import androidx.compose.material3.MaterialTheme 18 | import androidx.compose.material3.Slider 19 | import androidx.compose.material3.Surface 20 | import androidx.compose.material3.Text 21 | import androidx.compose.runtime.Composable 22 | import androidx.compose.runtime.getValue 23 | import androidx.compose.runtime.mutableStateOf 24 | import androidx.compose.runtime.remember 25 | import androidx.compose.runtime.setValue 26 | import androidx.compose.ui.Alignment 27 | import androidx.compose.ui.Modifier 28 | import androidx.compose.ui.graphics.Color 29 | import androidx.compose.ui.layout.layout 30 | import androidx.compose.ui.unit.dp 31 | import androidx.compose.ui.unit.offset 32 | import com.example.customlayout.ui.theme.CustomLayoutTheme 33 | 34 | class DividerLazyColumnExperimentActivity: ComponentActivity() { 35 | override fun onCreate(savedInstanceState: Bundle?) { 36 | super.onCreate(savedInstanceState) 37 | setContent { 38 | CustomLayoutTheme { 39 | // A surface container using the 'background' color from the theme 40 | Surface( 41 | modifier = Modifier.fillMaxSize(), 42 | color = MaterialTheme.colorScheme.background 43 | ) { 44 | Greeting() 45 | } 46 | } 47 | } 48 | } 49 | 50 | @Composable 51 | fun Greeting() { 52 | Column( 53 | horizontalAlignment = Alignment.CenterHorizontally, 54 | ) { 55 | val textWidth = 180.dp 56 | var parentPadding by remember { mutableStateOf(0) } 57 | Row(verticalAlignment = Alignment.CenterVertically) { 58 | Text(text = "Parent Padding: $parentPadding", modifier = Modifier.width(textWidth)) 59 | Slider( 60 | value = parentPadding.toFloat(), 61 | onValueChange = { parentPadding = it.toInt() }, 62 | valueRange = 0f..64f 63 | ) 64 | } 65 | var parentWidth by remember { mutableStateOf(150) } 66 | Row(verticalAlignment = Alignment.CenterVertically) { 67 | Text(text = "Parent Width: $parentWidth", modifier = Modifier.width(textWidth)) 68 | Slider( 69 | value = parentWidth.toFloat(), 70 | onValueChange = { parentWidth = it.toInt() }, 71 | valueRange = 0f..300f 72 | ) 73 | } 74 | var layoutSizeChange by remember { mutableStateOf(0) } 75 | Row(verticalAlignment = Alignment.CenterVertically) { 76 | Text( 77 | text = "Layout's Change: $layoutSizeChange", 78 | modifier = Modifier.width(textWidth) 79 | ) 80 | Slider( 81 | value = layoutSizeChange.toFloat(), 82 | onValueChange = { layoutSizeChange = it.toInt() }, 83 | valueRange = -250f..250f 84 | ) 85 | } 86 | var constraintOffSet by remember { mutableStateOf(0) } 87 | Row(verticalAlignment = Alignment.CenterVertically) { 88 | Text( 89 | text = "Constraint Offset: $constraintOffSet", 90 | modifier = Modifier.width(textWidth) 91 | ) 92 | Slider( 93 | value = constraintOffSet.toFloat(), 94 | onValueChange = { constraintOffSet = it.toInt() }, 95 | valueRange = -250f..250f 96 | ) 97 | } 98 | var placementX by remember { mutableStateOf(0) } 99 | Row(verticalAlignment = Alignment.CenterVertically) { 100 | Text(text = "Placement X: $placementX", modifier = Modifier.width(textWidth)) 101 | Slider( 102 | value = placementX.toFloat(), 103 | onValueChange = { placementX = it.toInt() }, 104 | valueRange = -150f..150f 105 | ) 106 | } 107 | var placementY by remember { mutableStateOf(0) } 108 | Row(verticalAlignment = Alignment.CenterVertically) { 109 | Text(text = "Placement Y: $placementY", modifier = Modifier.width(textWidth)) 110 | Slider( 111 | value = placementY.toFloat(), 112 | onValueChange = { placementY = it.toInt() }, 113 | valueRange = -150f..150f 114 | ) 115 | } 116 | LazyColumn( 117 | verticalArrangement = Arrangement.spacedBy(16.dp), 118 | modifier = Modifier 119 | .width(parentWidth.dp) 120 | .fillMaxHeight() 121 | .background(Color.Yellow) 122 | .padding(parentPadding.dp) 123 | ) { 124 | item { 125 | Divider(modifier = Modifier.height(20.dp)) 126 | } 127 | item { 128 | Divider(modifier = Modifier.height(20.dp) 129 | .layout { measurable, constraints -> 130 | val placeable = 131 | measurable.measure(constraints.offset(constraintOffSet.dp.roundToPx())) 132 | layout( 133 | placeable.width + layoutSizeChange.dp.roundToPx(), 134 | placeable.height 135 | ) { placeable.place(placementX.dp.roundToPx(), placementY.dp.roundToPx()) } 136 | } 137 | ) 138 | } 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/DividerLikeColumnActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.border 8 | import androidx.compose.foundation.layout.Arrangement 9 | import androidx.compose.foundation.layout.Box 10 | import androidx.compose.foundation.layout.BoxScope 11 | import androidx.compose.foundation.layout.Column 12 | import androidx.compose.foundation.layout.Row 13 | import androidx.compose.foundation.layout.Spacer 14 | import androidx.compose.foundation.layout.fillMaxSize 15 | import androidx.compose.foundation.layout.fillMaxWidth 16 | import androidx.compose.foundation.layout.height 17 | import androidx.compose.foundation.layout.size 18 | import androidx.compose.foundation.layout.width 19 | import androidx.compose.material3.Checkbox 20 | import androidx.compose.material3.Divider 21 | import androidx.compose.material3.MaterialTheme 22 | import androidx.compose.material3.Slider 23 | import androidx.compose.material3.Surface 24 | import androidx.compose.material3.Text 25 | import androidx.compose.runtime.Composable 26 | import androidx.compose.runtime.getValue 27 | import androidx.compose.runtime.mutableStateOf 28 | import androidx.compose.runtime.remember 29 | import androidx.compose.runtime.setValue 30 | import androidx.compose.ui.Alignment 31 | import androidx.compose.ui.Modifier 32 | import androidx.compose.ui.graphics.Color 33 | import androidx.compose.ui.layout.layout 34 | import androidx.compose.ui.unit.Dp 35 | import androidx.compose.ui.unit.dp 36 | import androidx.compose.ui.unit.offset 37 | import com.example.customlayout.ui.theme.CustomLayoutTheme 38 | 39 | class DividerLikeColumnActivity :ComponentActivity() { 40 | override fun onCreate(savedInstanceState: Bundle?) { 41 | super.onCreate(savedInstanceState) 42 | setContent { 43 | CustomLayoutTheme { 44 | // A surface container using the 'background' color from the theme 45 | Surface( 46 | modifier = Modifier.fillMaxSize(), 47 | color = MaterialTheme.colorScheme.background 48 | ) { 49 | Greeting() 50 | } 51 | } 52 | } 53 | } 54 | 55 | @Composable 56 | fun Greeting() { 57 | Column( 58 | horizontalAlignment = Alignment.CenterHorizontally, 59 | ) { 60 | val textWidth = 180.dp 61 | var containerSize by remember { mutableStateOf(150) } 62 | Row(verticalAlignment = Alignment.CenterVertically) { 63 | Text(text = "Container Size: $containerSize", modifier = Modifier.width(textWidth)) 64 | Slider( 65 | value = containerSize.toFloat(), 66 | onValueChange = { containerSize = it.toInt() }, 67 | valueRange = 0f..300f 68 | ) 69 | } 70 | var dividerWidth by remember { mutableStateOf(150) } 71 | var useDividerWidth by remember { mutableStateOf(false) } 72 | Row(verticalAlignment = Alignment.CenterVertically) { 73 | Row(modifier = Modifier.width(textWidth), 74 | verticalAlignment = Alignment.CenterVertically 75 | ) { 76 | Checkbox( 77 | checked = useDividerWidth, 78 | onCheckedChange = { useDividerWidth = it } 79 | ) 80 | Text(text = "Divider Size: $dividerWidth") 81 | } 82 | Slider( 83 | value = dividerWidth.toFloat(), 84 | onValueChange = { dividerWidth = it.toInt() }, 85 | valueRange = 0f..300f, 86 | enabled = useDividerWidth 87 | ) 88 | } 89 | var constraintOffSet by remember { mutableStateOf(0) } 90 | 91 | Row(verticalAlignment = Alignment.CenterVertically) { 92 | Text( 93 | text = "Constraint Offset: $constraintOffSet", 94 | modifier = Modifier.width(textWidth) 95 | ) 96 | Slider( 97 | value = constraintOffSet.toFloat(), 98 | onValueChange = { constraintOffSet = it.toInt() }, 99 | valueRange = -150f..150f 100 | ) 101 | } 102 | var layoutSizeChange by remember { mutableStateOf(0) } 103 | Row(verticalAlignment = Alignment.CenterVertically) { 104 | Text( 105 | text = "Layout's Change: $layoutSizeChange", 106 | modifier = Modifier.width(textWidth) 107 | ) 108 | Slider( 109 | value = layoutSizeChange.toFloat(), 110 | onValueChange = { layoutSizeChange = it.toInt() }, 111 | valueRange = -200f..200f 112 | ) 113 | } 114 | var placement by remember { mutableStateOf(0) } 115 | Row(verticalAlignment = Alignment.CenterVertically) { 116 | Text(text = "Placement: $placement", modifier = Modifier.width(textWidth)) 117 | Slider( 118 | value = placement.toFloat(), 119 | onValueChange = { placement = it.toInt() }, 120 | valueRange = -200f..200f 121 | ) 122 | } 123 | var verticalCentered by remember { mutableStateOf(false) } 124 | var horizontalCentered by remember { mutableStateOf(true) } 125 | Row { 126 | Row(modifier = Modifier.weight(1f), 127 | horizontalArrangement = Arrangement.Center, 128 | verticalAlignment = Alignment.CenterVertically 129 | ) { 130 | Checkbox( 131 | checked = verticalCentered, 132 | onCheckedChange = { verticalCentered = it } 133 | ) 134 | Text("Vertical Centered") 135 | } 136 | Row(modifier = Modifier.weight(1f), 137 | horizontalArrangement = Arrangement.Center, 138 | verticalAlignment = Alignment.CenterVertically 139 | ) { 140 | Checkbox( 141 | checked = horizontalCentered, 142 | onCheckedChange = { horizontalCentered = it } 143 | ) 144 | Text("Horizontal Centered") 145 | } 146 | } 147 | Column( 148 | horizontalAlignment = Alignment.CenterHorizontally, 149 | verticalArrangement = Arrangement.Center, 150 | modifier = Modifier.fillMaxSize() 151 | ) { 152 | val horizontalAlignment = if (verticalCentered) 153 | Alignment.CenterHorizontally else Alignment.Start 154 | val verticalArrangement = if (horizontalCentered) 155 | Arrangement.Center else Arrangement.Top 156 | Column( 157 | horizontalAlignment = horizontalAlignment, 158 | verticalArrangement = verticalArrangement, 159 | modifier = Modifier 160 | .size(containerSize.dp) 161 | .background(Color.Yellow) 162 | ) { 163 | val height = 20.dp 164 | Divider( 165 | Modifier 166 | .height(height) 167 | .fillMaxWidth()) 168 | Spacer(modifier = Modifier.height(height)) 169 | DividerLayout( 170 | constraintOffSet.dp, 171 | layoutSizeChange.dp, 172 | placement.dp, 173 | dividerWidth.dp, 174 | useDividerWidth 175 | ) { 176 | Box(modifier = Modifier 177 | .height(height) 178 | .fillMaxWidth()) 179 | } 180 | } 181 | } 182 | } 183 | } 184 | 185 | @Composable 186 | private fun DividerLayout( 187 | constraintOffSet: Dp, 188 | layoutSizeChange: Dp, 189 | placement: Dp, 190 | dividerWidth: Dp, 191 | useDividerWidth: Boolean, 192 | content: @Composable BoxScope.() -> Unit = {} 193 | ) { 194 | Box(modifier = Modifier 195 | .conditional(useDividerWidth) { 196 | size(dividerWidth) 197 | } 198 | .background(GrayAlpha) 199 | .layout { measurable, constraints -> 200 | // Measure 201 | val placeable = measurable.measure( 202 | constraints.offset( 203 | constraintOffSet.roundToPx(), 0 204 | ) 205 | ) 206 | 207 | // Layout 208 | layout( 209 | placeable.width + layoutSizeChange.roundToPx(), 210 | placeable.height 211 | ) { 212 | placeable.place(placement.roundToPx(), 0) 213 | } 214 | } 215 | .border(1.dp, Color.Red), 216 | content = content 217 | ) 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/DividerLikeLazyColumnActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.border 8 | import androidx.compose.foundation.layout.Arrangement 9 | import androidx.compose.foundation.layout.Box 10 | import androidx.compose.foundation.layout.BoxScope 11 | import androidx.compose.foundation.layout.Column 12 | import androidx.compose.foundation.layout.Row 13 | import androidx.compose.foundation.layout.Spacer 14 | import androidx.compose.foundation.layout.fillMaxSize 15 | import androidx.compose.foundation.layout.fillMaxWidth 16 | import androidx.compose.foundation.layout.height 17 | import androidx.compose.foundation.layout.size 18 | import androidx.compose.foundation.layout.width 19 | import androidx.compose.foundation.lazy.LazyColumn 20 | import androidx.compose.material3.Checkbox 21 | import androidx.compose.material3.Divider 22 | import androidx.compose.material3.MaterialTheme 23 | import androidx.compose.material3.Slider 24 | import androidx.compose.material3.Surface 25 | import androidx.compose.material3.Text 26 | import androidx.compose.runtime.Composable 27 | import androidx.compose.runtime.getValue 28 | import androidx.compose.runtime.mutableStateOf 29 | import androidx.compose.runtime.remember 30 | import androidx.compose.runtime.setValue 31 | import androidx.compose.ui.Alignment 32 | import androidx.compose.ui.Modifier 33 | import androidx.compose.ui.graphics.Color 34 | import androidx.compose.ui.layout.layout 35 | import androidx.compose.ui.unit.Dp 36 | import androidx.compose.ui.unit.dp 37 | import androidx.compose.ui.unit.offset 38 | import com.example.customlayout.ui.theme.CustomLayoutTheme 39 | 40 | class DividerLikeLazyColumnActivity :ComponentActivity() { 41 | override fun onCreate(savedInstanceState: Bundle?) { 42 | super.onCreate(savedInstanceState) 43 | setContent { 44 | CustomLayoutTheme { 45 | // A surface container using the 'background' color from the theme 46 | Surface( 47 | modifier = Modifier.fillMaxSize(), 48 | color = MaterialTheme.colorScheme.background 49 | ) { 50 | Greeting() 51 | } 52 | } 53 | } 54 | } 55 | 56 | @Composable 57 | fun Greeting() { 58 | Column( 59 | horizontalAlignment = Alignment.CenterHorizontally, 60 | ) { 61 | val textWidth = 180.dp 62 | var containerSize by remember { mutableStateOf(150) } 63 | Row(verticalAlignment = Alignment.CenterVertically) { 64 | Text(text = "Container Size: $containerSize", modifier = Modifier.width(textWidth)) 65 | Slider( 66 | value = containerSize.toFloat(), 67 | onValueChange = { containerSize = it.toInt() }, 68 | valueRange = 0f..300f 69 | ) 70 | } 71 | var dividerWidth by remember { mutableStateOf(150) } 72 | var useDividerWidth by remember { mutableStateOf(false) } 73 | Row(verticalAlignment = Alignment.CenterVertically) { 74 | Row(modifier = Modifier.width(textWidth), 75 | verticalAlignment = Alignment.CenterVertically 76 | ) { 77 | Checkbox( 78 | checked = useDividerWidth, 79 | onCheckedChange = { useDividerWidth = it } 80 | ) 81 | Text(text = "Divider Size: $dividerWidth") 82 | } 83 | Slider( 84 | value = dividerWidth.toFloat(), 85 | onValueChange = { dividerWidth = it.toInt() }, 86 | valueRange = 0f..300f, 87 | enabled = useDividerWidth 88 | ) 89 | } 90 | var constraintOffSet by remember { mutableStateOf(0) } 91 | 92 | Row(verticalAlignment = Alignment.CenterVertically) { 93 | Text( 94 | text = "Constraint Offset: $constraintOffSet", 95 | modifier = Modifier.width(textWidth) 96 | ) 97 | Slider( 98 | value = constraintOffSet.toFloat(), 99 | onValueChange = { constraintOffSet = it.toInt() }, 100 | valueRange = -150f..150f 101 | ) 102 | } 103 | var layoutSizeChange by remember { mutableStateOf(0) } 104 | Row(verticalAlignment = Alignment.CenterVertically) { 105 | Text( 106 | text = "Layout's Change: $layoutSizeChange", 107 | modifier = Modifier.width(textWidth) 108 | ) 109 | Slider( 110 | value = layoutSizeChange.toFloat(), 111 | onValueChange = { layoutSizeChange = it.toInt() }, 112 | valueRange = -200f..200f 113 | ) 114 | } 115 | var placement by remember { mutableStateOf(0) } 116 | Row(verticalAlignment = Alignment.CenterVertically) { 117 | Text(text = "Placement: $placement", modifier = Modifier.width(textWidth)) 118 | Slider( 119 | value = placement.toFloat(), 120 | onValueChange = { placement = it.toInt() }, 121 | valueRange = -200f..200f 122 | ) 123 | } 124 | var verticalCentered by remember { mutableStateOf(false) } 125 | var horizontalCentered by remember { mutableStateOf(true) } 126 | Row { 127 | Row(modifier = Modifier.weight(1f), 128 | horizontalArrangement = Arrangement.Center, 129 | verticalAlignment = Alignment.CenterVertically 130 | ) { 131 | Checkbox( 132 | checked = verticalCentered, 133 | onCheckedChange = { verticalCentered = it } 134 | ) 135 | Text("Vertical Centered") 136 | } 137 | Row(modifier = Modifier.weight(1f), 138 | horizontalArrangement = Arrangement.Center, 139 | verticalAlignment = Alignment.CenterVertically 140 | ) { 141 | Checkbox( 142 | checked = horizontalCentered, 143 | onCheckedChange = { horizontalCentered = it } 144 | ) 145 | Text("Horizontal Centered") 146 | } 147 | } 148 | Column( 149 | horizontalAlignment = Alignment.CenterHorizontally, 150 | verticalArrangement = Arrangement.Center, 151 | modifier = Modifier.fillMaxSize() 152 | ) { 153 | val horizontalAlignment = if (verticalCentered) 154 | Alignment.CenterHorizontally else Alignment.Start 155 | val verticalArrangement = if (horizontalCentered) 156 | Arrangement.Center else Arrangement.Top 157 | LazyColumn( 158 | horizontalAlignment = horizontalAlignment, 159 | verticalArrangement = verticalArrangement, 160 | modifier = Modifier 161 | .size(containerSize.dp) 162 | .background(Color.Yellow) 163 | ) { 164 | val height = 20.dp 165 | item { 166 | Divider( 167 | Modifier 168 | .height(height) 169 | .fillMaxWidth() 170 | ) 171 | } 172 | item { 173 | Spacer(modifier = Modifier.height(height)) 174 | } 175 | item { 176 | DividerLayout( 177 | constraintOffSet.dp, 178 | layoutSizeChange.dp, 179 | placement.dp, 180 | dividerWidth.dp, 181 | useDividerWidth 182 | ) { 183 | Box( 184 | modifier = Modifier 185 | .height(height) 186 | .fillMaxWidth() 187 | ) 188 | } 189 | } 190 | } 191 | } 192 | } 193 | } 194 | 195 | @Composable 196 | private fun DividerLayout( 197 | constraintOffSet: Dp, 198 | layoutSizeChange: Dp, 199 | placement: Dp, 200 | dividerWidth: Dp, 201 | useDividerWidth: Boolean, 202 | content: @Composable BoxScope.() -> Unit = {} 203 | ) { 204 | Box(modifier = Modifier 205 | .conditional(useDividerWidth) { 206 | size(dividerWidth) 207 | } 208 | .background(GrayAlpha) 209 | .layout { measurable, constraints -> 210 | // Measure 211 | val placeable = measurable.measure( 212 | constraints.offset( 213 | constraintOffSet.roundToPx(), 0 214 | ) 215 | ) 216 | 217 | // Layout 218 | layout( 219 | placeable.width + layoutSizeChange.roundToPx(), 220 | placeable.height 221 | ) { 222 | placeable.place(placement.roundToPx(), 0) 223 | } 224 | } 225 | .border(1.dp, Color.Red), 226 | content = content 227 | ) 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/DividerLikeOnlyColumnActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.border 8 | import androidx.compose.foundation.layout.Arrangement 9 | import androidx.compose.foundation.layout.Box 10 | import androidx.compose.foundation.layout.BoxScope 11 | import androidx.compose.foundation.layout.Column 12 | import androidx.compose.foundation.layout.Row 13 | import androidx.compose.foundation.layout.Spacer 14 | import androidx.compose.foundation.layout.fillMaxSize 15 | import androidx.compose.foundation.layout.fillMaxWidth 16 | import androidx.compose.foundation.layout.height 17 | import androidx.compose.foundation.layout.size 18 | import androidx.compose.foundation.layout.width 19 | import androidx.compose.material3.Checkbox 20 | import androidx.compose.material3.Divider 21 | import androidx.compose.material3.MaterialTheme 22 | import androidx.compose.material3.Slider 23 | import androidx.compose.material3.Surface 24 | import androidx.compose.material3.Text 25 | import androidx.compose.runtime.Composable 26 | import androidx.compose.runtime.getValue 27 | import androidx.compose.runtime.mutableStateOf 28 | import androidx.compose.runtime.remember 29 | import androidx.compose.runtime.setValue 30 | import androidx.compose.ui.Alignment 31 | import androidx.compose.ui.Modifier 32 | import androidx.compose.ui.graphics.Color 33 | import androidx.compose.ui.layout.layout 34 | import androidx.compose.ui.unit.Dp 35 | import androidx.compose.ui.unit.dp 36 | import androidx.compose.ui.unit.offset 37 | import com.example.customlayout.ui.theme.CustomLayoutTheme 38 | 39 | class DividerLikeOnlyColumnActivity :ComponentActivity() { 40 | override fun onCreate(savedInstanceState: Bundle?) { 41 | super.onCreate(savedInstanceState) 42 | setContent { 43 | CustomLayoutTheme { 44 | // A surface container using the 'background' color from the theme 45 | Surface( 46 | modifier = Modifier.fillMaxSize(), 47 | color = MaterialTheme.colorScheme.background 48 | ) { 49 | Greeting() 50 | } 51 | } 52 | } 53 | } 54 | 55 | @Composable 56 | fun Greeting() { 57 | Column( 58 | horizontalAlignment = Alignment.CenterHorizontally, 59 | ) { 60 | val textWidth = 180.dp 61 | var containerSize by remember { mutableStateOf(150) } 62 | Row(verticalAlignment = Alignment.CenterVertically) { 63 | Text(text = "Container Size: $containerSize", modifier = Modifier.width(textWidth)) 64 | Slider( 65 | value = containerSize.toFloat(), 66 | onValueChange = { containerSize = it.toInt() }, 67 | valueRange = 0f..300f 68 | ) 69 | } 70 | var layoutSizeChange by remember { mutableStateOf(0) } 71 | Row(verticalAlignment = Alignment.CenterVertically) { 72 | Text( 73 | text = "Layout's Change: $layoutSizeChange", 74 | modifier = Modifier.width(textWidth) 75 | ) 76 | Slider( 77 | value = layoutSizeChange.toFloat(), 78 | onValueChange = { layoutSizeChange = it.toInt() }, 79 | valueRange = -200f..200f 80 | ) 81 | } 82 | var verticalCentered by remember { mutableStateOf(false) } 83 | var horizontalCentered by remember { mutableStateOf(true) } 84 | Row { 85 | Row(modifier = Modifier.weight(1f), 86 | horizontalArrangement = Arrangement.Center, 87 | verticalAlignment = Alignment.CenterVertically 88 | ) { 89 | Checkbox( 90 | checked = verticalCentered, 91 | onCheckedChange = { verticalCentered = it } 92 | ) 93 | Text("Vertical Centered") 94 | } 95 | Row(modifier = Modifier.weight(1f), 96 | horizontalArrangement = Arrangement.Center, 97 | verticalAlignment = Alignment.CenterVertically 98 | ) { 99 | Checkbox( 100 | checked = horizontalCentered, 101 | onCheckedChange = { horizontalCentered = it } 102 | ) 103 | Text("Horizontal Centered") 104 | } 105 | } 106 | Column( 107 | horizontalAlignment = Alignment.CenterHorizontally, 108 | modifier = Modifier.fillMaxSize() 109 | ) { 110 | val horizontalAlignment = if (verticalCentered) 111 | Alignment.CenterHorizontally else Alignment.Start 112 | val verticalArrangement = if (horizontalCentered) 113 | Arrangement.Center else Arrangement.Top 114 | Column( 115 | horizontalAlignment = horizontalAlignment, 116 | verticalArrangement = verticalArrangement, 117 | modifier = Modifier 118 | .size(containerSize.dp) 119 | .background(Color.Yellow) 120 | ) { 121 | val height = 20.dp 122 | Divider( 123 | Modifier 124 | .height(height) 125 | .fillMaxWidth()) 126 | Spacer(modifier = Modifier.height(height)) 127 | Divider(modifier = Modifier 128 | .height(20.dp) 129 | .layout { measurable, constraints -> 130 | val placeable = measurable.measure(constraints) 131 | layout( 132 | width = layoutSizeChange.dp.roundToPx(), 133 | height = placeable.height 134 | ) { placeable.place(0, 0) } 135 | } 136 | ) 137 | Spacer(modifier = Modifier.height(height)) 138 | DividerLayout( 139 | layoutSizeChange.dp, 140 | ) { 141 | Box(modifier = Modifier 142 | .height(height) 143 | .fillMaxWidth()) 144 | } 145 | } 146 | } 147 | } 148 | } 149 | 150 | @Composable 151 | private fun DividerLayout( 152 | layoutSizeChange: Dp, 153 | content: @Composable BoxScope.() -> Unit = {} 154 | ) { 155 | Box(modifier = Modifier 156 | .background(GrayAlpha) 157 | .layout { measurable, constraints -> 158 | // Measure 159 | val placeable = measurable.measure(constraints) 160 | 161 | // Layout 162 | layout( 163 | layoutSizeChange.roundToPx(), 164 | placeable.height 165 | ) { 166 | placeable.place(0, 0) 167 | } 168 | } 169 | .border(1.dp, Color.Red), 170 | content = content 171 | ) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/LayoutModifierSingleExperimentActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.border 8 | import androidx.compose.foundation.layout.Arrangement 9 | import androidx.compose.foundation.layout.Box 10 | import androidx.compose.foundation.layout.BoxScope 11 | import androidx.compose.foundation.layout.Column 12 | import androidx.compose.foundation.layout.Row 13 | import androidx.compose.foundation.layout.fillMaxHeight 14 | import androidx.compose.foundation.layout.fillMaxSize 15 | import androidx.compose.foundation.layout.fillMaxWidth 16 | import androidx.compose.foundation.layout.size 17 | import androidx.compose.foundation.layout.width 18 | import androidx.compose.material3.Checkbox 19 | import androidx.compose.material3.ExperimentalMaterial3Api 20 | import androidx.compose.material3.MaterialTheme 21 | import androidx.compose.material3.Slider 22 | import androidx.compose.material3.Surface 23 | import androidx.compose.material3.Text 24 | import androidx.compose.material3.TextField 25 | import androidx.compose.runtime.Composable 26 | import androidx.compose.runtime.getValue 27 | import androidx.compose.runtime.mutableStateOf 28 | import androidx.compose.runtime.remember 29 | import androidx.compose.runtime.setValue 30 | import androidx.compose.ui.Alignment 31 | import androidx.compose.ui.Modifier 32 | import androidx.compose.ui.graphics.Color 33 | import androidx.compose.ui.layout.layout 34 | import androidx.compose.ui.text.input.TextFieldValue 35 | import androidx.compose.ui.unit.Dp 36 | import androidx.compose.ui.unit.dp 37 | import com.example.customlayout.ui.theme.CustomLayoutTheme 38 | 39 | class LayoutModifierSingleExperimentActivity : ComponentActivity() { 40 | override fun onCreate(savedInstanceState: Bundle?) { 41 | super.onCreate(savedInstanceState) 42 | setContent { 43 | CustomLayoutTheme { 44 | // A surface container using the 'background' color from the theme 45 | Surface( 46 | modifier = Modifier.fillMaxSize(), 47 | color = MaterialTheme.colorScheme.background 48 | ) { 49 | Greeting() 50 | } 51 | } 52 | } 53 | } 54 | 55 | @OptIn(ExperimentalMaterial3Api::class) 56 | @Composable 57 | fun Greeting() { 58 | Column( 59 | horizontalAlignment = Alignment.CenterHorizontally, 60 | ) { 61 | val textWidth = 180.dp 62 | var boxSize by remember { mutableStateOf(150) } 63 | Row(verticalAlignment = Alignment.CenterVertically) { 64 | Text(text = "Box Size: $boxSize", modifier = Modifier.width(textWidth)) 65 | Slider( 66 | value = boxSize.toFloat(), 67 | onValueChange = { boxSize = it.toInt() }, 68 | valueRange = 0f..300f 69 | ) 70 | } 71 | var minSize by remember { mutableStateOf(150) } 72 | var maxSize by remember { mutableStateOf(150) } 73 | 74 | Row(verticalAlignment = Alignment.CenterVertically) { 75 | Text( 76 | text = "Box Min Size: $minSize", 77 | modifier = Modifier.width(textWidth) 78 | ) 79 | Slider( 80 | value = minSize.toFloat(), 81 | onValueChange = { minSize = it.toInt() }, 82 | valueRange = 0f..maxSize.toFloat() 83 | ) 84 | } 85 | Row(verticalAlignment = Alignment.CenterVertically) { 86 | Text( 87 | text = "Box Max Size: $maxSize", 88 | modifier = Modifier.width(textWidth) 89 | ) 90 | Slider( 91 | value = maxSize.toFloat(), 92 | onValueChange = { maxSize = it.toInt() }, 93 | valueRange = minSize.toFloat()..300f 94 | ) 95 | } 96 | var layoutSizeChange by remember { mutableStateOf(0) } 97 | Row(verticalAlignment = Alignment.CenterVertically) { 98 | Text( 99 | text = "Layout's Change: $layoutSizeChange", 100 | modifier = Modifier.width(textWidth) 101 | ) 102 | Slider( 103 | value = layoutSizeChange.toFloat(), 104 | onValueChange = { layoutSizeChange = it.toInt() }, 105 | valueRange = -150f..150f 106 | ) 107 | } 108 | var placementX by remember { mutableStateOf(0) } 109 | Row(verticalAlignment = Alignment.CenterVertically) { 110 | Text(text = "Placement X: $placementX", modifier = Modifier.width(textWidth)) 111 | Slider( 112 | value = placementX.toFloat(), 113 | onValueChange = { placementX = it.toInt() }, 114 | valueRange = -150f..150f 115 | ) 116 | } 117 | var placementY by remember { mutableStateOf(0) } 118 | Row(verticalAlignment = Alignment.CenterVertically) { 119 | Text(text = "Placement Y: $placementY", modifier = Modifier.width(textWidth)) 120 | Slider( 121 | value = placementY.toFloat(), 122 | onValueChange = { placementY = it.toInt() }, 123 | valueRange = -150f..150f 124 | ) 125 | } 126 | var text by remember { mutableStateOf(TextFieldValue("")) } 127 | TextField( 128 | value = text, 129 | onValueChange = { newText -> 130 | text = newText 131 | } 132 | ) 133 | var fullWidthCheckedState by remember { mutableStateOf(false) } 134 | var fullHeightCheckedState by remember { mutableStateOf(false) } 135 | Row { 136 | Row(modifier = Modifier.weight(1f), 137 | horizontalArrangement = Arrangement.Center, 138 | verticalAlignment = Alignment.CenterVertically 139 | ) { 140 | Checkbox( 141 | checked = fullWidthCheckedState, 142 | onCheckedChange = { fullWidthCheckedState = it } 143 | ) 144 | Text("Full Width") 145 | } 146 | Row(modifier = Modifier.weight(1f), 147 | horizontalArrangement = Arrangement.Center, 148 | verticalAlignment = Alignment.CenterVertically 149 | ) { 150 | Checkbox( 151 | checked = fullHeightCheckedState, 152 | onCheckedChange = { fullHeightCheckedState = it } 153 | ) 154 | Text("Full Height") 155 | } 156 | } 157 | Column( 158 | horizontalAlignment = Alignment.CenterHorizontally, 159 | verticalArrangement = Arrangement.Center, 160 | modifier = Modifier.fillMaxSize() 161 | ) { 162 | BoxLayout( 163 | boxSize.dp, 164 | minSize.dp, 165 | maxSize.dp, 166 | layoutSizeChange.dp, 167 | placementX.dp, 168 | placementY.dp 169 | ) { 170 | val modifier = Modifier 171 | .conditional(fullWidthCheckedState) { 172 | fillMaxWidth() 173 | } 174 | .conditional(fullHeightCheckedState) { 175 | fillMaxHeight() 176 | } 177 | Text(text.text, modifier = modifier) 178 | } 179 | } 180 | } 181 | } 182 | 183 | @Composable 184 | private fun BoxLayout( 185 | size: Dp, 186 | minSize: Dp, 187 | maxSize: Dp, 188 | layoutSizeChange: Dp, 189 | placementX: Dp, 190 | placementY: Dp, 191 | content: @Composable BoxScope.() -> Unit = {} 192 | ) { Box(modifier = Modifier.size(size).background(Color.Yellow)) { 193 | 194 | Box(modifier = Modifier 195 | .size(size) 196 | .background(GrayAlpha) 197 | .layout { measurable, constraints -> 198 | // Measure 199 | val looseConstraints = constraints.copy( 200 | minWidth = minSize.roundToPx(), 201 | maxWidth = maxSize.roundToPx(), 202 | minHeight = minSize.roundToPx(), 203 | maxHeight = maxSize.roundToPx(), 204 | ) 205 | val placeable = measurable.measure(looseConstraints) 206 | 207 | // Layout 208 | layout( 209 | constraints.maxWidth + layoutSizeChange.roundToPx(), 210 | constraints.maxHeight + layoutSizeChange.roundToPx() 211 | ) { 212 | placeable.place(placementX.roundToPx(), placementY.roundToPx()) 213 | } 214 | } 215 | .border(1.dp, Color.Red), 216 | content = content 217 | ) 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/LayoutModifierSizeExperimentActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.border 8 | import androidx.compose.foundation.layout.Arrangement 9 | import androidx.compose.foundation.layout.Box 10 | import androidx.compose.foundation.layout.BoxScope 11 | import androidx.compose.foundation.layout.Column 12 | import androidx.compose.foundation.layout.Row 13 | import androidx.compose.foundation.layout.fillMaxHeight 14 | import androidx.compose.foundation.layout.fillMaxSize 15 | import androidx.compose.foundation.layout.fillMaxWidth 16 | import androidx.compose.foundation.layout.height 17 | import androidx.compose.foundation.layout.width 18 | import androidx.compose.material3.MaterialTheme 19 | import androidx.compose.material3.Slider 20 | import androidx.compose.material3.Surface 21 | import androidx.compose.material3.Text 22 | import androidx.compose.runtime.Composable 23 | import androidx.compose.runtime.getValue 24 | import androidx.compose.runtime.mutableStateOf 25 | import androidx.compose.runtime.remember 26 | import androidx.compose.runtime.setValue 27 | import androidx.compose.ui.Alignment 28 | import androidx.compose.ui.Alignment.Companion.CenterHorizontally 29 | import androidx.compose.ui.Modifier 30 | import androidx.compose.ui.graphics.Color 31 | import androidx.compose.ui.layout.layout 32 | import androidx.compose.ui.unit.Dp 33 | import androidx.compose.ui.unit.dp 34 | import androidx.compose.ui.unit.offset 35 | import com.example.customlayout.ui.theme.CustomLayoutTheme 36 | 37 | class LayoutModifierSizeExperimentActivity : ComponentActivity() { 38 | override fun onCreate(savedInstanceState: Bundle?) { 39 | super.onCreate(savedInstanceState) 40 | setContent { 41 | CustomLayoutTheme { 42 | // A surface container using the 'background' color from the theme 43 | Surface( 44 | modifier = Modifier.fillMaxSize(), 45 | color = MaterialTheme.colorScheme.background 46 | ) { 47 | Greeting() 48 | } 49 | } 50 | } 51 | } 52 | 53 | @Composable 54 | fun Greeting() { 55 | Column( 56 | horizontalAlignment = Alignment.CenterHorizontally, 57 | ) { 58 | val textWidth = 180.dp 59 | var boxSize by remember { mutableStateOf(150) } 60 | var parentSizeX by remember { mutableStateOf(0.9f) } 61 | var parentSizeY by remember { mutableStateOf(0.9f) } 62 | Row (verticalAlignment = Alignment.CenterVertically) { 63 | Text(text = "X Ratio: ${parentSizeX.format(2)}", modifier = Modifier.width(textWidth)) 64 | Slider( 65 | value = parentSizeX, 66 | onValueChange = { parentSizeX = it }, 67 | valueRange = 0f..1f 68 | ) 69 | } 70 | Row (verticalAlignment = Alignment.CenterVertically) { 71 | Text(text = "Y Ratio ${parentSizeY.format(2)}", modifier = Modifier.width(textWidth)) 72 | Slider( 73 | value = parentSizeY, 74 | onValueChange = { parentSizeY = it }, 75 | valueRange = 0f..1f 76 | ) 77 | } 78 | Row (verticalAlignment = Alignment.CenterVertically) { 79 | Text(text = "Size: $boxSize", modifier = Modifier.width(textWidth)) 80 | Slider( 81 | value = boxSize.toFloat(), 82 | onValueChange = { boxSize = it.toInt() }, 83 | valueRange = 0f..300f 84 | ) 85 | } 86 | var layoutSizeChange by remember { mutableStateOf(0) } 87 | Row (verticalAlignment = Alignment.CenterVertically) { 88 | Text(text = "Layout's Change: $layoutSizeChange", modifier = Modifier.width(textWidth)) 89 | Slider( 90 | value = layoutSizeChange.toFloat(), 91 | onValueChange = { layoutSizeChange = it.toInt() }, 92 | valueRange = -150f..150f 93 | ) 94 | } 95 | var constraintOffSet by remember { mutableStateOf(0) } 96 | Row (verticalAlignment = Alignment.CenterVertically) { 97 | Text(text = "Constraint Offset: $constraintOffSet", modifier = Modifier.width(textWidth)) 98 | Slider( 99 | value = constraintOffSet.toFloat(), 100 | onValueChange = { constraintOffSet = it.toInt() }, 101 | valueRange = -150f..150f 102 | ) 103 | } 104 | var placementX by remember { mutableStateOf(0) } 105 | Row (verticalAlignment = Alignment.CenterVertically) { 106 | Text(text = "Placement X: $placementX", modifier = Modifier.width(textWidth)) 107 | Slider( 108 | value = placementX.toFloat(), 109 | onValueChange = { placementX = it.toInt() }, 110 | valueRange = -150f..150f 111 | ) 112 | } 113 | var placementY by remember { mutableStateOf(0) } 114 | Row (verticalAlignment = Alignment.CenterVertically) { 115 | Text(text = "Placement Y: $placementY", modifier = Modifier.width(textWidth)) 116 | Slider( 117 | value = placementY.toFloat(), 118 | onValueChange = { placementY = it.toInt() }, 119 | valueRange = -150f..150f 120 | ) 121 | } 122 | Column( 123 | horizontalAlignment = CenterHorizontally, 124 | verticalArrangement = Arrangement.Center, 125 | modifier = Modifier.fillMaxSize() 126 | ) { 127 | Column( 128 | horizontalAlignment = CenterHorizontally, 129 | verticalArrangement = Arrangement.Center, 130 | modifier = Modifier 131 | .fillMaxWidth(parentSizeX) 132 | .fillMaxHeight(parentSizeY) 133 | .background(Color.LightGray) 134 | ) { 135 | val size = boxSize.dp 136 | BoxLayout( 137 | size, 138 | Color.Red, 139 | layoutSizeChange.dp, 140 | constraintOffSet.dp, 141 | placementX.dp, 142 | placementY.dp 143 | ) { 144 | BoxLayout( 145 | size, 146 | Color.Green, 147 | layoutSizeChange.dp, 148 | constraintOffSet.dp, 149 | placementX.dp, 150 | placementY.dp 151 | ) { 152 | BoxLayout( 153 | size, 154 | Color.Blue, 155 | layoutSizeChange.dp, 156 | constraintOffSet.dp, 157 | placementX.dp, 158 | placementY.dp 159 | ) { 160 | BoxLayout( 161 | size, 162 | Color.Magenta, 163 | layoutSizeChange.dp, 164 | constraintOffSet.dp, 165 | placementX.dp, 166 | placementY.dp 167 | ) { 168 | val textModifier = Modifier.border(1.dp, Color.Cyan) 169 | Text("Hello There", modifier = textModifier) 170 | } 171 | } 172 | } 173 | } 174 | } 175 | } 176 | } 177 | 178 | } 179 | 180 | @Composable 181 | private fun BoxLayout( 182 | size: Dp, 183 | borderColor: Color, 184 | layoutSizeChange: Dp, 185 | constraintOffSet: Dp, 186 | placementX: Dp, 187 | placementY: Dp, 188 | content: @Composable BoxScope.() -> Unit = {} 189 | ) { 190 | Box(modifier = Modifier 191 | .width(size) 192 | .height(size) 193 | .layout { measurable, constraints -> 194 | val placeable = measurable.measure( 195 | constraints.offset( 196 | constraintOffSet.roundToPx(), 197 | constraintOffSet.roundToPx() 198 | ) 199 | ) 200 | layout( 201 | placeable.width + layoutSizeChange.roundToPx(), 202 | placeable.height + layoutSizeChange.roundToPx() 203 | ) { 204 | placeable.place(placementX.roundToPx(), placementY.roundToPx()) 205 | } 206 | } 207 | .border(1.dp, borderColor), content = content) 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/LayoutSizeExperimentActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.border 8 | import androidx.compose.foundation.layout.Arrangement 9 | import androidx.compose.foundation.layout.Box 10 | import androidx.compose.foundation.layout.BoxScope 11 | import androidx.compose.foundation.layout.Column 12 | import androidx.compose.foundation.layout.Row 13 | import androidx.compose.foundation.layout.fillMaxHeight 14 | import androidx.compose.foundation.layout.fillMaxSize 15 | import androidx.compose.foundation.layout.fillMaxWidth 16 | import androidx.compose.foundation.layout.height 17 | import androidx.compose.foundation.layout.size 18 | import androidx.compose.foundation.layout.width 19 | import androidx.compose.material3.MaterialTheme 20 | import androidx.compose.material3.Slider 21 | import androidx.compose.material3.Surface 22 | import androidx.compose.material3.Text 23 | import androidx.compose.runtime.Composable 24 | import androidx.compose.runtime.mutableStateOf 25 | import androidx.compose.runtime.getValue 26 | import androidx.compose.runtime.remember 27 | import androidx.compose.runtime.setValue 28 | import androidx.compose.ui.Alignment 29 | import androidx.compose.ui.Modifier 30 | import androidx.compose.ui.graphics.Color 31 | import androidx.compose.ui.layout.Layout 32 | import androidx.compose.ui.unit.Dp 33 | import androidx.compose.ui.unit.dp 34 | import androidx.compose.ui.unit.offset 35 | import com.example.customlayout.ui.theme.CustomLayoutTheme 36 | 37 | class LayoutSizeExperimentActivity : ComponentActivity() { 38 | override fun onCreate(savedInstanceState: Bundle?) { 39 | super.onCreate(savedInstanceState) 40 | setContent { 41 | CustomLayoutTheme { 42 | // A surface container using the 'background' color from the theme 43 | Surface( 44 | modifier = Modifier.fillMaxSize(), 45 | color = MaterialTheme.colorScheme.background 46 | ) { 47 | Greeting() 48 | } 49 | } 50 | } 51 | } 52 | 53 | @Composable 54 | fun Greeting() { 55 | Column( 56 | horizontalAlignment = Alignment.CenterHorizontally, 57 | ) { 58 | val textWidth = 180.dp 59 | var boxSize by remember { mutableStateOf(150) } 60 | var parentSizeX by remember { mutableStateOf(0.9f) } 61 | var parentSizeY by remember { mutableStateOf(0.9f) } 62 | Row(verticalAlignment = Alignment.CenterVertically) { 63 | Text( 64 | text = "X Ratio: ${parentSizeX.format(2)}", 65 | modifier = Modifier.width(textWidth) 66 | ) 67 | Slider( 68 | value = parentSizeX, 69 | onValueChange = { parentSizeX = it }, 70 | valueRange = 0f..1f 71 | ) 72 | } 73 | Row(verticalAlignment = Alignment.CenterVertically) { 74 | Text( 75 | text = "Y Ratio ${parentSizeY.format(2)}", 76 | modifier = Modifier.width(textWidth) 77 | ) 78 | Slider( 79 | value = parentSizeY, 80 | onValueChange = { parentSizeY = it }, 81 | valueRange = 0f..1f 82 | ) 83 | } 84 | Row(verticalAlignment = Alignment.CenterVertically) { 85 | Text(text = "Size: $boxSize", modifier = Modifier.width(textWidth)) 86 | Slider( 87 | value = boxSize.toFloat(), 88 | onValueChange = { boxSize = it.toInt() }, 89 | valueRange = 0f..300f 90 | ) 91 | } 92 | var layoutSizeChange by remember { mutableStateOf(0) } 93 | Row(verticalAlignment = Alignment.CenterVertically) { 94 | Text( 95 | text = "Layout's Change: $layoutSizeChange", 96 | modifier = Modifier.width(textWidth) 97 | ) 98 | Slider( 99 | value = layoutSizeChange.toFloat(), 100 | onValueChange = { layoutSizeChange = it.toInt() }, 101 | valueRange = -150f..150f 102 | ) 103 | } 104 | var constraintOffSet by remember { mutableStateOf(0) } 105 | Row(verticalAlignment = Alignment.CenterVertically) { 106 | Text( 107 | text = "Constraint Offset: $constraintOffSet", 108 | modifier = Modifier.width(textWidth) 109 | ) 110 | Slider( 111 | value = constraintOffSet.toFloat(), 112 | onValueChange = { constraintOffSet = it.toInt() }, 113 | valueRange = -150f..150f 114 | ) 115 | } 116 | var placementX by remember { mutableStateOf(0) } 117 | Row(verticalAlignment = Alignment.CenterVertically) { 118 | Text(text = "Placement X: $placementX", modifier = Modifier.width(textWidth)) 119 | Slider( 120 | value = placementX.toFloat(), 121 | onValueChange = { placementX = it.toInt() }, 122 | valueRange = -150f..150f 123 | ) 124 | } 125 | var placementY by remember { mutableStateOf(0) } 126 | Row(verticalAlignment = Alignment.CenterVertically) { 127 | Text(text = "Placement Y: $placementY", modifier = Modifier.width(textWidth)) 128 | Slider( 129 | value = placementY.toFloat(), 130 | onValueChange = { placementY = it.toInt() }, 131 | valueRange = -150f..150f 132 | ) 133 | } 134 | Column( 135 | horizontalAlignment = Alignment.CenterHorizontally, 136 | verticalArrangement = Arrangement.Center, 137 | modifier = Modifier.fillMaxSize() 138 | ) { 139 | Column( 140 | horizontalAlignment = Alignment.CenterHorizontally, 141 | verticalArrangement = Arrangement.Center, 142 | modifier = Modifier 143 | .fillMaxWidth(parentSizeX) 144 | .fillMaxHeight(parentSizeY) 145 | .background(Color.LightGray) 146 | ) { 147 | val size = boxSize.dp 148 | BoxLayout( 149 | size, 150 | Color.Red, 151 | layoutSizeChange.dp, 152 | constraintOffSet.dp, 153 | placementX.dp, 154 | placementY.dp 155 | ) { 156 | BoxLayout( 157 | size, 158 | Color.Green, 159 | layoutSizeChange.dp, 160 | constraintOffSet.dp, 161 | placementX.dp, 162 | placementY.dp 163 | ) { 164 | BoxLayout( 165 | size, 166 | Color.Blue, 167 | layoutSizeChange.dp, 168 | constraintOffSet.dp, 169 | placementX.dp, 170 | placementY.dp 171 | ) { 172 | BoxLayout( 173 | size, 174 | Color.Magenta, 175 | layoutSizeChange.dp, 176 | constraintOffSet.dp, 177 | placementX.dp, 178 | placementY.dp 179 | ) { 180 | val textModifier = Modifier.border(1.dp, Color.Blue) 181 | Text("Hello There", modifier = textModifier) 182 | Text("Something Here", modifier = textModifier) 183 | Text("Test it one", modifier = textModifier) 184 | Text("Sometimes", modifier = textModifier) 185 | Text("Short", modifier = textModifier) 186 | } 187 | } 188 | } 189 | } 190 | } 191 | } 192 | } 193 | } 194 | 195 | @Composable 196 | fun BoxLayout( 197 | size: Dp, 198 | borderColor: Color, 199 | layoutSizeChange: Dp, 200 | constraintOffSet: Dp, 201 | placementX: Dp, 202 | placementY: Dp, 203 | content: @Composable () -> Unit = {} 204 | ) { 205 | Layout( 206 | modifier = Modifier 207 | .width(size) 208 | .height(size) 209 | .border(1.dp, borderColor), 210 | content = content 211 | ) { measurables, constraints -> 212 | val placaebles = measurables.map { measurable -> 213 | val looseConstraints = constraints.copy( 214 | minWidth = 0, 215 | minHeight = 0, 216 | ) 217 | measurable.measure(constraints = looseConstraints.offset( 218 | constraintOffSet.roundToPx(), 219 | constraintOffSet.roundToPx() 220 | )) 221 | } 222 | 223 | layout( 224 | placaebles.maxOf { it.width } + layoutSizeChange.roundToPx(), 225 | placaebles.sumOf { it.height } + layoutSizeChange.roundToPx() 226 | ) { 227 | var y = placementY.roundToPx() 228 | placaebles.forEach { placeable -> 229 | placeable.place(placementX.roundToPx(), y) 230 | y += placeable.height 231 | } 232 | } 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import androidx.activity.ComponentActivity 6 | import androidx.activity.compose.setContent 7 | import androidx.compose.foundation.layout.Arrangement 8 | import androidx.compose.foundation.layout.Column 9 | import androidx.compose.foundation.layout.fillMaxSize 10 | import androidx.compose.material3.Button 11 | import androidx.compose.material3.MaterialTheme 12 | import androidx.compose.material3.Surface 13 | import androidx.compose.material3.Text 14 | import androidx.compose.runtime.Composable 15 | import androidx.compose.ui.Alignment 16 | import androidx.compose.ui.Modifier 17 | import androidx.compose.ui.unit.dp 18 | import com.example.customlayout.ui.theme.CustomLayoutTheme 19 | import kotlin.reflect.KClass 20 | 21 | class MainActivity : ComponentActivity() { 22 | override fun onCreate(savedInstanceState: Bundle?) { 23 | super.onCreate(savedInstanceState) 24 | setContent { 25 | CustomLayoutTheme { 26 | // A surface container using the 'background' color from the theme 27 | Surface( 28 | modifier = Modifier.fillMaxSize(), 29 | color = MaterialTheme.colorScheme.background 30 | ) { 31 | Greeting() 32 | } 33 | } 34 | } 35 | } 36 | 37 | @Composable 38 | private fun Greeting(modifier: Modifier = Modifier) { 39 | Column( 40 | horizontalAlignment = Alignment.CenterHorizontally, 41 | verticalArrangement = Arrangement.spacedBy(16.dp) 42 | ) { 43 | ButtonLauncher(AutoWidthSortColumnActivity::class) 44 | ButtonLauncher(LayoutSizeExperimentActivity::class) 45 | ButtonLauncher(LayoutModifierSizeExperimentActivity::class) 46 | ButtonLauncher(LayoutModifierSingleExperimentActivity::class) 47 | ButtonLauncher(DividerColumnExperimentActivity::class) 48 | ButtonLauncher(DividerLazyColumnExperimentActivity::class) 49 | ButtonLauncher(NegativePaddingExperimentActivity::class) 50 | ButtonLauncher(BoxAsDividerNegativePaddingBehaviorActivity::class) 51 | ButtonLauncher(DividerLikeOnlyColumnActivity::class) 52 | ButtonLauncher(DividerLikeColumnActivity::class) 53 | ButtonLauncher(DividerLikeLazyColumnActivity::class) 54 | } 55 | } 56 | 57 | @Composable 58 | fun ButtonLauncher(kclass: KClass<*>) { 59 | Button(onClick = { 60 | startActivity(Intent(this, kclass.java)) 61 | }) { 62 | Text(kclass.simpleName.toString()) 63 | } 64 | } 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/NegativePaddingExperimentActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.foundation.background 7 | import androidx.compose.foundation.layout.Arrangement 8 | import androidx.compose.foundation.layout.Column 9 | import androidx.compose.foundation.layout.Row 10 | import androidx.compose.foundation.layout.fillMaxHeight 11 | import androidx.compose.foundation.layout.fillMaxSize 12 | import androidx.compose.foundation.layout.fillMaxWidth 13 | import androidx.compose.foundation.layout.height 14 | import androidx.compose.foundation.layout.offset 15 | import androidx.compose.foundation.layout.padding 16 | import androidx.compose.foundation.layout.requiredWidth 17 | import androidx.compose.foundation.layout.width 18 | import androidx.compose.material3.Divider 19 | import androidx.compose.material3.MaterialTheme 20 | import androidx.compose.material3.Slider 21 | import androidx.compose.material3.Surface 22 | import androidx.compose.material3.Text 23 | import androidx.compose.runtime.Composable 24 | import androidx.compose.runtime.getValue 25 | import androidx.compose.runtime.mutableStateOf 26 | import androidx.compose.runtime.remember 27 | import androidx.compose.runtime.setValue 28 | import androidx.compose.ui.Alignment 29 | import androidx.compose.ui.Modifier 30 | import androidx.compose.ui.graphics.Color 31 | import androidx.compose.ui.layout.layout 32 | import androidx.compose.ui.text.style.TextAlign 33 | import androidx.compose.ui.unit.dp 34 | import androidx.compose.ui.unit.offset 35 | import com.example.customlayout.ui.theme.CustomLayoutTheme 36 | 37 | class NegativePaddingExperimentActivity : ComponentActivity() { 38 | override fun onCreate(savedInstanceState: Bundle?) { 39 | super.onCreate(savedInstanceState) 40 | setContent { 41 | CustomLayoutTheme { 42 | // A surface container using the 'background' color from the theme 43 | Surface( 44 | modifier = Modifier.fillMaxSize(), 45 | color = MaterialTheme.colorScheme.background 46 | ) { 47 | Greeting() 48 | } 49 | } 50 | } 51 | } 52 | 53 | @Composable 54 | fun Greeting() { 55 | Column( 56 | horizontalAlignment = Alignment.CenterHorizontally, 57 | ) { 58 | val textWidth = 180.dp 59 | var parentWidth by remember { mutableStateOf(300) } 60 | Row(verticalAlignment = Alignment.CenterVertically) { 61 | Text(text = "Parent Width: $parentWidth", modifier = Modifier.width(textWidth)) 62 | Slider( 63 | value = parentWidth.toFloat(), 64 | onValueChange = { parentWidth = it.toInt() }, 65 | valueRange = 0f..300f 66 | ) 67 | } 68 | var parentPadding by remember { mutableStateOf(0) } 69 | Row(verticalAlignment = Alignment.CenterVertically) { 70 | Text(text = "Parent Padding: $parentPadding", modifier = Modifier.width(textWidth)) 71 | Slider( 72 | value = parentPadding.toFloat(), 73 | onValueChange = { parentPadding = it.toInt() }, 74 | valueRange = 0f..64f 75 | ) 76 | } 77 | var dividersPadding by remember { mutableStateOf(0) } 78 | Row(verticalAlignment = Alignment.CenterVertically) { 79 | Text( 80 | text = "Dividers' Padding: $dividersPadding", 81 | modifier = Modifier.width(textWidth) 82 | ) 83 | Slider( 84 | value = dividersPadding.toFloat(), 85 | onValueChange = { dividersPadding = it.toInt() }, 86 | valueRange = -150f..150f 87 | ) 88 | } 89 | Column( 90 | verticalArrangement = Arrangement.spacedBy(16.dp), 91 | modifier = Modifier 92 | .width(parentWidth.dp) 93 | .fillMaxHeight() 94 | .background(Color.Yellow) 95 | .padding(parentPadding.dp) 96 | ) { 97 | Text("0. Reference Divider") 98 | Divider(modifier = Modifier.height(20.dp)) 99 | Text("1. Offset Padding") 100 | DividerOffset(dividersPadding) 101 | Text("2. Required Width Padding") 102 | DividerRequiredWidth(parentWidth, parentPadding, dividersPadding) 103 | Text("3. Layout Constraint Only Padding") 104 | DividerLayoutConstraint(dividersPadding) 105 | Text("4. Layout Managed Padding") 106 | DividerLayoutManaged(dividersPadding) 107 | } 108 | } 109 | } 110 | 111 | @Composable 112 | private fun DividerLayoutManaged(dividersPadding: Int) { 113 | Divider(modifier = Modifier 114 | .height(20.dp) 115 | .layout { measurable, constraints -> 116 | val placeable = 117 | measurable.measure(constraints.offset((-dividersPadding * 2).dp.roundToPx())) 118 | layout( 119 | placeable.width + (dividersPadding * 2).dp.roundToPx(), 120 | placeable.height 121 | ) { placeable.place(0 + dividersPadding.dp.roundToPx(), 0) } 122 | } 123 | ) 124 | } 125 | 126 | @Composable 127 | private fun DividerLayoutConstraint(dividersPadding: Int) { 128 | Divider(modifier = Modifier 129 | .height(20.dp) 130 | .layout { measurable, constraints -> 131 | val placeable = 132 | measurable.measure(constraints.offset((-dividersPadding * 2).dp.roundToPx())) 133 | layout( 134 | placeable.width, 135 | placeable.height 136 | ) { placeable.place(0, 0) } 137 | } 138 | ) 139 | } 140 | 141 | @Composable 142 | private fun DividerRequiredWidth( 143 | parentWidth: Int, 144 | parentPadding: Int, 145 | dividersPadding: Int 146 | ) { 147 | Divider( 148 | modifier = Modifier 149 | .height(20.dp) 150 | .requiredWidth( 151 | width = (parentWidth.dp - parentPadding.dp * 2 - dividersPadding.dp * 2), 152 | ) 153 | ) 154 | } 155 | 156 | @Composable 157 | private fun DividerOffset(dividersPadding: Int) { 158 | Divider(modifier = Modifier 159 | .height((20.dp)) 160 | .offset(dividersPadding.dp)) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/Util.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout 2 | 3 | import androidx.compose.ui.Modifier 4 | import androidx.compose.ui.graphics.Color 5 | 6 | fun Float.format(digits: Int) = "%.${digits}f".format(this) 7 | 8 | fun Modifier.conditional(condition : Boolean, modifier : Modifier.() -> Modifier) : Modifier { 9 | return if (condition) { 10 | then(modifier(Modifier)) 11 | } else { 12 | this 13 | } 14 | } 15 | 16 | val GrayAlpha: Color 17 | get() = Color(148, 148, 148, 128) 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.graphics.toArgb 14 | import androidx.compose.ui.platform.LocalContext 15 | import androidx.compose.ui.platform.LocalView 16 | import androidx.core.view.WindowCompat 17 | 18 | private val DarkColorScheme = darkColorScheme( 19 | primary = Purple80, 20 | secondary = PurpleGrey80, 21 | tertiary = Pink80 22 | ) 23 | 24 | private val LightColorScheme = lightColorScheme( 25 | primary = Purple40, 26 | secondary = PurpleGrey40, 27 | tertiary = Pink40 28 | 29 | /* Other default colors to override 30 | background = Color(0xFFFFFBFE), 31 | surface = Color(0xFFFFFBFE), 32 | onPrimary = Color.White, 33 | onSecondary = Color.White, 34 | onTertiary = Color.White, 35 | onBackground = Color(0xFF1C1B1F), 36 | onSurface = Color(0xFF1C1B1F), 37 | */ 38 | ) 39 | 40 | @Composable 41 | fun CustomLayoutTheme( 42 | darkTheme: Boolean = isSystemInDarkTheme(), 43 | // Dynamic color is available on Android 12+ 44 | dynamicColor: Boolean = true, 45 | content: @Composable () -> Unit 46 | ) { 47 | val colorScheme = when { 48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 49 | val context = LocalContext.current 50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 51 | } 52 | 53 | darkTheme -> DarkColorScheme 54 | else -> LightColorScheme 55 | } 56 | val view = LocalView.current 57 | if (!view.isInEditMode) { 58 | SideEffect { 59 | val window = (view.context as Activity).window 60 | window.statusBarColor = colorScheme.primary.toArgb() 61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme 62 | } 63 | } 64 | 65 | MaterialTheme( 66 | colorScheme = colorScheme, 67 | typography = Typography, 68 | content = content 69 | ) 70 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/customlayout/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.example.customlayout.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elye/demo_android_jetpack_compose_custom_layout/a0b4809293f9b60a03319e23284030c9dfdb36c5/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elye/demo_android_jetpack_compose_custom_layout/a0b4809293f9b60a03319e23284030c9dfdb36c5/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elye/demo_android_jetpack_compose_custom_layout/a0b4809293f9b60a03319e23284030c9dfdb36c5/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elye/demo_android_jetpack_compose_custom_layout/a0b4809293f9b60a03319e23284030c9dfdb36c5/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elye/demo_android_jetpack_compose_custom_layout/a0b4809293f9b60a03319e23284030c9dfdb36c5/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elye/demo_android_jetpack_compose_custom_layout/a0b4809293f9b60a03319e23284030c9dfdb36c5/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elye/demo_android_jetpack_compose_custom_layout/a0b4809293f9b60a03319e23284030c9dfdb36c5/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elye/demo_android_jetpack_compose_custom_layout/a0b4809293f9b60a03319e23284030c9dfdb36c5/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elye/demo_android_jetpack_compose_custom_layout/a0b4809293f9b60a03319e23284030c9dfdb36c5/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elye/demo_android_jetpack_compose_custom_layout/a0b4809293f9b60a03319e23284030c9dfdb36c5/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CustomLayout 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |