实现了 Agent 和 Profile 数据类,添加了 AddAgent 界面
This commit is contained in:
@@ -61,6 +61,7 @@ import com.aiosman.ravenow.Messaging
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.ui.NavigationRoute
|
||||
import com.aiosman.ravenow.ui.index.tabs.add.AddPage
|
||||
import com.aiosman.ravenow.ui.index.tabs.ai.Agent
|
||||
import com.aiosman.ravenow.ui.index.tabs.message.NotificationsScreen
|
||||
import com.aiosman.ravenow.ui.index.tabs.moment.MomentsList
|
||||
import com.aiosman.ravenow.ui.index.tabs.profile.ProfileWrap
|
||||
@@ -332,7 +333,7 @@ fun IndexScreen() {
|
||||
) { page ->
|
||||
when (page) {
|
||||
0 -> Home()
|
||||
1 -> DiscoverScreen()
|
||||
1 -> Agent()
|
||||
2 -> Add()
|
||||
3 -> Notifications()
|
||||
4 -> Profile()
|
||||
|
||||
250
app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/Agent.kt
Normal file
250
app/src/main/java/com/aiosman/ravenow/ui/index/tabs/ai/Agent.kt
Normal file
@@ -0,0 +1,250 @@
|
||||
package com.aiosman.ravenow.ui.index.tabs.ai
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.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.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
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.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.LocalNavController
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.ui.NavigationRoute
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn( ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun Agent() {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val navController = LocalNavController.current
|
||||
val navigationBarPaddings =
|
||||
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + 48.dp
|
||||
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
||||
var pagerState = rememberPagerState { 4 }
|
||||
var scope = rememberCoroutineScope()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
top = statusBarPaddingValues.calculateTopPadding()+18.dp,
|
||||
bottom = navigationBarPaddings,
|
||||
start = 16.dp,
|
||||
end = 16.dp
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.height(36.dp) // 设置高度为36dp
|
||||
.fillMaxWidth(), // 占据整行宽度
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.Bottom
|
||||
) {
|
||||
// 搜索框 - 占据剩余空间
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.height(36.dp)
|
||||
.weight(1f) // 权重为1,占据剩余空间
|
||||
.clip(shape = RoundedCornerShape(18.dp))
|
||||
.background(AppColors.inputBackground)
|
||||
.padding(horizontal = 8.dp, vertical = 0.dp)
|
||||
.noRippleClickable {
|
||||
// 搜索框点击事件
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.rider_pro_nav_search),
|
||||
contentDescription = null,
|
||||
tint = AppColors.inputHint
|
||||
)
|
||||
Box {
|
||||
Text(
|
||||
text = stringResource(R.string.search),
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
color = AppColors.inputHint,
|
||||
fontSize = 17.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 间隔
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
// 新增
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.noRippleClickable {
|
||||
// 图标点击事件
|
||||
navController.navigate(
|
||||
NavigationRoute.AddAgent.route
|
||||
)
|
||||
},
|
||||
painter = painterResource(id = R.drawable.rider_pro_new_post_add_pic),
|
||||
contentDescription = null,
|
||||
tint = AppColors.text
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight(),
|
||||
// center the tabs
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.Bottom
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.noRippleClickable {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(0)
|
||||
}
|
||||
},
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.agent_mine),
|
||||
fontSize = 14.sp,
|
||||
color = if (pagerState.currentPage == 0) AppColors.mainText else AppColors.checkedBackground,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(if (pagerState.currentPage == 0) AppColors.checkedBackground else AppColors.unCheckedBackground)
|
||||
.padding(horizontal = 11.dp, vertical = 4.dp)
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.noRippleClickable {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(1)
|
||||
}
|
||||
},
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.agent_hot),
|
||||
fontSize = 14.sp,
|
||||
color = if (pagerState.currentPage == 1) AppColors.mainText else AppColors.checkedBackground,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(if (pagerState.currentPage == 1) AppColors.checkedBackground else AppColors.unCheckedBackground)
|
||||
.padding(horizontal = 11.dp, vertical = 4.dp)
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.noRippleClickable {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(2)
|
||||
}
|
||||
},
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.agent_recommend),
|
||||
fontSize = 14.sp,
|
||||
color = if (pagerState.currentPage == 2) AppColors.mainText else AppColors.checkedBackground,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(if (pagerState.currentPage == 2) AppColors.checkedBackground else AppColors.unCheckedBackground)
|
||||
.padding(horizontal = 11.dp, vertical = 4.dp)
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.noRippleClickable {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(3)
|
||||
}
|
||||
},
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.agent_other),
|
||||
fontSize = 14.sp,
|
||||
color = if (pagerState.currentPage == 3) AppColors.mainText else AppColors.checkedBackground,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(if (pagerState.currentPage == 3) AppColors.checkedBackground else AppColors.unCheckedBackground)
|
||||
.padding(horizontal = 11.dp, vertical = 4.dp)
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
) {
|
||||
when (it) {
|
||||
0 -> {
|
||||
// ExploreMomentsList()
|
||||
}
|
||||
|
||||
1 -> {
|
||||
//TimelineMomentsList()
|
||||
}
|
||||
|
||||
2 -> {
|
||||
//HotMomentsList()
|
||||
}
|
||||
|
||||
3 -> {
|
||||
//MineAgent()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.aiosman.ravenow.ui.index.tabs.ai
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
object AgentViewModel: ViewModel() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.aiosman.ravenow.ui.index.tabs.ai
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.aiosman.ravenow.data.AgentService
|
||||
import com.aiosman.ravenow.data.MomentService
|
||||
import com.aiosman.ravenow.data.UserServiceImpl
|
||||
import com.aiosman.ravenow.entity.AgentEntity
|
||||
import com.aiosman.ravenow.entity.AgentLoader
|
||||
import com.aiosman.ravenow.entity.AgentLoaderExtraArgs
|
||||
import kotlinx.coroutines.launch
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
open class BaseAgentModel :ViewModel(){
|
||||
// private val agentService: AgentService = AgentServiceImpl()
|
||||
|
||||
val agentLoader = AgentLoader().apply {
|
||||
onListChanged = {
|
||||
agentList = it
|
||||
}
|
||||
}
|
||||
var refreshing by mutableStateOf(false)
|
||||
var isFirstLoad = true
|
||||
var agentList by mutableStateOf<List<AgentEntity>>(listOf())
|
||||
open fun extraArgs(): AgentLoaderExtraArgs {
|
||||
return AgentLoaderExtraArgs()
|
||||
}
|
||||
fun refreshPager(pullRefresh: Boolean = false) {
|
||||
if (!isFirstLoad && !pullRefresh) {
|
||||
return
|
||||
}
|
||||
isFirstLoad = false
|
||||
agentLoader.clear()
|
||||
viewModelScope.launch {
|
||||
agentLoader.loadData(extraArgs())
|
||||
}
|
||||
}
|
||||
|
||||
fun loadMore() {
|
||||
viewModelScope.launch {
|
||||
agentLoader.loadMore(extraArgs())
|
||||
agentList = agentLoader.list
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun ResetModel() {
|
||||
agentLoader.clear()
|
||||
isFirstLoad = true
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.ui.composables.AgentCard
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun MineAgent() {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val model = MineAgentViewModel
|
||||
var agentList = model.agentList
|
||||
val scope = rememberCoroutineScope()
|
||||
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
|
||||
model.refreshPager(
|
||||
pullRefresh = true
|
||||
)
|
||||
})
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
// observe list scrolling
|
||||
val reachedBottom by remember {
|
||||
derivedStateOf {
|
||||
val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull()
|
||||
lastVisibleItem?.index != 0 && lastVisibleItem?.index == listState.layoutInfo.totalItemsCount - 2
|
||||
}
|
||||
}
|
||||
|
||||
// load more if scrolled to bottom
|
||||
LaunchedEffect(reachedBottom) {
|
||||
if (reachedBottom) {
|
||||
model.loadMore()
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
model.refreshPager()
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
|
||||
) {
|
||||
if(agentList.size == 0) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.mipmap.rider_pro_following_empty),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(140.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.size(32.dp))
|
||||
androidx.compose.material.Text(
|
||||
text = "You haven't followed anyone yet",
|
||||
color = AppColors.text,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.W600
|
||||
)
|
||||
Spacer(modifier = Modifier.size(16.dp))
|
||||
androidx.compose.material.Text(
|
||||
text = "Click start your social journey.",
|
||||
color = AppColors.text,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.W400
|
||||
)
|
||||
}
|
||||
}
|
||||
}else{
|
||||
Box(Modifier.pullRefresh(state)) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = listState
|
||||
) {
|
||||
items(
|
||||
agentList.size,
|
||||
key = { idx -> idx }
|
||||
) { idx ->
|
||||
val agentItem = agentList[idx] ?: return@items
|
||||
AgentCard(agentEntity = agentItem,
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import com.aiosman.ravenow.entity.AgentLoaderExtraArgs
|
||||
import com.aiosman.ravenow.entity.MomentLoaderExtraArgs
|
||||
import com.aiosman.ravenow.ui.index.tabs.ai.BaseAgentModel
|
||||
import com.aiosman.ravenow.ui.index.tabs.moment.BaseMomentModel
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
|
||||
object MineAgentViewModel : BaseAgentModel() {
|
||||
|
||||
init {
|
||||
//EventBus.getDefault().register(this)
|
||||
}
|
||||
override fun extraArgs(): AgentLoaderExtraArgs {
|
||||
return AgentLoaderExtraArgs()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -93,9 +93,12 @@ fun NotificationsScreen() {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
.padding(horizontal = 15.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// 左侧占位元素
|
||||
Box(modifier = Modifier.size(24.dp))
|
||||
|
||||
// 左侧 Column:label 居中显示
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -105,21 +108,24 @@ fun NotificationsScreen() {
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.main_message),
|
||||
fontSize = 16.sp,
|
||||
fontSize = 17.sp,
|
||||
fontWeight = FontWeight.W700,
|
||||
color = AppColors.text
|
||||
)
|
||||
}
|
||||
|
||||
// 右侧图片按钮:靠右显示
|
||||
// 右侧图标
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_favourite),
|
||||
contentDescription = stringResource(R.string.main_message),
|
||||
painter = painterResource(id = R.drawable.rider_pro_group),
|
||||
contentDescription = "add",
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.noRippleClickable {
|
||||
// 点击事件
|
||||
}
|
||||
|
||||
},
|
||||
colorFilter = ColorFilter.tint(AppColors.text)
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
||||
@@ -53,6 +53,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
@@ -61,6 +62,7 @@ import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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 com.aiosman.ravenow.AppState
|
||||
@@ -384,11 +386,22 @@ fun ProfileV3(
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_back_icon), // Replace with your image resource
|
||||
contentDescription = "Back",
|
||||
modifier = Modifier
|
||||
.noRippleClickable {
|
||||
navController.navigateUp()
|
||||
}
|
||||
.size(24.dp),
|
||||
colorFilter = ColorFilter.tint(AppColors.text)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
CustomAsyncImage(
|
||||
LocalContext.current,
|
||||
profile?.avatar,
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.size(32.dp)
|
||||
.clip(CircleShape),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.Crop
|
||||
|
||||
Reference in New Issue
Block a user