diff --git a/README.md b/README.md new file mode 100644 index 0000000..add3293 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# 💫 Animations in Jetpack Compose 💫 + +Navigation transition animations without accompanist library. + +# You will learn: +- Visibility animation +- Content Animation +- Composable State Animation +- Infinite Animation +- Navigation Animation +- Navigation in Jetpack Compose + +# Video + +https://github.com/KaushalVasava/JetPackCompose_Basic/assets/49050597/ed4ca42d-4e59-429a-a575-88eda2d259d9 + +# Author +Kaushal Vasava. diff --git a/app/build.gradle b/app/build.gradle index 75677b5..9082afd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,13 +4,13 @@ plugins { } android { - namespace 'com.lahsuak.apps.jetpackcomposebasic' - compileSdk 33 + namespace "com.lahsuak.apps.jetpackcomposebasic" + compileSdk 34 defaultConfig { applicationId "com.lahsuak.apps.jetpackcomposebasic" minSdk 23 - targetSdk 33 + targetSdk 34 versionCode 1 versionName "1.0" @@ -27,17 +27,17 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } buildFeatures { compose true } composeOptions { - kotlinCompilerExtensionVersion '1.1.1' + kotlinCompilerExtensionVersion '1.4.5' } packagingOptions { resources { @@ -48,16 +48,18 @@ android { dependencies { - implementation 'androidx.core:core-ktx:1.9.0' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' - implementation 'androidx.activity:activity-compose:1.6.1' + implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2' + implementation 'androidx.activity:activity-compose:1.7.2' implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" - implementation 'androidx.compose.material3:material3:1.1.0-alpha03' + implementation 'androidx.compose.material3:material3:1.1.1' testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.4' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" + + implementation "androidx.navigation:navigation-compose:2.7.2" } \ No newline at end of file diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/MainActivity.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/MainActivity.kt index 187ee32..091159b 100644 --- a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/MainActivity.kt +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/MainActivity.kt @@ -1,50 +1,172 @@ package com.lahsuak.apps.jetpackcomposebasic +import android.content.res.Configuration.UI_MODE_NIGHT_YES import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.spring +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Button +import androidx.compose.material3.ElevatedButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.navigation.compose.rememberNavController +import com.lahsuak.apps.jetpackcomposebasic.model.AnimationItem +import com.lahsuak.apps.jetpackcomposebasic.ui.navigation.navhost.AppNavHost +import com.lahsuak.apps.jetpackcomposebasic.ui.screen.ImageBorderAnimation import com.lahsuak.apps.jetpackcomposebasic.ui.theme.JetPackComposeBasicTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - JetPackComposeBasicTheme { - // A surface container using the 'background' color from the theme - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - Greeting("Android") - } + JetPackComposeBasicTheme(dynamicColor = false) { + AppNavHost(navController = rememberNavController()) } } } } -/*** -Composable functions : -A composable function is a regular function annotated with @Composable. -This enables your function to call other @Composable functions within it. -You can see how the Greeting function is marked as @Composable. -This function will produce a piece of UI hierarchy displaying the given input, -String. Text is a composable function provided by the library. -***/ + +@Composable +fun MyApp(modifier: Modifier = Modifier) { + /** PERSISTENT STATE + * Instead of using remember you can use rememberSaveable. + This will save each state surviving configuration changes (such as rotations) and process death. + **/ + var shouldShowOnboarding by rememberSaveable { mutableStateOf(true) } + + Surface(modifier) { + if (shouldShowOnboarding) { + OnboardingScreen(onContinueClicked = { shouldShowOnboarding = false }) + } else { + Greetings() + } + } +} + @Composable fun Greeting(name: String) { - Text(text = "Hello $name!") + var expanded by rememberSaveable { mutableStateOf(false) } + + val extraPadding by animateDpAsState( + if (expanded) 48.dp else 0.dp, + animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessLow + ), label = "Greeting" + ) + Surface( + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp) + ) { + Row(modifier = Modifier.padding(24.dp)) { + Column( + modifier = Modifier + .weight(1f) + .padding(bottom = extraPadding.coerceAtLeast(0.dp)) + ) { + Text(text = "Hello,") + Text( + text = name, style = MaterialTheme.typography.headlineMedium.copy( + fontWeight = FontWeight.ExtraBold + ) + ) + } + ElevatedButton( + onClick = { expanded = !expanded } + ) { + Text(if (expanded) "Show less" else "Show more") + } + } + } } -@Preview(showBackground = true) +@Composable +private fun Greetings( + modifier: Modifier = Modifier, + names: List = List(1000) { "item $it" }, +) { + LazyColumn(modifier = modifier.padding(vertical = 4.dp)) { + items(items = names) { name -> + Greeting(name = name) + } + } +} + +// add new on-boarding screen +@Composable +fun OnboardingScreen( + onContinueClicked: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text("Welcome to the Basics Codelab!") + Button( + modifier = Modifier.padding(vertical = 24.dp), + onClick = onContinueClicked + ) { + Text("Continue") + } + } +} + +@Preview(showBackground = true, widthDp = 320) @Composable fun DefaultPreview() { + JetPackComposeBasicTheme(dynamicColor = false) { + Greetings() + } +} + +@Preview( + showBackground = true, + widthDp = 320, + uiMode = UI_MODE_NIGHT_YES, + name = "Dark" +) +@Preview(showBackground = true, widthDp = 320) +@Composable +fun DefaultPreviewDark() { + JetPackComposeBasicTheme(dynamicColor = false) { + Greetings() + } +} + +@Preview(showBackground = true, widthDp = 320, heightDp = 320) +@Composable +fun OnboardingPreview() { + JetPackComposeBasicTheme { + OnboardingScreen(onContinueClicked = {}) + } +} + +@Preview +@Composable +fun MyAppPreview() { JetPackComposeBasicTheme { - Greeting("Android") + MyApp(Modifier.fillMaxSize()) } } \ No newline at end of file diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/model/AnimationItem.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/model/AnimationItem.kt new file mode 100644 index 0000000..e7d3fab --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/model/AnimationItem.kt @@ -0,0 +1,6 @@ +package com.lahsuak.apps.jetpackcomposebasic.model + +data class AnimationItem( + val name: String, + val navigationRoute: String, +) diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/navigation/AppNavigation.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/navigation/AppNavigation.kt new file mode 100644 index 0000000..f5beb71 --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/navigation/AppNavigation.kt @@ -0,0 +1,18 @@ +package com.lahsuak.apps.jetpackcomposebasic.ui.navigation + +enum class Screen { + Home, + AnimateVisibility, + AnimateContent, + AnimateState, + InfiniteAnimation, + ImageBorderAnimation, +} +sealed class NavigationItem(val route: String) { + object Home : NavigationItem(Screen.Home.name) + object AnimateVisibility : NavigationItem(Screen.AnimateVisibility.name) + object AnimateContent : NavigationItem(Screen.AnimateContent.name) + object AnimateState : NavigationItem(Screen.AnimateState.name) + object InfiniteAnimation : NavigationItem(Screen.InfiniteAnimation.name) + object ImageBorderAnimation : NavigationItem(Screen.ImageBorderAnimation.name) +} \ No newline at end of file diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/navigation/navhost/AppNavHost.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/navigation/navhost/AppNavHost.kt new file mode 100644 index 0000000..cea0545 --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/navigation/navhost/AppNavHost.kt @@ -0,0 +1,72 @@ +package com.lahsuak.apps.jetpackcomposebasic.ui.navigation.navhost + +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.core.tween +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import com.lahsuak.apps.jetpackcomposebasic.ui.navigation.NavigationItem +import com.lahsuak.apps.jetpackcomposebasic.ui.screen.AnimateContent +import com.lahsuak.apps.jetpackcomposebasic.ui.screen.AnimateStateValue +import com.lahsuak.apps.jetpackcomposebasic.ui.screen.AnimateViewVisibility +import com.lahsuak.apps.jetpackcomposebasic.ui.screen.HomeScreen +import com.lahsuak.apps.jetpackcomposebasic.ui.screen.ImageBorderAnimation +import com.lahsuak.apps.jetpackcomposebasic.ui.screen.RepeatAnimationInfinite + +@Composable +fun AppNavHost( + modifier: Modifier = Modifier, + navController: NavHostController, + startDestination: String = NavigationItem.Home.route, +) { + NavHost( + modifier = modifier, + navController = navController, + startDestination = startDestination, + enterTransition = { + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Left, + tween(400) + ) + }, + exitTransition = { + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Left, + tween(400) + ) + }, + popEnterTransition = { + slideIntoContainer( + towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right, + animationSpec = tween(400) + ) + }, + popExitTransition = { + slideOutOfContainer( + towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right, + animationSpec = tween(400) + ) + } + ) { + composable(NavigationItem.Home.route) { + HomeScreen(navController) + } + composable(NavigationItem.AnimateState.route) { + AnimateStateValue() + } + composable(NavigationItem.AnimateContent.route) { + AnimateContent() + } + composable(NavigationItem.AnimateVisibility.route) { + AnimateViewVisibility() + } + composable(NavigationItem.InfiniteAnimation.route) { + RepeatAnimationInfinite() + } + composable(NavigationItem.ImageBorderAnimation.route) { + ImageBorderAnimation() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/AnimateContent.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/AnimateContent.kt new file mode 100644 index 0000000..627285c --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/AnimateContent.kt @@ -0,0 +1,62 @@ +package com.lahsuak.apps.jetpackcomposebasic.ui.screen + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color + +@Composable +fun AnimateContent() { + Column(Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) { + var isVisible by remember { + mutableStateOf(false) + } + Button( + colors = ButtonDefaults.buttonColors( + containerColor = Color.Green, + contentColor = Color.Black, + disabledContainerColor = Color.Gray, + disabledContentColor = Color.DarkGray + ), + onClick = { isVisible = !isVisible }) { + Text("Toggle") + } + AnimatedContent( + targetState = isVisible, + modifier = Modifier + .fillMaxWidth() + .weight(1f), + label = "content", + content = { + if (it) { + Box(modifier = Modifier.background(Color.Green)) + } else { + Box(modifier = Modifier.background(Color.Red)) + } + }, + transitionSpec = { + slideInHorizontally { + if (isVisible) it else -it + } togetherWith slideOutHorizontally { + if (isVisible) -it else it + } + } + ) + } +} diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/AnimateStateValue.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/AnimateStateValue.kt new file mode 100644 index 0000000..99591eb --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/AnimateStateValue.kt @@ -0,0 +1,74 @@ +package com.lahsuak.apps.jetpackcomposebasic.ui.screen + +import androidx.compose.animation.animateColor +import androidx.compose.animation.core.animateInt +import androidx.compose.animation.core.tween +import androidx.compose.animation.core.updateTransition +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp + +@Composable +fun AnimateStateValue() { + Column(Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) { + var isRound by remember { + mutableStateOf(false) + } + Button( + colors = ButtonDefaults.buttonColors( + containerColor = Color.Green, + contentColor = Color.Black, + disabledContainerColor = Color.Gray, + disabledContentColor = Color.DarkGray + ), + onClick = { isRound = !isRound }) { + Text("Toggle") + } + val transition = updateTransition(targetState = isRound, label = null) + val borderRadius by transition.animateInt(transitionSpec = { + tween(2000) + }, label = "borderRadius") { + if (it) 100 + else 0 + } + val color by transition.animateColor(transitionSpec = { + tween(2000) + }, label = "color") { + if (it) + Color.Green + else + Color.Red + } +// val borderRadius by animateIntAsState( +// targetValue = if (isRound) 40 else 20, +// label = "animate", +// animationSpec = spring( +// dampingRatio = Spring.DampingRatioHighBouncy, +// stiffness = Spring.StiffnessLow +// ) +// //tween(delayMillis = 100, durationMillis = 3000) +// ) + Box( + modifier = Modifier + .size(200.dp) + .clip(RoundedCornerShape(borderRadius)) + .background(color) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/AnimateViewVisibility.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/AnimateViewVisibility.kt new file mode 100644 index 0000000..7f7b7a4 --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/AnimateViewVisibility.kt @@ -0,0 +1,52 @@ +package com.lahsuak.apps.jetpackcomposebasic.ui.screen + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color + +@Composable +fun AnimateViewVisibility() { + Column(Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) { + var isVisible by remember { + mutableStateOf(false) + } + Button( + colors = ButtonDefaults.buttonColors( + containerColor = Color.Green, + contentColor = Color.Black, + disabledContainerColor = Color.Gray, + disabledContentColor = Color.DarkGray + ), + onClick = { isVisible = !isVisible }) { + Text("Toggle") + } + AnimatedVisibility( + visible = isVisible, + enter = slideInVertically() + fadeIn(), + exit = slideOutVertically() + fadeOut(), + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + Box(modifier = Modifier.background(Color.Red)) + } + } +} diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/HomeScreen.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/HomeScreen.kt new file mode 100644 index 0000000..c4ff3bb --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/HomeScreen.kt @@ -0,0 +1,50 @@ +package com.lahsuak.apps.jetpackcomposebasic.ui.screen + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Card +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import com.lahsuak.apps.jetpackcomposebasic.model.AnimationItem +import com.lahsuak.apps.jetpackcomposebasic.ui.navigation.NavigationItem + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HomeScreen(navController: NavController) { + val animationsType = listOf( + AnimationItem("Animate Content", NavigationItem.AnimateContent.route), + AnimationItem("Animate State", NavigationItem.AnimateState.route), + AnimationItem("Animate Visibility", NavigationItem.AnimateVisibility.route), + AnimationItem("Infinite Animation", NavigationItem.InfiniteAnimation.route), + AnimationItem("Image border animation", NavigationItem.ImageBorderAnimation.route), + ) + LazyColumn { + items(animationsType) { + Card( + onClick = { + navController.navigate(it.navigationRoute) + }, modifier = Modifier + .fillMaxWidth() + .padding( + horizontal = 16.dp, + vertical = 8.dp + ) + ) { + Text( + it.name, + fontSize = 32.sp, + fontWeight = FontWeight.SemiBold, + modifier = Modifier.padding(24.dp) + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/ImageBorderAnimation.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/ImageBorderAnimation.kt new file mode 100644 index 0000000..fd704f9 --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/ImageBorderAnimation.kt @@ -0,0 +1,56 @@ +package com.lahsuak.apps.jetpackcomposebasic.ui.screen + +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowForward +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.drawscope.rotate +import androidx.compose.ui.unit.dp + +@Composable +fun ImageBorderAnimation() { + val infiniteTransition = rememberInfiniteTransition(label = "infinite") + val rotationAnimation = infiniteTransition.animateFloat( + initialValue = 0f, + targetValue = 360f, + animationSpec = infiniteRepeatable(tween(1000, easing = LinearEasing)), label = "rotate" + ) + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Image( + imageVector = Icons.Default.ArrowForward, contentDescription = null, + modifier = Modifier + .drawBehind { + rotate(rotationAnimation.value) { + drawCircle( + Brush.linearGradient( + colors = listOf( + Color.Blue, Color.Green, Color.Yellow, Color.Red, Color.Magenta + ) + ), + style = Stroke(8f) + ) + } + } + .size(60.dp) + .padding(8.dp) + .clip(CircleShape) + ) + } +} diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/RepeatAnimationInfinite.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/RepeatAnimationInfinite.kt new file mode 100644 index 0000000..54aa520 --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/RepeatAnimationInfinite.kt @@ -0,0 +1,53 @@ +package com.lahsuak.apps.jetpackcomposebasic.ui.screen + +import androidx.compose.animation.animateColor +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp + +@Composable +fun RepeatAnimationInfinite() { + Column( + Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + var isRound by remember { + mutableStateOf(false) + } + val repeatable = rememberInfiniteTransition(label = "repeatable") + val color by repeatable.animateColor( + initialValue = Color.Green, + targetValue = Color.Red, + animationSpec = + infiniteRepeatable( + animation = tween(5000), + repeatMode = RepeatMode.Reverse + ), label = "color" + ) + Box( + modifier = Modifier + .size(200.dp) + .background(color) + ) + } +} diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/theme/Color.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/theme/Color.kt index e0a4930..69564a0 100644 --- a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/theme/Color.kt +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/theme/Color.kt @@ -8,4 +8,9 @@ val Pink80 = Color(0xFFEFB8C8) val Purple40 = Color(0xFF6650a4) val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) \ No newline at end of file +val Pink40 = Color(0xFF7D5260) + +val Navy = Color(0xFF073042) +val Blue = Color(0xFF4285F4) +val LightBlue = Color(0xFFD7EFFE) +val Chartreuse = Color(0xFFEFF7CF) \ No newline at end of file diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/theme/Theme.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/theme/Theme.kt index 5ec8f0d..b112dfd 100644 --- a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/theme/Theme.kt +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/theme/Theme.kt @@ -10,31 +10,24 @@ import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.core.view.ViewCompat private val DarkColorScheme = darkColorScheme( - primary = Purple80, - secondary = PurpleGrey80, - tertiary = Pink80 + surface = Blue, + onSurface = Navy, + primary = Navy, + onPrimary = Chartreuse ) private val LightColorScheme = lightColorScheme( - primary = Purple40, - secondary = PurpleGrey40, - tertiary = Pink40 - - /* Other default colors to override - background = Color(0xFFFFFBFE), - surface = Color(0xFFFFFBFE), - onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ + surface = Blue, + onSurface = Color.White, + primary = LightBlue, + onPrimary = Navy ) @Composable diff --git a/build.gradle b/build.gradle index e868fc8..abd017e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,10 @@ buildscript { ext { - compose_version = '1.3.2' + compose_version = '1.4.5' } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.3.0' apply false - id 'com.android.library' version '7.3.0' apply false - id 'org.jetbrains.kotlin.android' version '1.6.10' apply false + id 'com.android.application' version '8.1.0' apply false + id 'com.android.library' version '8.1.0' apply false + id 'org.jetbrains.kotlin.android' version '1.8.20' apply false } \ No newline at end of file