├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── rodrigodominguez │ │ └── motionlayouttalk │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── rodrigodominguez │ │ │ └── motionlayouttalk │ │ │ ├── MainActivity.kt │ │ │ ├── abcconstraintlayout │ │ │ └── ConstraintLayoutABC.kt │ │ │ ├── abcmotion │ │ │ ├── CollapseScreenComposable.kt │ │ │ ├── MotionLayoutABC.kt │ │ │ └── MotionLayoutYoutubeDemo.kt │ │ │ └── ui │ │ │ └── theme │ │ │ ├── Color.kt │ │ │ ├── Shape.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 │ │ ├── raw │ │ ├── constraint_set.json5 │ │ ├── motion_scene1.json5 │ │ ├── motion_scene1_collapse.json5 │ │ ├── motion_scene_abc.json5 │ │ └── motion_scene_base.json5 │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── rodrigodominguez │ └── motionlayouttalk │ └── 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ComposeMotionLayoutSamples 2 | 3 | 4 | ## Sample 1 5 | drawing 6 | 7 | 8 | ## Sample 2 9 | drawing 10 | 11 | 12 | ## Find me at: 13 | [![Medium](https://img.shields.io/badge/Medium-@rodrigomartind-9146FF?style=for-the-badge&logo=medium&logoColor=white&labelColor=101010)](https://rodrigomartind.medium.com/) 14 | [![Twitter](https://img.shields.io/badge/Twitter-@rodrigomartind-1DA1F2?style=for-the-badge&logo=twitter&logoColor=white&labelColor=101010)](https://twitter.com/RodrigoMartinD) 15 | [![Instagram](https://img.shields.io/badge/Instagram-@rodrigomartind-E4405F?style=for-the-badge&logo=instagram&logoColor=white&labelColor=101010)](https://instagram.com/rodrigomartind) 16 |
17 | [![Facebook](https://img.shields.io/badge/Facebook-@rodrigo.martin.dom-1877F2?style=for-the-badge&logo=facebook&logoColor=white&labelColor=101010)](https://facebook.com/Rodrigo.Martin.Dom) 18 | [![LinkedIn](https://img.shields.io/badge/LinkedIn-Rodrigo_Dominguez-0077B5?style=for-the-badge&logo=linkedin&logoColor=white&labelColor=101010)](https://www.linkedin.com/in/rodrigo-martin-dominguez-463b5a33/) 19 |
20 | [![YouTube](https://img.shields.io/badge/YouTube-Hermanos_Binarios-FF0000?style=for-the-badge&logo=youtube&logoColor=white&labelColor=101010)](https://www.youtube.com/channel/UCBs51OPI3dU1hv9yQZ6BOHA) 21 | 22 | ## Contact me: 23 | [![Email](https://img.shields.io/badge/rodrigomartind@gmail.com-my_personal_email-EC5252?style=for-the-badge&logo=gmail&logoColor=white&labelColor=101010)](mailto:rodrigomartind@gmail.com) 24 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | compileSdk 31 8 | 9 | defaultConfig { 10 | applicationId "com.rodrigodominguez.motionlayouttalk" 11 | minSdk 23 12 | targetSdk 31 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | vectorDrawables { 18 | useSupportLibrary true 19 | } 20 | } 21 | 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | compileOptions { 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | targetCompatibility JavaVersion.VERSION_1_8 31 | } 32 | kotlinOptions { 33 | jvmTarget = '1.8' 34 | useIR = true 35 | } 36 | buildFeatures { 37 | compose true 38 | } 39 | composeOptions { 40 | kotlinCompilerExtensionVersion compose_version 41 | kotlinCompilerVersion '1.5.21' 42 | } 43 | packagingOptions { 44 | resources { 45 | excludes += '/META-INF/{AL2.0,LGPL2.1}' 46 | } 47 | } 48 | } 49 | 50 | dependencies { 51 | 52 | implementation 'androidx.core:core-ktx:1.7.0' 53 | implementation 'androidx.appcompat:appcompat:1.4.1' 54 | implementation 'com.google.android.material:material:1.5.0' 55 | implementation "androidx.compose.ui:ui:$compose_version" 56 | implementation "androidx.compose.material:material:$compose_version" 57 | implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" 58 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1' 59 | implementation 'androidx.activity:activity-compose:1.4.0' 60 | implementation("androidx.constraintlayout:constraintlayout-compose:1.0.0") 61 | implementation 'androidx.constraintlayout:constraintlayout-core:1.0.3' 62 | testImplementation 'junit:junit:4.+' 63 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 64 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 65 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" 66 | debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" 67 | } -------------------------------------------------------------------------------- /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/rodrigodominguez/motionlayouttalk/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.rodrigodominguez.motionlayouttalk 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.rodrigodominguez.motionlayouttalk", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/rodrigodominguez/motionlayouttalk/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.rodrigodominguez.motionlayouttalk 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.animation.core.animateFloatAsState 7 | import androidx.compose.animation.core.tween 8 | import androidx.compose.foundation.background 9 | import androidx.compose.foundation.clickable 10 | import androidx.compose.foundation.gestures.Orientation 11 | import androidx.compose.foundation.layout.Box 12 | import androidx.compose.foundation.layout.fillMaxSize 13 | import androidx.compose.foundation.shape.CircleShape 14 | import androidx.compose.material.ExperimentalMaterialApi 15 | import androidx.compose.material.FractionalThreshold 16 | import androidx.compose.material.MaterialTheme 17 | import androidx.compose.material.Surface 18 | import androidx.compose.material.Text 19 | import androidx.compose.material.rememberSwipeableState 20 | import androidx.compose.material.swipeable 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.Modifier 27 | import androidx.compose.ui.draw.clip 28 | import androidx.compose.ui.graphics.Color 29 | import androidx.compose.ui.layout.layoutId 30 | import androidx.compose.ui.layout.onSizeChanged 31 | import androidx.compose.ui.platform.LocalDensity 32 | import androidx.compose.ui.tooling.preview.Preview 33 | import androidx.compose.ui.unit.dp 34 | import androidx.compose.ui.unit.sp 35 | import androidx.constraintlayout.compose.ExperimentalMotionApi 36 | import androidx.constraintlayout.compose.MotionLayout 37 | import androidx.constraintlayout.compose.MotionLayoutDebugFlags 38 | import androidx.constraintlayout.compose.MotionScene 39 | import com.rodrigodominguez.motionlayouttalk.abcmotion.YoutubeDemoComposable 40 | import com.rodrigodominguez.motionlayouttalk.ui.theme.MotionLayoutTalkTheme 41 | import java.util.* 42 | import kotlin.math.roundToInt 43 | 44 | class MainActivity : ComponentActivity() { 45 | 46 | 47 | @ExperimentalMaterialApi 48 | @ExperimentalMotionApi 49 | override fun onCreate(savedInstanceState: Bundle?) { 50 | super.onCreate(savedInstanceState) 51 | setContent { 52 | MotionLayoutTalkTheme { 53 | // A surface container using the 'background' color from the theme 54 | Surface(color = MaterialTheme.colors.background) { 55 | YoutubeDemoComposable() 56 | } 57 | } 58 | } 59 | } 60 | } 61 | 62 | 63 | @ExperimentalMotionApi 64 | @ExperimentalMaterialApi 65 | @Composable 66 | fun MotionSampleSwipe() { 67 | var componentHeight by remember { mutableStateOf(100f) } 68 | val swipeableState = rememberSwipeableState("Bottom") 69 | val anchors = mapOf(0f to "Bottom", componentHeight to "Top") 70 | 71 | val mprogress = (swipeableState.offset.value / componentHeight) 72 | 73 | MotionLayout( 74 | motionScene = MotionScene( 75 | """{ 76 | Header: { exportAs: 'motion6'}, 77 | ConstraintSets: { 78 | start: { 79 | box1: { 80 | width: '90%', 81 | height: 90, 82 | start: ['parent', 'start'], 83 | end: ['parent', 'end'], 84 | bottom: ['parent','bottom', 16], 85 | translationZ: 1 86 | } 87 | }, 88 | end: { 89 | box1: { 90 | width: '100%', 91 | height: 220, 92 | start: ['parent', 'start'], 93 | end: ['parent', 'end'], 94 | top: ['parent','top'], 95 | translationZ: 0 96 | } 97 | } 98 | } 99 | }""" 100 | ), 101 | progress = mprogress, 102 | debug = EnumSet.of(MotionLayoutDebugFlags.SHOW_ALL), 103 | modifier = Modifier 104 | .fillMaxSize() 105 | .background(Color.Red) 106 | ) { 107 | Box( 108 | modifier = Modifier 109 | .layoutId("box1") 110 | .background(Color.Green) 111 | .swipeable( 112 | state = swipeableState, 113 | anchors = anchors, 114 | reverseDirection = true, 115 | thresholds = { _, _ -> FractionalThreshold(0.5f) }, 116 | orientation = Orientation.Vertical 117 | ).onSizeChanged { size -> 118 | componentHeight = size.height.toFloat() 119 | } 120 | ) 121 | } 122 | } 123 | 124 | @Preview(showBackground = true) 125 | @Composable 126 | fun DefaultPreview() { 127 | MotionLayoutTalkTheme { 128 | } 129 | } -------------------------------------------------------------------------------- /app/src/main/java/com/rodrigodominguez/motionlayouttalk/abcconstraintlayout/ConstraintLayoutABC.kt: -------------------------------------------------------------------------------- 1 | package com.rodrigodominguez.motionlayouttalk.abcconstraintlayout 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.fillMaxSize 5 | import androidx.compose.material.Button 6 | import androidx.compose.material.Text 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.graphics.Color 10 | import androidx.compose.ui.layout.layoutId 11 | import androidx.compose.ui.tooling.preview.Preview 12 | import androidx.compose.ui.unit.dp 13 | import androidx.compose.ui.unit.sp 14 | import androidx.constraintlayout.compose.ConstraintLayout 15 | import androidx.constraintlayout.compose.ConstraintSet 16 | 17 | @Preview 18 | @Composable 19 | fun ConstraintLayoutSample1() { 20 | ConstraintLayout( 21 | modifier = Modifier 22 | .fillMaxSize() 23 | .background(Color.White) 24 | ) { 25 | val (button, title) = createRefs() 26 | val guideline1 = createGuidelineFromStart(36.dp) 27 | 28 | Text(text = "Title text", 29 | fontSize = 34.sp, 30 | modifier = Modifier.constrainAs(title) { 31 | centerVerticallyTo(parent) 32 | start.linkTo(guideline1) 33 | }) 34 | 35 | Button( 36 | modifier = Modifier.constrainAs(button) { 37 | top.linkTo(title.bottom, margin = 16.dp) 38 | start.linkTo(guideline1) 39 | }, 40 | onClick = { } 41 | ) { 42 | Text(text = "Button") 43 | } 44 | 45 | } 46 | } 47 | 48 | @Composable 49 | fun ConstraintLayoutSample2() { 50 | ConstraintLayout( 51 | ConstraintSet { 52 | val button = createRefFor("button") 53 | val title = createRefFor("title") 54 | val g1 = createGuidelineFromStart(80.dp) 55 | constrain(button) { 56 | top.linkTo(title.bottom, 32.dp) 57 | start.linkTo(g1) 58 | } 59 | constrain(title) { 60 | centerVerticallyTo(parent) 61 | start.linkTo(g1) 62 | } 63 | }, 64 | modifier = Modifier 65 | .fillMaxSize() 66 | .background(Color.White) 67 | ) { 68 | Text( 69 | modifier = Modifier.layoutId("title"), 70 | text = "Title text", fontSize = 44.sp 71 | ) 72 | Button( 73 | modifier = Modifier.layoutId("button"), onClick = { } 74 | ) { 75 | Text(text = "Button") 76 | } 77 | } 78 | } 79 | 80 | @Preview 81 | @Composable 82 | fun ConstraintLayoutSample3() { 83 | ConstraintLayout( 84 | ConstraintSet( 85 | """{ 86 | Debug: { name: 'somename' }, 87 | g1: { type: 'vGuideline', start: 56 }, 88 | button: { 89 | top: ['title', 'bottom', 16], 90 | start: ['g1', 'start'] 91 | }, 92 | title: { 93 | centerVertically: 'parent', 94 | start: ['g1', 'start'] 95 | } 96 | }""" 97 | ), 98 | modifier = Modifier 99 | .fillMaxSize() 100 | .background(Color.White) 101 | ) { 102 | Text( 103 | modifier = Modifier.layoutId("title"), 104 | text = "Title text", fontSize = 44.sp 105 | ) 106 | Button( 107 | modifier = Modifier.layoutId("button"), onClick = { } 108 | ) { 109 | Text(text = "Button") 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /app/src/main/java/com/rodrigodominguez/motionlayouttalk/abcmotion/CollapseScreenComposable.kt: -------------------------------------------------------------------------------- 1 | package com.rodrigodominguez.motionlayouttalk.abcmotion 2 | 3 | import androidx.compose.animation.core.animateFloatAsState 4 | import androidx.compose.animation.core.tween 5 | import androidx.compose.foundation.background 6 | import androidx.compose.foundation.clickable 7 | import androidx.compose.foundation.layout.Box 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.shape.CircleShape 10 | import androidx.compose.foundation.shape.CutCornerShape 11 | import androidx.compose.material.Text 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.runtime.mutableStateOf 15 | import androidx.compose.runtime.remember 16 | import androidx.compose.runtime.setValue 17 | import androidx.compose.ui.Modifier 18 | import androidx.compose.ui.draw.clip 19 | import androidx.compose.ui.graphics.Color 20 | import androidx.compose.ui.layout.layoutId 21 | import androidx.compose.ui.platform.LocalContext 22 | import androidx.compose.ui.tooling.preview.Preview 23 | import androidx.compose.ui.unit.sp 24 | import androidx.constraintlayout.compose.ExperimentalMotionApi 25 | import androidx.constraintlayout.compose.MotionLayout 26 | import androidx.constraintlayout.compose.MotionScene 27 | import com.rodrigodominguez.motionlayouttalk.R 28 | 29 | @ExperimentalMotionApi 30 | @Composable 31 | @Preview 32 | fun CollapseScreenDemo() { 33 | var animateToEnd by remember { 34 | mutableStateOf(false) 35 | } 36 | val progress by animateFloatAsState( 37 | targetValue = if (animateToEnd) 1f else 0f, 38 | animationSpec = tween(3000) 39 | ) 40 | val context = LocalContext.current 41 | val motionScene = remember { 42 | context.resources.openRawResource(R.raw.motion_scene1_collapse) 43 | .readBytes() 44 | .decodeToString() 45 | } 46 | MotionLayout( 47 | motionScene = MotionScene(content = motionScene), 48 | progress = progress, 49 | modifier = Modifier 50 | .fillMaxSize() 51 | .background(Color.Black), 52 | ) { 53 | val contentAttr = motionProperties(id = "content") 54 | val titleAttr = motionProperties(id = "title") 55 | 56 | Box( 57 | modifier = Modifier 58 | .layoutId("content") 59 | .clip( 60 | CutCornerShape( 61 | topStart = contentAttr.value.int("corner").toFloat() 62 | ) 63 | ) 64 | .background(Color.LightGray) 65 | .clickable { animateToEnd = !animateToEnd } 66 | ) 67 | Box( 68 | modifier = Modifier 69 | .layoutId("film_image") 70 | .background(Color.Green) 71 | ) 72 | 73 | Text( 74 | text = "MotionLayout example", 75 | fontSize = titleAttr.value.fontSize("fontSize"), 76 | modifier = Modifier.layoutId("title") 77 | ) 78 | 79 | Box( 80 | modifier = Modifier 81 | .layoutId("circle") 82 | .clip(CircleShape) 83 | .background(Color.Red) 84 | ) 85 | } 86 | } -------------------------------------------------------------------------------- /app/src/main/java/com/rodrigodominguez/motionlayouttalk/abcmotion/MotionLayoutABC.kt: -------------------------------------------------------------------------------- 1 | package com.rodrigodominguez.motionlayouttalk.abcmotion 2 | 3 | import androidx.compose.animation.core.animateFloatAsState 4 | import androidx.compose.animation.core.tween 5 | import androidx.compose.foundation.background 6 | import androidx.compose.foundation.clickable 7 | import androidx.compose.foundation.layout.Box 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.getValue 11 | import androidx.compose.runtime.mutableStateOf 12 | import androidx.compose.runtime.remember 13 | import androidx.compose.runtime.setValue 14 | import androidx.compose.ui.Modifier 15 | import androidx.compose.ui.graphics.Color 16 | import androidx.compose.ui.layout.layoutId 17 | import androidx.compose.ui.platform.LocalContext 18 | import androidx.compose.ui.tooling.preview.Preview 19 | import androidx.constraintlayout.compose.ExperimentalMotionApi 20 | import androidx.constraintlayout.compose.MotionLayout 21 | import androidx.constraintlayout.compose.MotionLayoutDebugFlags 22 | import androidx.constraintlayout.compose.MotionScene 23 | import com.rodrigodominguez.motionlayouttalk.R 24 | import java.util.* 25 | 26 | @Preview 27 | @ExperimentalMotionApi 28 | @Composable 29 | fun MotionLayoutSample1() { 30 | var animateToEnd by remember { mutableStateOf(false) } 31 | val progress by animateFloatAsState( 32 | targetValue = if (animateToEnd) 1f else 0f, 33 | animationSpec = tween(1000) 34 | ) 35 | val context = LocalContext.current 36 | val motionScene = remember { 37 | context.resources 38 | .openRawResource(R.raw.motion_scene_abc) 39 | .readBytes() 40 | .decodeToString() 41 | } 42 | MotionLayout( 43 | motionScene = MotionScene(content = motionScene), 44 | progress = progress, 45 | modifier = Modifier 46 | .fillMaxSize() 47 | .background(Color.White), 48 | debug = EnumSet.of(MotionLayoutDebugFlags.SHOW_ALL) 49 | ) { 50 | Box(modifier = Modifier 51 | .layoutId("box_demo") 52 | .background(Color.Red) 53 | .clickable { 54 | animateToEnd = !animateToEnd 55 | }) 56 | 57 | 58 | } 59 | } -------------------------------------------------------------------------------- /app/src/main/java/com/rodrigodominguez/motionlayouttalk/abcmotion/MotionLayoutYoutubeDemo.kt: -------------------------------------------------------------------------------- 1 | package com.rodrigodominguez.motionlayouttalk.abcmotion 2 | 3 | import androidx.compose.animation.core.animateFloatAsState 4 | import androidx.compose.animation.core.tween 5 | import androidx.compose.foundation.background 6 | import androidx.compose.foundation.clickable 7 | import androidx.compose.foundation.layout.Box 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.material.Text 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.runtime.getValue 12 | import androidx.compose.runtime.mutableStateOf 13 | import androidx.compose.runtime.remember 14 | import androidx.compose.runtime.setValue 15 | import androidx.compose.ui.Modifier 16 | import androidx.compose.ui.graphics.Color 17 | import androidx.compose.ui.layout.layoutId 18 | import androidx.compose.ui.platform.LocalContext 19 | import androidx.compose.ui.tooling.preview.Preview 20 | import androidx.compose.ui.unit.sp 21 | import androidx.constraintlayout.compose.ExperimentalMotionApi 22 | import androidx.constraintlayout.compose.MotionLayout 23 | import androidx.constraintlayout.compose.MotionLayoutDebugFlags 24 | import androidx.constraintlayout.compose.MotionScene 25 | import com.rodrigodominguez.motionlayouttalk.R 26 | import java.util.* 27 | 28 | @Preview 29 | @ExperimentalMotionApi 30 | @Composable 31 | fun YoutubeDemoComposable() { 32 | var animatedToEnd by remember { 33 | mutableStateOf(false) 34 | } 35 | 36 | val progress by animateFloatAsState( 37 | targetValue = if (animatedToEnd) 1f else 0f, 38 | animationSpec = tween(1000) 39 | ) 40 | 41 | val context = LocalContext.current 42 | val motionScene = remember { 43 | context.resources.openRawResource(R.raw.motion_scene1) 44 | .readBytes() 45 | .decodeToString() 46 | } 47 | MotionLayout( 48 | motionScene = MotionScene(content = motionScene), 49 | progress = progress, 50 | modifier = Modifier 51 | .fillMaxSize() 52 | .background(Color.White) 53 | ) { 54 | val contentProperties = motionProperties(id = "content_video") 55 | 56 | Box( 57 | modifier = Modifier 58 | .layoutId("content_video") 59 | .background(contentProperties.value.color("background")) 60 | .clickable { animatedToEnd = !animatedToEnd } 61 | ) 62 | 63 | Box( 64 | modifier = Modifier 65 | .layoutId("video_view") 66 | .background(Color.Black) 67 | ) 68 | 69 | Text( 70 | text = "Title video", fontSize = 18.sp, 71 | modifier = Modifier.layoutId("title") 72 | ) 73 | 74 | Text( 75 | text = "Description video", 76 | modifier = Modifier.layoutId("description") 77 | ) 78 | } 79 | } -------------------------------------------------------------------------------- /app/src/main/java/com/rodrigodominguez/motionlayouttalk/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.rodrigodominguez.motionlayouttalk.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple200 = Color(0xFFBB86FC) 6 | val Purple500 = Color(0xFF6200EE) 7 | val Purple700 = Color(0xFF3700B3) 8 | val Teal200 = Color(0xFF03DAC5) -------------------------------------------------------------------------------- /app/src/main/java/com/rodrigodominguez/motionlayouttalk/ui/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package com.rodrigodominguez.motionlayouttalk.ui.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val Shapes = Shapes( 8 | small = RoundedCornerShape(4.dp), 9 | medium = RoundedCornerShape(4.dp), 10 | large = RoundedCornerShape(0.dp) 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/rodrigodominguez/motionlayouttalk/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.rodrigodominguez.motionlayouttalk.ui.theme 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.darkColors 6 | import androidx.compose.material.lightColors 7 | import androidx.compose.runtime.Composable 8 | 9 | private val DarkColorPalette = darkColors( 10 | primary = Purple200, 11 | primaryVariant = Purple700, 12 | secondary = Teal200 13 | ) 14 | 15 | private val LightColorPalette = lightColors( 16 | primary = Purple500, 17 | primaryVariant = Purple700, 18 | secondary = Teal200 19 | 20 | /* Other default colors to override 21 | background = Color.White, 22 | surface = Color.White, 23 | onPrimary = Color.White, 24 | onSecondary = Color.Black, 25 | onBackground = Color.Black, 26 | onSurface = Color.Black, 27 | */ 28 | ) 29 | 30 | @Composable 31 | fun MotionLayoutTalkTheme( 32 | darkTheme: Boolean = isSystemInDarkTheme(), 33 | content: @Composable() () -> Unit 34 | ) { 35 | val colors = if (darkTheme) { 36 | DarkColorPalette 37 | } else { 38 | LightColorPalette 39 | } 40 | 41 | MaterialTheme( 42 | colors = colors, 43 | typography = Typography, 44 | shapes = Shapes, 45 | content = content 46 | ) 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/rodrigodominguez/motionlayouttalk/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.rodrigodominguez.motionlayouttalk.ui.theme 2 | 3 | import androidx.compose.material.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 | body1 = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp 15 | ) 16 | /* Other default text styles to override 17 | button = TextStyle( 18 | fontFamily = FontFamily.Default, 19 | fontWeight = FontWeight.W500, 20 | fontSize = 14.sp 21 | ), 22 | caption = TextStyle( 23 | fontFamily = FontFamily.Default, 24 | fontWeight = FontWeight.Normal, 25 | fontSize = 12.sp 26 | ) 27 | */ 28 | ) -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigomartind/ComposeMotionLayoutSamples/1ba344881858f8506a9130d4629fd15b8c64ea8c/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigomartind/ComposeMotionLayoutSamples/1ba344881858f8506a9130d4629fd15b8c64ea8c/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigomartind/ComposeMotionLayoutSamples/1ba344881858f8506a9130d4629fd15b8c64ea8c/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigomartind/ComposeMotionLayoutSamples/1ba344881858f8506a9130d4629fd15b8c64ea8c/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigomartind/ComposeMotionLayoutSamples/1ba344881858f8506a9130d4629fd15b8c64ea8c/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigomartind/ComposeMotionLayoutSamples/1ba344881858f8506a9130d4629fd15b8c64ea8c/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigomartind/ComposeMotionLayoutSamples/1ba344881858f8506a9130d4629fd15b8c64ea8c/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigomartind/ComposeMotionLayoutSamples/1ba344881858f8506a9130d4629fd15b8c64ea8c/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigomartind/ComposeMotionLayoutSamples/1ba344881858f8506a9130d4629fd15b8c64ea8c/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigomartind/ComposeMotionLayoutSamples/1ba344881858f8506a9130d4629fd15b8c64ea8c/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/raw/constraint_set.json5: -------------------------------------------------------------------------------- 1 | { 2 | id_of_view: { 3 | width: 100, 4 | height: 200, 5 | // 'wrap' - wrap content 6 | // 'preferWrap' - wrap content if meeting constraints 7 | // 'spread' - fill the space define by the constraints 8 | // 'parent' - size of parents 9 | // percent (e.g. '25%') - percentage of the parents size 10 | // Ratio (e.g. '16:9') - constrain this side to be the enforce the aspect ratio 11 | top: ['parent','top', 16], 12 | bottom:['parent','bottom', 16], 13 | start: ['parent','start', 16], 14 | end: ['parent','end', 16], 15 | circular: ['center', 30, 100], 16 | centerHorizontally: 'parent', 17 | centerVertically: 'parent', 18 | translationX: 'float', 19 | translationY: 'float', 20 | translationZ: 'float', 21 | elevation: 'float', 22 | alpha: '1.0', 23 | rotationX: '0.0', 24 | rotationY: '0.0', 25 | rotationZ: '0.0', 26 | scaleX: '1.0', 27 | scaleY: '1.0', 28 | visibility: 'visible/invisible/gone' 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/motion_scene1.json5: -------------------------------------------------------------------------------- 1 | { 2 | ConstraintSets: { 3 | start: { 4 | content_video: { 5 | width: '90%', 6 | height: 80, 7 | bottom: ['parent','bottom', 16], 8 | end: ['parent','end'], 9 | start: ['parent','start'], 10 | custom: { 11 | background: '#33FF6D' 12 | } 13 | }, 14 | video_view: { 15 | width: 120, 16 | height: 'spread', 17 | top: ['content_video', 'top'], 18 | bottom: ['content_video', 'bottom'], 19 | start: ['content_video', 'start'], 20 | }, 21 | title: { 22 | width: 'spread', 23 | height: 'wrap', 24 | top: ['content_video', 'top', 8], 25 | start: ['video_view', 'end', 8] 26 | }, 27 | description: { 28 | width: 'spread', 29 | height: 'wrap', 30 | top: ['title', 'bottom', 4], 31 | start: ['video_view', 'end', 8] 32 | } 33 | }, 34 | end: { 35 | content_video: { 36 | width: 'spread', 37 | height: 380, 38 | top: ['parent','top'], 39 | end: ['parent','end'], 40 | start: ['parent','start'], 41 | custom: { 42 | background: '#FFD633' 43 | } 44 | }, 45 | video_view: { 46 | width: 'spread', 47 | height: 280, 48 | top: ['parent', 'top'], 49 | start: ['parent', 'start'], 50 | end: ['parent', 'end'] 51 | }, 52 | title: { 53 | width: 'spread', 54 | height: 'wrap', 55 | top: ['video_view', 'bottom', 16], 56 | start: ['parent', 'start', 16] 57 | }, 58 | description: { 59 | width: 'spread', 60 | height: 'wrap', 61 | top: ['title', 'bottom', 4], 62 | start: ['parent', 'start', 16] 63 | } 64 | } 65 | }, 66 | Transitions: { 67 | default: { 68 | from: 'start', 69 | to: 'end' 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/motion_scene1_collapse.json5: -------------------------------------------------------------------------------- 1 | { 2 | ConstraintSets: { 3 | start: { 4 | content: { 5 | width: 'spread', 6 | height: 'spread', 7 | top: ['parent', 'top', 150], 8 | bottom: ['parent', 'bottom'], 9 | start: ['parent', 'start'], 10 | end: ['parent', 'end'], 11 | translationZ: 0, 12 | custom: { 13 | corner: 320 14 | } 15 | }, 16 | film_image: { 17 | width: 100, 18 | height: 180, 19 | start: ['parent','start',32], 20 | top: ['parent','top', 100], 21 | translationZ: 1 22 | }, 23 | circle: { 24 | width: 56, 25 | height: 56, 26 | top: ['content','top'], 27 | bottom: ['content','top'], 28 | end: ['parent','end', 16], 29 | translationZ: 1 30 | }, 31 | title: { 32 | width: 'wrap', 33 | height: 'wrap', 34 | top: ['content', 'top'], 35 | start: ['film_image', 'end', 16], 36 | end: ['parent', 'end', 16], 37 | translationZ: 1, 38 | custom: { 39 | fontSize: 22 40 | } 41 | } 42 | }, 43 | end: { 44 | content: { 45 | width: 'spread', 46 | height: 'spread', 47 | top: ['parent', 'top'], 48 | bottom: ['parent', 'bottom'], 49 | start: ['parent', 'start'], 50 | end: ['parent', 'end'], 51 | custom: { 52 | corner: 0 53 | }, 54 | translationZ: 1 55 | }, 56 | film_image: { 57 | width: 100, 58 | height: 180, 59 | start: ['parent','start',32], 60 | top: ['parent','top', 100], 61 | translationZ: 0 62 | }, 63 | circle: { 64 | width: 0, 65 | height: 0, 66 | top: ['content','top'], 67 | bottom: ['content','top'], 68 | end: ['parent','end', 36], 69 | translationZ: 1 70 | }, 71 | title: { 72 | width: 'wrap', 73 | height: 70, 74 | top: ['parent', 'top'], 75 | start: ['parent', 'start', 16], 76 | end: ['parent', 'end', 16], 77 | translationZ: 1, 78 | custom: { 79 | fontSize: 28 80 | } 81 | } 82 | } 83 | 84 | }, 85 | Transitions: { 86 | default : { 87 | from: 'start', 88 | to : 'end', 89 | KeyFrames: { 90 | KeyPositions: [ 91 | { 92 | target: ['film_image'], 93 | frames: [50], 94 | percentX: [0.0], 95 | percentY: [-0.15] 96 | 97 | } 98 | ], 99 | KeyAttributes: [ 100 | { 101 | target: ['film_image'], 102 | frames: [50], 103 | rotationZ: [55] 104 | } 105 | ] 106 | } 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/motion_scene_abc.json5: -------------------------------------------------------------------------------- 1 | { 2 | ConstraintSets: { 3 | start: { 4 | box_demo: { 5 | width: 200, 6 | height: 200, 7 | end: ['parent','end', 16], 8 | top: ['parent','top', 16] 9 | } 10 | }, 11 | end: { 12 | box_demo: { 13 | width: 200, 14 | height: 200, 15 | bottom: ['parent','bottom', 16], 16 | start: ['parent','start', 16] 17 | } 18 | } 19 | }, 20 | Transitions: { 21 | default: { 22 | from: 'start', 23 | to: 'end', 24 | KeyFrames: { 25 | KeyAttributes: [ 26 | ] 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/res/raw/motion_scene_base.json5: -------------------------------------------------------------------------------- 1 | { 2 | Design:{}, 3 | Variables:{}, 4 | ConstraintSets:{ 5 | start: { 6 | id_of_view: { 7 | 8 | } 9 | }, 10 | end: { 11 | id_of_view: { 12 | } 13 | } 14 | }, 15 | Transitions: { 16 | from: 'start', 17 | to: 'end', 18 | default:{ 19 | KeyFrames:{ 20 | KeyPositions: {}, 21 | KeyAttributes: {}, 22 | KeyCycles: {}, 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /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 | MotionLayoutTalk 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 21 | 22 |