diff --git a/README.md b/README.md index 576ff89..7b6ea8b 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ repositories { ```groovy dependencies { - implementation 'com.github.germainkevinbusiness:CollapsingTopBarCompose:1.0.0-alpha06' + implementation 'com.github.germainkevinbusiness:CollapsingTopBarCompose:1.0.0-alpha07' } ``` @@ -49,7 +49,7 @@ In order to use a ```CollapsingTopBar```, you first need to create a ```TopBarSc ```kotlin val scrollBehavior = remember { - CollapsingTopBarDefaults.behaviorOnScroll( + CollapsingTopBarDefaults.scrollBehavior( isAlwaysCollapsed = false, isInitiallyCollapsed = true, collapsedTopBarHeight = 56.dp, @@ -81,7 +81,7 @@ So when we put it all together we got: ```kotlin val scrollBehavior = remember { - CollapsingTopBarDefaults.behaviorOnScroll( + CollapsingTopBarDefaults.scrollBehavior( isAlwaysCollapsed = false, isInitiallyCollapsed = true, collapsedTopBarHeight = 56.dp, diff --git a/app/build.gradle b/app/build.gradle index e966efc..715a918 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,7 +11,7 @@ android { minSdk 21 targetSdk 32 versionCode 1 - versionName "1.0.0-alpha06" + versionName "1.0.0-alpha07" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { @@ -55,6 +55,7 @@ dependencies { // Compose dependencies + implementation "androidx.compose.material:material:$compose_version" implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation "androidx.compose.material:material-icons-extended:$compose_version" diff --git a/app/src/main/java/com/germainkevin/collapsingtopbarcompose/Components.kt b/app/src/main/java/com/germainkevin/collapsingtopbarcompose/Components.kt deleted file mode 100644 index a35d9cc..0000000 --- a/app/src/main/java/com/germainkevin/collapsingtopbarcompose/Components.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.germainkevin.collapsingtopbarcompose - -import android.content.Context -import android.widget.Toast -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Menu -import androidx.compose.material.icons.outlined.MoreVert -import androidx.compose.material.icons.outlined.Search -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.germainkevin.collapsingtopbar.CollapsingTopBar -import com.germainkevin.collapsingtopbar.TopBarScrollBehavior - -@Composable -fun ContactListNames(context: Context, contactName: String) { - Row( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight() - .clickable { createToast(context, contactName) }, - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = contactName, - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurface, - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight() - .padding(16.dp) - ) - Divider() - } -} - -@Composable -fun MoreMenuIcons() { - IconButton(onClick = { }) { - Icon( - Icons.Outlined.Search, - contentDescription = stringResource(id = R.string.action_search), - tint = MaterialTheme.colorScheme.primary - ) - } - IconButton(onClick = { - }) { - Icon( - Icons.Outlined.MoreVert, - contentDescription = stringResource(id = R.string.more_menu_desc), - tint = MaterialTheme.colorScheme.primary - ) - } -} - -fun createToast(context: Context, message: String) = - Toast.makeText(context, message, Toast.LENGTH_LONG).show() \ No newline at end of file diff --git a/app/src/main/java/com/germainkevin/collapsingtopbarcompose/MainActivity.kt b/app/src/main/java/com/germainkevin/collapsingtopbarcompose/MainActivity.kt index 0ae6c25..dd18b9c 100644 --- a/app/src/main/java/com/germainkevin/collapsingtopbarcompose/MainActivity.kt +++ b/app/src/main/java/com/germainkevin/collapsingtopbarcompose/MainActivity.kt @@ -3,13 +3,22 @@ package com.germainkevin.collapsingtopbarcompose import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.Scaffold import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Email +import androidx.compose.material.icons.filled.Face +import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.Menu +import androidx.compose.material.rememberScaffoldState import androidx.compose.material3.* import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext @@ -19,9 +28,12 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.germainkevin.collapsingtopbar.CollapsingTopBar import com.germainkevin.collapsingtopbar.CollapsingTopBarDefaults +import com.germainkevin.collapsingtopbarcompose.ui.ContactListNames +import com.germainkevin.collapsingtopbarcompose.ui.LeftDrawer +import com.germainkevin.collapsingtopbarcompose.ui.MoreMenuIcons import com.germainkevin.collapsingtopbarcompose.ui.theme.CollapsingTopBarComposeTheme +import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterial3Api::class) class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -32,18 +44,46 @@ class MainActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { + val coroutineScope = rememberCoroutineScope() + val scaffoldState = rememberScaffoldState() + + val openLeftDrawer: () -> Unit = { + coroutineScope.launch { + scaffoldState.drawerState.open() + } + } + val closeLeftDrawer: () -> Unit = { + coroutineScope.launch { + scaffoldState.drawerState.close() + } + } + + // icons to mimic drawer destinations + val items = + listOf(Icons.Default.Favorite, Icons.Default.Face, Icons.Default.Email) + val context = LocalContext.current val contactNames = context.resources.getStringArray(R.array.contactNames) - // A scrollBehavior determines the behavior of the CollapsingTopBar - // when it is being scrolled and also to track the nestedScroll events + + /** + * A scrollBehavior determines the behavior of the CollapsingTopBar when it is + * being scrolled and also to track the nestedScroll events*/ val scrollBehavior = remember { - CollapsingTopBarDefaults.behaviorOnScroll( + CollapsingTopBarDefaults.scrollBehavior( isAlwaysCollapsed = false, + isExpandedWhenFirstDisplayed = false, expandedTopBarMaxHeight = 256.dp, ) } Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + scaffoldState = scaffoldState, + drawerContent = { + LeftDrawer( + closeLeftDrawer = closeLeftDrawer, + drawerItems = items + ) + }, topBar = { CollapsingTopBar( scrollBehavior = scrollBehavior, @@ -54,7 +94,7 @@ class MainActivity : ComponentActivity() { style = LocalTextStyle.current.copy( fontSize = 24.sp, fontWeight = FontWeight.Normal, - color = MaterialTheme.colorScheme.primary + color = MaterialTheme.colorScheme.onPrimary ) ) }, @@ -66,16 +106,16 @@ class MainActivity : ComponentActivity() { ), style = LocalTextStyle.current.copy( fontWeight = FontWeight.Normal, - color = MaterialTheme.colorScheme.onPrimaryContainer + color = MaterialTheme.colorScheme.onPrimary ) ) }, navigationIcon = { - IconButton(onClick = { }) { + IconButton(onClick = openLeftDrawer) { Icon( imageVector = Icons.Filled.Menu, contentDescription = stringResource(id = R.string.hamburger_menu), - tint = MaterialTheme.colorScheme.primary + tint = MaterialTheme.colorScheme.onPrimary ) } }, @@ -84,9 +124,13 @@ class MainActivity : ComponentActivity() { }, content = { innerPadding -> LazyColumn( + modifier = Modifier.background(MaterialTheme.colorScheme.background), contentPadding = innerPadding, verticalArrangement = Arrangement.spacedBy(8.dp) ) { + item { + Spacer(modifier = Modifier.height(6.dp)) + } items(count = contactNames.size) { ContactListNames(context, contactNames[it]) } diff --git a/app/src/main/java/com/germainkevin/collapsingtopbarcompose/Utils.kt b/app/src/main/java/com/germainkevin/collapsingtopbarcompose/Utils.kt new file mode 100644 index 0000000..ff5a002 --- /dev/null +++ b/app/src/main/java/com/germainkevin/collapsingtopbarcompose/Utils.kt @@ -0,0 +1,7 @@ +package com.germainkevin.collapsingtopbarcompose + +import android.content.Context +import android.widget.Toast + +fun createToast(context: Context, message: String) = + Toast.makeText(context, message, Toast.LENGTH_LONG).show() \ No newline at end of file diff --git a/app/src/main/java/com/germainkevin/collapsingtopbarcompose/ui/Components.kt b/app/src/main/java/com/germainkevin/collapsingtopbarcompose/ui/Components.kt new file mode 100644 index 0000000..f784713 --- /dev/null +++ b/app/src/main/java/com/germainkevin/collapsingtopbarcompose/ui/Components.kt @@ -0,0 +1,89 @@ +package com.germainkevin.collapsingtopbarcompose.ui + +import android.content.Context +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.MoreVert +import androidx.compose.material.icons.outlined.Search +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.germainkevin.collapsingtopbarcompose.R +import com.germainkevin.collapsingtopbarcompose.createToast + +@Composable +fun ContactListNames(context: Context, contactName: String) { + Row( + modifier = Modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.background) + .clickable { createToast(context, contactName) }, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = contactName, + style = MaterialTheme.typography.bodyLarge + .copy(color = MaterialTheme.colorScheme.onBackground), + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) + } +} + +@Composable +fun MoreMenuIcons() { + IconButton(onClick = { }) { + Icon( + Icons.Outlined.Search, + contentDescription = stringResource(id = R.string.action_search), + tint = MaterialTheme.colorScheme.onPrimary + ) + } + IconButton(onClick = { + }) { + Icon( + Icons.Outlined.MoreVert, + contentDescription = stringResource(id = R.string.more_menu_desc), + tint = MaterialTheme.colorScheme.onPrimary + ) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun LeftDrawer( + drawerItems: List, + closeLeftDrawer: () -> Unit +) { + val selectedItem = remember { mutableStateOf(drawerItems[0]) } + Column( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + ) { + drawerItems.forEach { item -> + NavigationDrawerItem( + icon = { Icon(item, contentDescription = null) }, + label = { Text(item.name) }, + selected = item == selectedItem.value, + onClick = { + selectedItem.value = item + closeLeftDrawer() + }, + shape = RoundedCornerShape(topEnd = 24.dp, bottomEnd = 24.dp), + badge = { Text(text = " 20") }, + modifier = Modifier.padding(end = 16.dp, top = 12.dp, bottom = 12.dp) + ) + } + } +} \ No newline at end of file diff --git a/collapsingtopbar/build.gradle b/collapsingtopbar/build.gradle index 93615ad..a79dfff 100644 --- a/collapsingtopbar/build.gradle +++ b/collapsingtopbar/build.gradle @@ -24,7 +24,7 @@ afterEvaluate { // You can then customize attributes of the publication as shown below. groupId = 'com.germainkevin.collapsingtopbarcompose' artifactId = 'collapsingtopbarcompose' - version = '1.0.0-alpha06' + version = '1.0.0-alpha07' } } } diff --git a/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/CollapsingTopBar.kt b/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/CollapsingTopBar.kt index a2681d3..7403b85 100644 --- a/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/CollapsingTopBar.kt +++ b/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/CollapsingTopBar.kt @@ -46,7 +46,7 @@ fun CollapsingTopBar( navigationIcon: @Composable (() -> Unit)? = null, actions: @Composable RowScope.() -> Unit = {}, centeredTitleAndSubtitle: Boolean = true, - colors: CollapsingTopBarColors = CollapsingTopBarDefaults.collapsingTopBarColors(), + colors: CollapsingTopBarColors = CollapsingTopBarDefaults.colors(), contentPadding: PaddingValues = CollapsingTopBarDefaults.ContentPadding, elevation: Dp = 0.dp, scrollBehavior: TopBarScrollBehavior diff --git a/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/CollapsingTopBarColors.kt b/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/CollapsingTopBarColors.kt index 9690a73..c4c22a5 100644 --- a/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/CollapsingTopBarColors.kt +++ b/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/CollapsingTopBarColors.kt @@ -18,7 +18,7 @@ interface CollapsingTopBarColors { var contentColor: Color } -class CollapsingTopBarColorsImpl( +class DefaultCollapsingTopBarColors( override var backgroundColor: Color, override var contentColor: Color ) : CollapsingTopBarColors \ No newline at end of file diff --git a/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/CollapsingTopBarDefaults.kt b/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/CollapsingTopBarDefaults.kt index 6b87018..3f7d14b 100644 --- a/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/CollapsingTopBarDefaults.kt +++ b/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/CollapsingTopBarDefaults.kt @@ -1,8 +1,8 @@ package com.germainkevin.collapsingtopbar import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.material.MaterialTheme import androidx.compose.material.contentColorFor +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -15,7 +15,8 @@ object CollapsingTopBarDefaults { PaddingValues(start = appBarHorizontalPadding, end = appBarHorizontalPadding) /** - * Specifies how the [CollapsingTopBar] should behave when a [Modifier.nestedScroll] is detected. + * Specifies how the [CollapsingTopBar] should behave when a [Modifier.nestedScroll] + * is detected. * * @param isAlwaysCollapsed This will make this [CollapsingTopBar] stay collapsed and stay with * the [collapsedTopBarHeight] height. It's false by default @@ -27,7 +28,7 @@ object CollapsingTopBarDefaults { * @param expandedTopBarMaxHeight The height of the [CollapsingTopBar] when it's expended, * the default value is [defaultMaximumTopBarHeight] * */ - fun behaviorOnScroll( + fun scrollBehavior( isAlwaysCollapsed: Boolean = false, isExpandedWhenFirstDisplayed: Boolean = true, collapsedTopBarHeight: Dp = defaultMinimumTopBarHeight, @@ -41,10 +42,12 @@ object CollapsingTopBarDefaults { /** * Default colors used in the [CollapsingTopBar] + * @param backgroundColor The background color of the [CollapsingTopBar] + * @param contentColor The content color inside of the [CollapsingTopBar] * */ @Composable - fun collapsingTopBarColors( - backgroundColor: Color = MaterialTheme.colors.background, + fun colors( + backgroundColor: Color = MaterialTheme.colorScheme.primary, contentColor: Color = contentColorFor(backgroundColor) - ): CollapsingTopBarColors = CollapsingTopBarColorsImpl(backgroundColor, contentColor) + ): CollapsingTopBarColors = DefaultCollapsingTopBarColors(backgroundColor, contentColor) } \ No newline at end of file diff --git a/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/TopBarScrollBehavior.kt b/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/TopBarScrollBehavior.kt index 431215f..5b04eb5 100644 --- a/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/TopBarScrollBehavior.kt +++ b/collapsingtopbar/src/main/java/com/germainkevin/collapsingtopbar/TopBarScrollBehavior.kt @@ -11,13 +11,12 @@ import androidx.compose.ui.unit.Dp /** - * A TopBarScrollBehavior defines how a [CollapsingTopBar] should behave when the content under - * it is scrolled. + * Defines how a [CollapsingTopBar] should behave during a [Modifier.nestedScroll] event. * */ interface TopBarScrollBehavior { /** - * This will make this [CollapsingTopBar] never expand */ + * When set to true, it will make this [CollapsingTopBar] never expand and stay collapsed */ var isAlwaysCollapsed: Boolean /** @@ -36,17 +35,24 @@ interface TopBarScrollBehavior { var currentTopBarHeight: Dp /** - * The offset that changes the height of the [CollapsingTopBar] + * The offset that is added to the height of the [CollapsingTopBar] based on scroll events * */ var topBarOffset: Float /** - * Tracks how many times [topBarOffset]'s value is 0.0f. Useful when [isInitiallyCollapsed] is - * set to true, because we want the [CollapsingTopBar] to start changing size only after the - * first scrolling up event is detected through our [nestedScrollConnection], and because - * the first scrolling up event tend to be the third time [topBarOffset] is equal to 0.0f, - * all we gotta do is check when [topBarOffset] is equal to 0.0f 3 times, then let - * [currentTopBarHeight] be equal to [expandedTopBarMaxHeight] + [topBarOffset] + * When true, Sets the [CollapsingTopBar] to an expanded state when first displayed on the UI + * by setting the [CollapsingTopBar]'s height to [expandedTopBarMaxHeight] + * */ + var isExpandedWhenFirstDisplayed: Boolean + + /** + * Tracks how many times [topBarOffset]'s value is 0.0f. Useful when + * [isExpandedWhenFirstDisplayed] is set to false, because we want the [CollapsingTopBar] to + * start changing size only after the first scrolling up event is detected through our + * [nestedScrollConnection], and because the first scrolling up event tend to be the third time + * [topBarOffset] is equal to 0.0f, all we gotta do is check when [topBarOffset] is equal to + * 0.0f 3 times, then let [currentTopBarHeight] be equal to + * [expandedTopBarMaxHeight] + [topBarOffset] * */ var trackOffSetIsZero: Int @@ -60,12 +66,6 @@ interface TopBarScrollBehavior { * */ var offsetLimit: Float - /** - * When true, Sets the [CollapsingTopBar] to an expanded state when first displayed on the UI - * by setting the [CollapsingTopBar]'s height to [expandedTopBarMaxHeight] - * */ - var isExpandedWhenFirstDisplayed: Boolean - /** * A [NestedScrollConnection] that should be attached to a [Modifier.nestedScroll] in order to * keep track of the scroll events. @@ -91,7 +91,9 @@ class DefaultBehaviorOnScroll( override var trackOffSetIsZero: Int by mutableStateOf(0) override var currentTopBarHeight: Dp by mutableStateOf( - if (isAlwaysCollapsed || !isExpandedWhenFirstDisplayed) collapsedTopBarHeight + if (isAlwaysCollapsed) collapsedTopBarHeight + else if (!isExpandedWhenFirstDisplayed) collapsedTopBarHeight + else if (!isAlwaysCollapsed && isExpandedWhenFirstDisplayed) expandedTopBarMaxHeight else expandedTopBarMaxHeight )