From 4b69bc1423e6423d7e5ddd4281c5ffef5b0e115f Mon Sep 17 00:00:00 2001 From: Jossi Wolf <10628007+jossiwolf@users.noreply.github.com> Date: Wed, 24 Feb 2021 17:16:00 +0100 Subject: [PATCH] [Compose] Add merge paths flag (#1744) Fixes #1734 LottieAnimationView had merge paths disabled by default, but LottieAnimation enables them by default. As long as merge paths contain known bugs, I think it makes sense not to change that behavior - what do you think? I added a flag for it to the state. If you want, we can add it as a constructor param too, but I feel like that's not needed as it's not that commonly used. --- CHANGELOG_COMPOSE.md | 3 ++ build.gradle | 2 +- .../airbnb/lottie/compose/LottieAnimation.kt | 11 ++--- .../lottie/compose/LottieAnimationState.kt | 46 ++++++++++++++++++- .../lottiefiles/LottieFilesSearchPage.kt | 3 +- .../sample/compose/player/PlayerPage.kt | 23 ++++++++-- .../sample/compose/player/ToolbarChip.kt | 9 ++-- .../src/main/res/values/strings.xml | 1 + 8 files changed, 77 insertions(+), 21 deletions(-) diff --git a/CHANGELOG_COMPOSE.md b/CHANGELOG_COMPOSE.md index 0e006170c6..90560778b3 100644 --- a/CHANGELOG_COMPOSE.md +++ b/CHANGELOG_COMPOSE.md @@ -1,6 +1,9 @@ #### Note: For the time being, we won't provide numbered releases for every new Jetpack Compose version. Check out our [snapshot builds](https://github.com/airbnb/lottie/blob/master/android-compose.md#getting-started) instead. +# 1.0.0-alpha07-SNAPSHOT +* Add flag for merge paths to LottieAnimationState + # 1.0.0-alpha06 * Compatible with Jetpack Compose Alpha 12 diff --git a/build.gradle b/build.gradle index a1809233e5..b35e69293e 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { } dependencies { classpath 'org.ajoberstar:grgit:1.9.3' - classpath 'com.android.tools.build:gradle:7.0.0-alpha06' + classpath 'com.android.tools.build:gradle:7.0.0-alpha07' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlinVersion" classpath 'org.ajoberstar:grgit:1.9.3' diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt index 1c7a10a0a8..61b3473d40 100644 --- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt +++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimation.kt @@ -9,7 +9,6 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.drawscope.drawIntoCanvas import androidx.compose.ui.graphics.drawscope.withTransform import androidx.compose.ui.graphics.nativeCanvas -import androidx.compose.ui.platform.AmbientContext import androidx.compose.ui.platform.LocalContext import com.airbnb.lottie.LottieComposition import com.airbnb.lottie.LottieCompositionFactory @@ -80,11 +79,7 @@ fun LottieAnimation( state: LottieAnimationState, modifier: Modifier = Modifier, ) { - val drawable = remember { - LottieDrawable().apply { - enableMergePathsForKitKatAndAbove(true) - } - } + val drawable = remember { LottieDrawable() } SideEffect { drawable.composition = composition @@ -118,7 +113,6 @@ fun LottieAnimation( } if (composition == null || composition.duration == 0f) return - SideEffect {} Canvas( modifier = Modifier @@ -129,6 +123,7 @@ fun LottieAnimation( drawable.progress = state.progress drawable.setOutlineMasksAndMattes(state.outlineMasksAndMattes) drawable.isApplyingOpacityToLayersEnabled = state.applyOpacityToLayers + drawable.enableMergePathsForKitKatAndAbove(state.enableMergePaths) withTransform({ scale(size.width / composition.bounds.width().toFloat(), size.height / composition.bounds.height().toFloat(), Offset.Zero) }) { @@ -143,4 +138,4 @@ private fun Modifier.maintainAspectRatio(composition: LottieComposition?): Modif return this.then(aspectRatio(composition.bounds.width() / composition.bounds.height().toFloat())) } -private fun lerp(a: Float, b: Float, @FloatRange(from = 0.0, to = 1.0) percentage: Float) = a + percentage * (b - a) +private fun lerp(a: Float, b: Float, @FloatRange(from = 0.0, to = 1.0) percentage: Float) = a + percentage * (b - a) diff --git a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt index a2457895e5..b410285e43 100644 --- a/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt +++ b/lottie-compose/src/main/java/com/airbnb/lottie/compose/LottieAnimationState.kt @@ -6,25 +6,47 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +/** + * Create a [LottieAnimationState] and remember it + * + * @param autoPlay Initial value for [LottieAnimationState.isPlaying] + * @param repeatCount Initial value for [LottieAnimationState.repeatCount] + * @param initialProgress Initial value for [LottieAnimationState.progress] + * @param enableMergePaths Initial value for [LottieAnimationState.enableMergePaths] + */ @Composable fun rememberLottieAnimationState( autoPlay: Boolean = true, repeatCount: Int = 0, initialProgress: Float = 0f, + enableMergePaths: Boolean = true ): LottieAnimationState { // Use rememberSavedInstanceState so you can pause/resume animations return remember(repeatCount, autoPlay) { - LottieAnimationState(isPlaying = autoPlay, repeatCount, initialProgress) + LottieAnimationState( + isPlaying = autoPlay, + repeatCount = repeatCount, + initialProgress = initialProgress, + enableMergePaths = enableMergePaths + ) } } /** - * @see rememberLottieAnimationState() + * State of the [LottieAnimation] composable + * + * @param isPlaying Initial value for [isPlaying] + * @param repeatCount Initial value for [repeatCount] + * @param initialProgress Initial value for [progress] + * @param enableMergePaths Initial value for [enableMergePaths] + * + * @see rememberLottieAnimationState */ class LottieAnimationState( isPlaying: Boolean, repeatCount: Int = 0, initialProgress: Float = 0f, + enableMergePaths: Boolean = true ) { var progress by mutableStateOf(initialProgress) @@ -32,7 +54,15 @@ class LottieAnimationState( private var _frame = mutableStateOf(0) val frame: Int by _frame + /** + * Whether the animation is currently playing. + */ var isPlaying by mutableStateOf(isPlaying) + + /** + * How many times the animation will be played. Use [Int.MAX_VALUE] for + * infinite repetitions. + */ var repeatCount by mutableStateOf(repeatCount) var speed by mutableStateOf(1f) @@ -58,6 +88,18 @@ class LottieAnimationState( */ var applyOpacityToLayers by mutableStateOf(false) + /** + * Enable this to get merge path support. + *

+ * Merge paths currently don't work if the the operand shape is entirely contained within the + * first shape. If you need to cut out one shape from another shape, use an even-odd fill type + * instead of using merge paths. + *

+ * If your animation contains merge paths and you are encountering rendering issues, disabling + * merge paths might help. + */ + var enableMergePaths by mutableStateOf(enableMergePaths) + internal fun updateFrame(frame: Int) { _frame.value = frame } diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesSearchPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesSearchPage.kt index 37e48e2859..4933326d19 100644 --- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesSearchPage.kt +++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/lottiefiles/LottieFilesSearchPage.kt @@ -141,7 +141,8 @@ fun LottieFilesSearchPage( value = state.query, onValueChange = onQueryChanged, label = { Text(stringResource(R.string.query)) }, - modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp) + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), + singleLine = true ) LazyColumn( modifier = Modifier.weight(1f) diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt index a8c81f4432..f882c0147a 100644 --- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt +++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/PlayerPage.kt @@ -19,7 +19,9 @@ 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.graphics.vector.rememberVectorPainter import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign @@ -50,6 +52,7 @@ fun PlayerPage( val scaffoldState = rememberScaffoldState() val outlineMasksAndMattes = remember { mutableStateOf(false) } val applyOpacityToLayers = remember { mutableStateOf(false) } + val enableMergePaths = remember { mutableStateOf(animationState.enableMergePaths) } var focusMode by remember { mutableStateOf(false) } var backgroundColor by remember { mutableStateOf(animationBackgroundColor) } var showWarningsDialog by remember { mutableStateOf(false) } @@ -72,6 +75,7 @@ fun PlayerPage( animationState.outlineMasksAndMattes = outlineMasksAndMattes.value animationState.applyOpacityToLayers = applyOpacityToLayers.value + animationState.enableMergePaths = enableMergePaths.value Scaffold( scaffoldState = scaffoldState, @@ -164,6 +168,7 @@ fun PlayerPage( backgroundColor = backgroundColorToolbar, outlineMasksAndMattes = outlineMasksAndMattes, applyOpacityToLayers = applyOpacityToLayers, + enableMergePaths = enableMergePaths ) } } @@ -344,6 +349,7 @@ private fun Toolbar( backgroundColor: MutableState, outlineMasksAndMattes: MutableState, applyOpacityToLayers: MutableState, + enableMergePaths: MutableState, ) { Row( modifier = Modifier @@ -352,40 +358,47 @@ private fun Toolbar( .padding(bottom = 8.dp) ) { ToolbarChip( - iconRes = R.drawable.ic_masks_and_mattes, + iconPainter = painterResource(R.drawable.ic_masks_and_mattes), label = stringResource(R.string.toolbar_item_masks), isActivated = outlineMasksAndMattes.value, onClick = { outlineMasksAndMattes.value = it }, modifier = Modifier.padding(end = 8.dp) ) ToolbarChip( - iconRes = R.drawable.ic_layers, + iconPainter = painterResource(R.drawable.ic_layers), label = stringResource(R.string.toolbar_item_opacity_layers), isActivated = applyOpacityToLayers.value, onClick = { applyOpacityToLayers.value = it }, modifier = Modifier.padding(end = 8.dp) ) ToolbarChip( - iconRes = R.drawable.ic_color, + iconPainter = painterResource(R.drawable.ic_color), label = stringResource(R.string.toolbar_item_color), isActivated = backgroundColor.value, onClick = { backgroundColor.value = it }, modifier = Modifier.padding(end = 8.dp) ) ToolbarChip( - iconRes = R.drawable.ic_speed, + iconPainter = painterResource(R.drawable.ic_speed), label = stringResource(R.string.toolbar_item_speed), isActivated = speed.value, onClick = { speed.value = it }, modifier = Modifier.padding(end = 8.dp) ) ToolbarChip( - iconRes = R.drawable.ic_border, + iconPainter = painterResource(R.drawable.ic_border), label = stringResource(R.string.toolbar_item_border), isActivated = border.value, onClick = { border.value = it }, modifier = Modifier.padding(end = 8.dp) ) + ToolbarChip( + iconPainter = rememberVectorPainter(Icons.Default.MergeType), + label = stringResource(R.string.toolbar_item_merge_paths), + isActivated = enableMergePaths.value, + onClick = { enableMergePaths.value = it }, + modifier = Modifier.padding(end = 8.dp) + ) } } diff --git a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/ToolbarChip.kt b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/ToolbarChip.kt index 69904b8fe1..1440c32277 100644 --- a/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/ToolbarChip.kt +++ b/sample-compose/src/main/java/com/airbnb/lottie/sample/compose/player/ToolbarChip.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle @@ -28,7 +29,7 @@ fun ToolbarChip( isActivated: Boolean, onClick: (isActivated: Boolean) -> Unit, modifier: Modifier = Modifier, - @DrawableRes iconRes: Int = 0, + iconPainter: Painter? = null, ) { val unActivatedColor = remember { Color(0xFF444444) } Surface( @@ -44,9 +45,9 @@ fun ToolbarChip( modifier = Modifier .padding(horizontal = 8.dp, vertical = 4.dp) ) { - if (iconRes != 0) { + if (iconPainter != null) { Icon( - painterResource(iconRes), + iconPainter, tint = if (isActivated) Color.White else unActivatedColor, modifier = Modifier .preferredSize(12.dp), @@ -67,7 +68,7 @@ fun ToolbarChip( @Composable fun PreviewToolbarChip() { ToolbarChip( - iconRes = R.drawable.ic_border, + iconPainter = painterResource(R.drawable.ic_border), label = stringResource(R.string.toolbar_item_border), isActivated = false, onClick = {} diff --git a/sample-compose/src/main/res/values/strings.xml b/sample-compose/src/main/res/values/strings.xml index 46de9bf5ea..4f9ee0d647 100644 --- a/sample-compose/src/main/res/values/strings.xml +++ b/sample-compose/src/main/res/values/strings.xml @@ -22,6 +22,7 @@ Speed Background Apply Opacity To Layers + Enable Merge Paths Failed to load composition OK