From e2e58ea955b7b23a355c8c6d5308195afaed2b8e Mon Sep 17 00:00:00 2001 From: AllenTom Date: Thu, 18 Jul 2024 11:53:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../riderpro/BottomNavigationPlaceholder.kt | 9 +- .../java/com/aiosman/riderpro/CommentModal.kt | 21 +- .../java/com/aiosman/riderpro/MainActivity.kt | 6 +- .../java/com/aiosman/riderpro/PagingSimple.kt | 6 +- .../main/java/com/aiosman/riderpro/Post.kt | 329 ++++++++++++++---- 5 files changed, 286 insertions(+), 85 deletions(-) diff --git a/app/src/main/java/com/aiosman/riderpro/BottomNavigationPlaceholder.kt b/app/src/main/java/com/aiosman/riderpro/BottomNavigationPlaceholder.kt index 130ccb3..6074343 100644 --- a/app/src/main/java/com/aiosman/riderpro/BottomNavigationPlaceholder.kt +++ b/app/src/main/java/com/aiosman/riderpro/BottomNavigationPlaceholder.kt @@ -1,19 +1,24 @@ package com.aiosman.riderpro +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBars import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity @Composable -fun BottomNavigationPlaceholder() { +fun BottomNavigationPlaceholder( + color: Color? = null +) { val navigationBarHeight = with(LocalDensity.current) { WindowInsets.navigationBars.getBottom(this).toDp() } Box( - modifier = Modifier.height(navigationBarHeight) + modifier = Modifier.height(navigationBarHeight).fillMaxWidth().background(color ?: Color.Transparent) ) } \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/CommentModal.kt b/app/src/main/java/com/aiosman/riderpro/CommentModal.kt index 04f9696..fc917bc 100644 --- a/app/src/main/java/com/aiosman/riderpro/CommentModal.kt +++ b/app/src/main/java/com/aiosman/riderpro/CommentModal.kt @@ -6,20 +6,29 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +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.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight @@ -31,9 +40,15 @@ import androidx.compose.ui.unit.sp @Preview @Composable fun CommentModalContent() { - Column( - modifier = Modifier + val insets = WindowInsets + val imePadding = insets.ime.getBottom(density = LocalDensity.current) + var bottomPadding by remember { mutableStateOf(0.dp) } + LaunchedEffect(imePadding) { + bottomPadding = imePadding.dp + } + Column( + modifier = Modifier.height(500.dp) ) { Box( modifier = Modifier @@ -58,7 +73,7 @@ fun CommentModalContent() { .padding(horizontal = 16.dp) .weight(1f) ) { - CommentsSection() + CommentsSection{} } Box( modifier = Modifier diff --git a/app/src/main/java/com/aiosman/riderpro/MainActivity.kt b/app/src/main/java/com/aiosman/riderpro/MainActivity.kt index 52388ec..1d061f9 100644 --- a/app/src/main/java/com/aiosman/riderpro/MainActivity.kt +++ b/app/src/main/java/com/aiosman/riderpro/MainActivity.kt @@ -125,11 +125,9 @@ fun NavigationController(navController: NavHostController) { OfficialPhotographer() } composable(route = "Post") { - Box( - modifier = Modifier.padding(bottom = navigationBarHeight) - ) { + PostPage() - } + } composable(route = "ModificationList") { ModificationListScreen() diff --git a/app/src/main/java/com/aiosman/riderpro/PagingSimple.kt b/app/src/main/java/com/aiosman/riderpro/PagingSimple.kt index d1fc81a..a81a00b 100644 --- a/app/src/main/java/com/aiosman/riderpro/PagingSimple.kt +++ b/app/src/main/java/com/aiosman/riderpro/PagingSimple.kt @@ -29,6 +29,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -279,7 +280,10 @@ fun MomentBottomOperateRowGroup(modifier: Modifier) { if (showCommentModal) { ModalBottomSheet( onDismissRequest = { showCommentModal = false }, - containerColor = Color.White + containerColor = Color.White, + sheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = true + ) ) { CommentModalContent() } diff --git a/app/src/main/java/com/aiosman/riderpro/Post.kt b/app/src/main/java/com/aiosman/riderpro/Post.kt index 320ecb9..4528aa9 100644 --- a/app/src/main/java/com/aiosman/riderpro/Post.kt +++ b/app/src/main/java/com/aiosman/riderpro/Post.kt @@ -1,5 +1,7 @@ package com.aiosman.riderpro +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.animateContentSize import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -18,7 +20,9 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape @@ -34,6 +38,14 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -45,6 +57,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import kotlinx.coroutines.launch fun makeMockImages(): List { return listOf( @@ -53,40 +66,49 @@ fun makeMockImages(): List { PostImage(R.drawable.rider_pro_moment_demo_1, "Image 3") ) } + @Preview @Composable fun PostPage() { - Scaffold( - modifier = Modifier.fillMaxSize(), - bottomBar = { - BottomNavigationBar() - } - ) { it - Column( - modifier = Modifier.fillMaxSize() + var showCollapseContent by remember { mutableStateOf(true) } + val scrollState = rememberLazyListState() + + StatusBarMaskLayout { + Scaffold( + modifier = Modifier.fillMaxSize(), + bottomBar = { BottomNavigationBar() } ) { - Header() - Box( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(1f) - ){ - PostImageView(makeMockImages()) + it + Column(modifier = Modifier.fillMaxSize()) { + Header() + Column(modifier = Modifier.animateContentSize()) { + AnimatedVisibility(visible = showCollapseContent) { + // collapse content + Column { + Box( + modifier = Modifier + .fillMaxWidth() + .aspectRatio(1f) + + ) { + PostImageView(makeMockImages()) + } + PostDetails() + } + } + } + Box( + modifier = Modifier + .fillMaxWidth() + + ) { + CommentsSection(scrollState) { + showCollapseContent = it + } + } } - - PostDetails() - Box( - modifier = Modifier - .fillMaxWidth() - .weight(1f) - ){ - CommentsSection() - - } - } } - } @Composable @@ -114,27 +136,43 @@ fun Header() { ) Spacer(modifier = Modifier.width(8.dp)) Text(text = "Diego Morata", fontWeight = FontWeight.Bold) - Box(modifier = Modifier - .height(20.dp).wrapContentWidth() - .padding(start = 6.dp), - contentAlignment = Alignment.Center){ - Image(modifier = Modifier.height(18.dp),painter = painterResource(id = R.drawable.follow_bg), contentDescription = "") - Text(text = "FOLLOW", fontSize = 12.sp, color = Color.White, style = TextStyle(fontWeight = FontWeight.Bold)) + Box( + modifier = Modifier + .height(20.dp) + .wrapContentWidth() + .padding(start = 6.dp), + contentAlignment = Alignment.Center + ) { + Image( + modifier = Modifier.height(18.dp), + painter = painterResource(id = R.drawable.follow_bg), + contentDescription = "" + ) + Text( + text = "FOLLOW", + fontSize = 12.sp, + color = Color.White, + style = TextStyle(fontWeight = FontWeight.Bold) + ) } } } + data class PostImage( val imgRes: Int, val description: String ) + @OptIn(ExperimentalFoundationApi::class) @Composable fun PostImageView(images: List) { - val pagerState = rememberPagerState(pageCount = {images.size}) + val pagerState = rememberPagerState(pageCount = { images.size }) Column { HorizontalPager( state = pagerState, - modifier = Modifier.weight(1f).fillMaxWidth() + modifier = Modifier + .weight(1f) + .fillMaxWidth() ) { page -> Image( painter = painterResource(id = images[page].imgRes), @@ -147,7 +185,8 @@ fun PostImageView(images: List) { // Indicator container Row( modifier = Modifier - .padding(8.dp).fillMaxWidth(), + .padding(8.dp) + .fillMaxWidth(), horizontalArrangement = Arrangement.Center ) { images.forEachIndexed { index, _ -> @@ -156,7 +195,11 @@ fun PostImageView(images: List) { .size(8.dp) .clip(CircleShape) - .background(if (pagerState.currentPage == index) Color.Red else Color.Gray.copy(alpha = 0.5f)) + .background( + if (pagerState.currentPage == index) Color.Red else Color.Gray.copy( + alpha = 0.5f + ) + ) .padding(4.dp) @@ -176,6 +219,7 @@ fun PostDetails() { Text(text = "共231条评论") } } + fun MakeMockComments(): List { return listOf( Comment( @@ -207,6 +251,118 @@ fun MakeMockComments(): List { likes = 200, replies = emptyList() ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), + Comment( + name = "Diego Morata", + comment = "这里太适合骑行了", + date = "3 days ago", + likes = 200, + replies = emptyList() + ), Comment( name = "Diego Morata", comment = "这里太适合骑行了", @@ -218,16 +374,30 @@ fun MakeMockComments(): List { } @Composable -fun CommentsSection() { +fun CommentsSection(scrollState: LazyListState = rememberLazyListState(), onWillCollapse: (Boolean) -> Unit) { val items = MakeMockComments() - LazyColumn (modifier = Modifier - .padding(16.dp) - .fillMaxHeight()) { - items(items) {comment -> + LazyColumn( + state = scrollState, modifier = Modifier + .padding(start = 16.dp, end = 16.dp) + .fillMaxHeight() + ) { + items(items) { comment -> CommentItem(comment) } } + + // Detect scroll direction and update showCollapseContent + val coroutineScope = rememberCoroutineScope() + LaunchedEffect(scrollState) { + coroutineScope.launch { + snapshotFlow { scrollState.firstVisibleItemScrollOffset } + .collect { offset -> + onWillCollapse(offset == 0) + } + } + } } + data class Comment( val name: String, val comment: String, @@ -276,45 +446,54 @@ fun CommentItem(comment: Comment) { @OptIn(ExperimentalMaterial3Api::class) @Composable fun BottomNavigationBar() { - - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp).background(Color.White) + Column( + modifier = Modifier.background(Color.White) ) { - // grey round box - Box( + Row( + verticalAlignment = Alignment.CenterVertically, modifier = Modifier - .padding(8.dp) - .clip(RoundedCornerShape(16.dp)) - .background(Color.Gray.copy(alpha = 0.1f)) - .weight(1f) - .height(31.dp) - .padding(8.dp) + .padding(horizontal = 16.dp, vertical = 8.dp) + .background(Color.White) ) { - Row( - verticalAlignment = Alignment.CenterVertically + // grey round box + Box( + modifier = Modifier + .padding(8.dp) + .clip(RoundedCornerShape(16.dp)) + .background(Color.Gray.copy(alpha = 0.1f)) + .weight(1f) + .height(31.dp) + .padding(8.dp) ) { - Icon(Icons.Filled.Edit, contentDescription = "Send") - Spacer(modifier = Modifier.width(8.dp)) - Text(text = "说点什么", fontSize = 12.sp) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon(Icons.Filled.Edit, contentDescription = "Send") + Spacer(modifier = Modifier.width(8.dp)) + Text(text = "说点什么", fontSize = 12.sp) + } } - } - IconButton( - onClick = { /*TODO*/ }) { - Icon(Icons.Filled.Favorite, contentDescription = "Send") - } - Text(text = "2077") - IconButton( - onClick = { /*TODO*/ }) { - Icon(Icons.Filled.Star, contentDescription = "Send") - } - Text(text = "2077") - IconButton( - onClick = { /*TODO*/ }) { - Icon(Icons.Filled.CheckCircle, contentDescription = "Send") - } - Text(text = "2077") + IconButton( + onClick = { /*TODO*/ }) { + Icon(Icons.Filled.Favorite, contentDescription = "Send") + } + Text(text = "2077") + IconButton( + onClick = { /*TODO*/ }) { + Icon(Icons.Filled.Star, contentDescription = "Send") + } + Text(text = "2077") + IconButton( + onClick = { /*TODO*/ }) { + Icon(Icons.Filled.CheckCircle, contentDescription = "Send") + } + Text(text = "2077") + } + BottomNavigationPlaceholder( + color = Color.White + ) } + } \ No newline at end of file