新增发现页
新增发现页,展示所有动态的缩略图,点击可跳转到对应动态详情页。
This commit is contained in:
@@ -43,6 +43,7 @@ import com.aiosman.riderpro.ui.login.LoginPage
|
||||
import com.aiosman.riderpro.ui.login.SignupScreen
|
||||
import com.aiosman.riderpro.ui.login.UserAuthScreen
|
||||
import com.aiosman.riderpro.ui.index.tabs.message.NotificationsScreen
|
||||
import com.aiosman.riderpro.ui.index.tabs.search.SearchScreen
|
||||
import com.aiosman.riderpro.ui.modification.EditModificationScreen
|
||||
import com.aiosman.riderpro.ui.post.NewPostImageGridScreen
|
||||
import com.aiosman.riderpro.ui.post.NewPostScreen
|
||||
@@ -75,6 +76,7 @@ sealed class NavigationRoute(
|
||||
data object ChangePasswordScreen : NavigationRoute("ChangePasswordScreen")
|
||||
data object FavouritesScreen : NavigationRoute("FavouritesScreen")
|
||||
data object NewPostImageGrid : NavigationRoute("NewPostImageGrid")
|
||||
data object Search : NavigationRoute("Search")
|
||||
}
|
||||
|
||||
|
||||
@@ -219,6 +221,13 @@ fun NavigationController(
|
||||
composable(route = NavigationRoute.NewPostImageGrid.route) {
|
||||
NewPostImageGridScreen()
|
||||
}
|
||||
composable(route = NavigationRoute.Search.route) {
|
||||
CompositionLocalProvider(
|
||||
LocalAnimatedContentScope provides this,
|
||||
) {
|
||||
SearchScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.aiosman.riderpro.ui.index.tabs.add.AddPage
|
||||
import com.aiosman.riderpro.ui.index.tabs.message.NotificationsScreen
|
||||
import com.aiosman.riderpro.ui.index.tabs.moment.MomentsList
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.ProfilePage
|
||||
import com.aiosman.riderpro.ui.index.tabs.search.DiscoverScreen
|
||||
import com.aiosman.riderpro.ui.index.tabs.search.SearchScreen
|
||||
import com.aiosman.riderpro.ui.index.tabs.shorts.ShortVideo
|
||||
import com.aiosman.riderpro.ui.index.tabs.street.StreetPage
|
||||
@@ -124,7 +125,7 @@ fun IndexScreen() {
|
||||
) { page ->
|
||||
when (page) {
|
||||
0 -> Home()
|
||||
1 -> SearchScreen()
|
||||
1 -> DiscoverScreen()
|
||||
2 -> Add()
|
||||
3 -> Notifications()
|
||||
4 -> Profile()
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
package com.aiosman.riderpro.ui.index.tabs.search
|
||||
|
||||
import android.graphics.drawable.VectorDrawable
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
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.asPaddingValues
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
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.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import com.aiosman.riderpro.ui.post.PostViewModel
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Preview
|
||||
@Composable
|
||||
fun DiscoverScreen() {
|
||||
val model = DiscoverViewModel
|
||||
val navController = LocalNavController.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
||||
val navigationBarPaddings =
|
||||
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
|
||||
LaunchedEffect(Unit) {
|
||||
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(Color.White)
|
||||
.fillMaxSize()
|
||||
.padding(bottom = navigationBarPaddings)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(statusBarPaddingValues.calculateTopPadding()))
|
||||
|
||||
SearchButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, start = 24.dp, end = 24.dp),
|
||||
) {
|
||||
navController.navigate("Search")
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp)
|
||||
.weight(1f)
|
||||
) {
|
||||
DiscoverView()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SearchButton(
|
||||
modifier: Modifier = Modifier,
|
||||
clickAction: () -> Unit = {}
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.clip(shape = RoundedCornerShape(8.dp))
|
||||
|
||||
.background(Color(0xFFEEEEEE))
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
.noRippleClickable {
|
||||
clickAction()
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Search,
|
||||
contentDescription = null
|
||||
)
|
||||
Box {
|
||||
Text(
|
||||
text = "Search",
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
color = Color(0xFF9E9E9E),
|
||||
fontSize = 18.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DiscoverView() {
|
||||
val model = DiscoverViewModel
|
||||
var dataFlow = model.discoverMomentsFlow
|
||||
var moments = dataFlow.collectAsLazyPagingItems()
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(3),
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
items(moments.itemCount) { idx ->
|
||||
val momentItem = moments[idx] ?: return@items
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1f)
|
||||
.padding(2.dp)
|
||||
|
||||
.noRippleClickable {
|
||||
PostViewModel.preTransit(momentItem)
|
||||
navController.navigate(
|
||||
NavigationRoute.Post.route.replace(
|
||||
"{id}",
|
||||
momentItem.id.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
) {
|
||||
CustomAsyncImage(
|
||||
imageUrl = momentItem.images[0].thumbnail,
|
||||
contentDescription = "",
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
context = context
|
||||
)
|
||||
if (momentItem.images.size > 1) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp, end = 8.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.size(24.dp),
|
||||
painter = painterResource(R.drawable.rider_pro_images),
|
||||
contentDescription = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.aiosman.riderpro.ui.index.tabs.search
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import com.aiosman.riderpro.data.MomentService
|
||||
import com.aiosman.riderpro.entity.MomentEntity
|
||||
import com.aiosman.riderpro.entity.MomentPagingSource
|
||||
import com.aiosman.riderpro.entity.MomentRemoteDataSource
|
||||
import com.aiosman.riderpro.entity.MomentServiceImpl
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object DiscoverViewModel:ViewModel() {
|
||||
private val momentService: MomentService = MomentServiceImpl()
|
||||
private val _discoverMomentsFlow =
|
||||
MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
|
||||
val discoverMomentsFlow = _discoverMomentsFlow.asStateFlow()
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
pagingSourceFactory = {
|
||||
MomentPagingSource(
|
||||
MomentRemoteDataSource(momentService),
|
||||
)
|
||||
}
|
||||
).flow.cachedIn(viewModelScope).collectLatest {
|
||||
_discoverMomentsFlow.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
@@ -37,6 +38,8 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
@@ -68,19 +71,28 @@ fun SearchScreen() {
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
||||
val navigationBarPaddings =
|
||||
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(Color.White)
|
||||
.fillMaxSize()
|
||||
|
||||
.padding(bottom = navigationBarPaddings)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(statusBarPaddingValues.calculateTopPadding()))
|
||||
SearchInput(
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 16.dp, start = 24.dp, end = 24.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp, start = 24.dp, end = 24.dp),
|
||||
text = model.searchText,
|
||||
onTextChange = {
|
||||
model.searchText = it
|
||||
@@ -89,7 +101,8 @@ fun SearchScreen() {
|
||||
model.search()
|
||||
// hide ime
|
||||
keyboardController?.hide() // Hide the keyboard
|
||||
}
|
||||
},
|
||||
focusRequester = focusRequester
|
||||
)
|
||||
if (model.showResult) {
|
||||
Spacer(modifier = Modifier.padding(8.dp))
|
||||
@@ -115,8 +128,6 @@ fun SearchScreen() {
|
||||
pagerState = pagerState
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +136,8 @@ fun SearchInput(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String = "",
|
||||
onTextChange: (String) -> Unit = {},
|
||||
onSearch: () -> Unit = {}
|
||||
onSearch: () -> Unit = {},
|
||||
focusRequester: FocusRequester = remember { FocusRequester() }
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
@@ -157,7 +169,8 @@ fun SearchInput(
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(start = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
.fillMaxWidth()
|
||||
.focusRequester(focusRequester),
|
||||
singleLine = true,
|
||||
textStyle = TextStyle(
|
||||
fontSize = 18.sp
|
||||
@@ -238,7 +251,10 @@ fun UserItem(accountProfile: AccountProfileEntity) {
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(16.dp).noRippleClickable {
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
.noRippleClickable {
|
||||
navController.navigate("AccountProfile/${accountProfile.id}")
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
|
||||
5
app/src/main/res/drawable/rider_pro_images.xml
Normal file
5
app/src/main/res/drawable/rider_pro_images.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:alpha="0.77" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M22,16L22,4c0,-1.1 -0.9,-2 -2,-2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zM11.4,12.53l1.63,2.18 2.58,-3.22c0.2,-0.25 0.58,-0.25 0.78,0l2.96,3.7c0.26,0.33 0.03,0.81 -0.39,0.81L9,16c-0.41,0 -0.65,-0.47 -0.4,-0.8l2,-2.67c0.2,-0.26 0.6,-0.26 0.8,0zM2,7v13c0,1.1 0.9,2 2,2h13c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L5,20c-0.55,0 -1,-0.45 -1,-1L4,7c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1z"/>
|
||||
|
||||
</vector>
|
||||
Reference in New Issue
Block a user