From b1ebe72cc31c6c870eea52a72d2fda1f61641eff Mon Sep 17 00:00:00 2001 From: acclorite Date: Sun, 4 Aug 2024 18:53:28 +0300 Subject: [PATCH] AnimatedTopAppBar: New custom content option, to also change scrollable color for content below. --- .../components/AnimatedTopAppBar.kt | 152 +++++++++--------- .../library/components/LibraryTabRow.kt | 4 +- .../library/components/LibraryTopBar.kt | 21 ++- 3 files changed, 93 insertions(+), 84 deletions(-) diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/components/AnimatedTopAppBar.kt b/app/src/main/java/ua/acclorite/book_story/presentation/components/AnimatedTopAppBar.kt index 70da23f8..08e54bfe 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/components/AnimatedTopAppBar.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/components/AnimatedTopAppBar.kt @@ -1,16 +1,17 @@ package ua.acclorite.book_story.presentation.components -import androidx.compose.animation.core.FastOutLinearInEasing import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.spring import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.statusBars import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme @@ -18,12 +19,11 @@ import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.lerp -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.dp +import androidx.compose.ui.res.stringResource +import ua.acclorite.book_story.R /** * Animated top app bar. @@ -36,6 +36,7 @@ import androidx.compose.ui.unit.dp * @param content1Visibility Whether first top app bar should be shown. * @param content2Visibility Whether second top app bar should be shown. * @param content3Visibility Whether third top app bar should be shown. + * @param customContent Custom content below [TopAppBar]. */ @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -59,98 +60,93 @@ fun AnimatedTopAppBar( content3Visibility: Boolean? = null, content3NavigationIcon: @Composable () -> Unit = {}, content3Title: @Composable () -> Unit = {}, - content3Actions: @Composable RowScope.() -> Unit = {} + content3Actions: @Composable RowScope.() -> Unit = {}, + + customContent: @Composable ColumnScope.() -> Unit = {} ) { - Box(modifier = Modifier.fillMaxWidth()) { - val density = LocalDensity.current - val statusBarPadding = with(density) { - WindowInsets.statusBars.getTop(density).toDp() - } + val animatedContainerColor = lerp( + containerColor, + scrolledContainerColor, + animateFloatAsState( + if (isTopBarScrolled == true) 1f else 0f, + animationSpec = spring( + stiffness = Spring.StiffnessMediumLow + ), + label = stringResource(id = R.string.top_app_bar_anim_content_desc) + ).value + ) - if (containerColor.alpha != 0f) { - Box( - modifier = Modifier - .fillMaxWidth() - .height(64.dp + statusBarPadding) - .background( - if (isTopBarScrolled == true) scrolledContainerColor - else containerColor - ) - ) - } - - val animatedContainerColor = remember( - containerColor, - scrolledContainerColor, - isTopBarScrolled - ) { - lerp( - containerColor, - scrolledContainerColor, - FastOutLinearInEasing.transform(if (isTopBarScrolled == true) 1f else 0f) - ) - } - - CustomAnimatedVisibility( - visible = content1Visibility ?: true, - enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)), - exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow)) - ) { - TopAppBar( - windowInsets = WindowInsets.statusBars, - navigationIcon = content1NavigationIcon, - title = content1Title, - actions = content1Actions, - scrollBehavior = scrollBehavior, - modifier = Modifier.fillMaxWidth(), - colors = TopAppBarDefaults.topAppBarColors( - containerColor = animatedContainerColor, - scrolledContainerColor = scrolledContainerColor - ) - ) - } - - if (content2Visibility != null) { + Column( + Modifier + .fillMaxWidth() + .background(animatedContainerColor) + ) { + Box(modifier = Modifier.fillMaxWidth()) { CustomAnimatedVisibility( - visible = content2Visibility, + visible = content1Visibility ?: true, enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)), exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow)) ) { TopAppBar( windowInsets = WindowInsets.statusBars, - navigationIcon = content2NavigationIcon, - title = content2Title, - actions = content2Actions, + navigationIcon = content1NavigationIcon, + title = content1Title, + actions = content1Actions, scrollBehavior = scrollBehavior, modifier = Modifier.fillMaxWidth(), colors = TopAppBarDefaults.topAppBarColors( - containerColor = animatedContainerColor, + containerColor = if (isTopBarScrolled != null) Color.Transparent + else containerColor, scrolledContainerColor = scrolledContainerColor ) ) } - } - if (content3Visibility != null) { - CustomAnimatedVisibility( - visible = content3Visibility, - enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)), - exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow)) - ) { - TopAppBar( - windowInsets = WindowInsets.statusBars, - navigationIcon = content3NavigationIcon, - title = content3Title, - actions = content3Actions, - scrollBehavior = scrollBehavior, - modifier = Modifier.fillMaxWidth(), - colors = TopAppBarDefaults.topAppBarColors( - containerColor = animatedContainerColor, - scrolledContainerColor = scrolledContainerColor + if (content2Visibility != null) { + CustomAnimatedVisibility( + visible = content2Visibility, + enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)), + exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow)) + ) { + TopAppBar( + windowInsets = WindowInsets.statusBars, + navigationIcon = content2NavigationIcon, + title = content2Title, + actions = content2Actions, + scrollBehavior = scrollBehavior, + modifier = Modifier.fillMaxWidth(), + colors = TopAppBarDefaults.topAppBarColors( + containerColor = if (isTopBarScrolled != null) Color.Transparent + else containerColor, + scrolledContainerColor = scrolledContainerColor + ) ) - ) + } + } + + if (content3Visibility != null) { + CustomAnimatedVisibility( + visible = content3Visibility, + enter = fadeIn(spring(stiffness = Spring.StiffnessMediumLow)), + exit = fadeOut(spring(stiffness = Spring.StiffnessMediumLow)) + ) { + TopAppBar( + windowInsets = WindowInsets.statusBars, + navigationIcon = content3NavigationIcon, + title = content3Title, + actions = content3Actions, + scrollBehavior = scrollBehavior, + modifier = Modifier.fillMaxWidth(), + colors = TopAppBarDefaults.topAppBarColors( + containerColor = if (isTopBarScrolled != null) Color.Transparent + else containerColor, + scrolledContainerColor = scrolledContainerColor + ) + ) + } } } + customContent() } } diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/library/components/LibraryTabRow.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/library/components/LibraryTabRow.kt index 7671ab3e..886b4793 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/library/components/LibraryTabRow.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/library/components/LibraryTabRow.kt @@ -33,12 +33,14 @@ import ua.acclorite.book_story.presentation.screens.library.data.LibraryEvent * Tab row, either scrollable or static, depends on screen width. * * @param onEvent Event used to Scroll to Page. + * @param itemCountBackgroundColor Item count background color. * @param books The list of all books. * @param pagerState [PagerState]. */ @Composable fun LibraryTabRow( onEvent: (LibraryEvent) -> Unit, + itemCountBackgroundColor: Color, books: List, pagerState: PagerState ) { @@ -113,7 +115,7 @@ fun LibraryTabRow( text = tabItem.second.toString(), modifier = Modifier .background( - MaterialTheme.colorScheme.surfaceContainer, + itemCountBackgroundColor, MaterialTheme.shapes.medium ) .padding(horizontal = 8.dp, vertical = 4.dp), diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/library/components/LibraryTopBar.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/library/components/LibraryTopBar.kt index 5251e7c5..dd0113e9 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/library/components/LibraryTopBar.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/library/components/LibraryTopBar.kt @@ -1,5 +1,8 @@ package ua.acclorite.book_story.presentation.screens.library.components +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -168,12 +171,20 @@ fun LibraryTopBar( }, content3Actions = { MoreDropDown() + }, + customContent = { + LibraryTabRow( + onEvent = onEvent, + books = state.value.categorizedBooks, + itemCountBackgroundColor = animateColorAsState( + if (state.value.hasSelectedItems) MaterialTheme.colorScheme.surfaceContainerHighest + else MaterialTheme.colorScheme.surfaceContainer, + animationSpec = spring(stiffness = Spring.StiffnessMediumLow), + label = stringResource(id = R.string.top_app_bar_anim_content_desc) + ).value, + pagerState = pagerState + ) } ) - LibraryTabRow( - onEvent = onEvent, - books = state.value.categorizedBooks, - pagerState = pagerState - ) } } \ No newline at end of file