新增新闻标签页;修改底部导航栏背景颜色
This commit is contained in:
@@ -276,7 +276,7 @@ fun IndexScreen() {
|
|||||||
bottomBar = {
|
bottomBar = {
|
||||||
NavigationBar(
|
NavigationBar(
|
||||||
modifier = Modifier.height(58.dp + navigationBarHeight),
|
modifier = Modifier.height(58.dp + navigationBarHeight),
|
||||||
containerColor = AppColors.background
|
containerColor = AppColors.secondaryBackground
|
||||||
) {
|
) {
|
||||||
item.forEachIndexed { idx, it ->
|
item.forEachIndexed { idx, it ->
|
||||||
val isSelected = model.tabIndex == idx
|
val isSelected = model.tabIndex == idx
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import com.aiosman.ravenow.ui.index.tabs.moment.tabs.dynamic.Dynamic
|
|||||||
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.Explore
|
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.Explore
|
||||||
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.hot.HotMomentsList
|
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.hot.HotMomentsList
|
||||||
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentsList
|
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.timeline.TimelineMomentsList
|
||||||
|
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.news.NewsScreen
|
||||||
import com.aiosman.ravenow.ui.index.tabs.search.SearchViewModel
|
import com.aiosman.ravenow.ui.index.tabs.search.SearchViewModel
|
||||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -251,7 +252,7 @@ fun MomentsList() {
|
|||||||
}
|
}
|
||||||
5 -> {
|
5 -> {
|
||||||
// 新闻页面
|
// 新闻页面
|
||||||
|
NewsScreen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,337 @@
|
|||||||
|
package com.aiosman.ravenow.ui.index.tabs.moment.tabs.news
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
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.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
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.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
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.ColorFilter
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
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.entity.MomentEntity
|
||||||
|
import com.aiosman.ravenow.exp.timeAgo
|
||||||
|
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||||
|
import com.aiosman.ravenow.ui.index.tabs.moment.tabs.dynamic.DynamicViewModel
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
|
@Composable
|
||||||
|
fun NewsScreen() {
|
||||||
|
val model = DynamicViewModel
|
||||||
|
val moments = model.moments
|
||||||
|
val AppColors = LocalAppTheme.current
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
// 下拉刷新状态
|
||||||
|
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
|
||||||
|
model.refreshPager(pullRefresh = true)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 列表状态
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
|
// 用于跟踪是否已经触发过加载更多
|
||||||
|
var hasTriggeredLoadMore by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// 监听滚动到底部
|
||||||
|
val reachedBottom by remember {
|
||||||
|
derivedStateOf {
|
||||||
|
val layoutInfo = listState.layoutInfo
|
||||||
|
val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull()
|
||||||
|
val totalItems = layoutInfo.totalItemsCount
|
||||||
|
|
||||||
|
if (lastVisibleItem == null || totalItems == 0) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
val isLastItemVisible = lastVisibleItem.index >= totalItems - 2
|
||||||
|
isLastItemVisible && !hasTriggeredLoadMore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载更多数据
|
||||||
|
LaunchedEffect(reachedBottom) {
|
||||||
|
if (reachedBottom) {
|
||||||
|
hasTriggeredLoadMore = true
|
||||||
|
model.loadMore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化加载数据
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
model.refreshPager()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听数据变化,重置加载状态
|
||||||
|
LaunchedEffect(moments.size) {
|
||||||
|
if (moments.size > 0) {
|
||||||
|
kotlinx.coroutines.delay(500)
|
||||||
|
hasTriggeredLoadMore = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(AppColors.background)
|
||||||
|
) {
|
||||||
|
Box(Modifier.pullRefresh(state)) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
state = listState
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
moments.size,
|
||||||
|
key = { idx -> idx }
|
||||||
|
) { idx ->
|
||||||
|
// 处理下标越界
|
||||||
|
if (idx < 0 || idx >= moments.size) return@items
|
||||||
|
val momentItem = moments[idx] ?: return@items
|
||||||
|
|
||||||
|
NewsItem(
|
||||||
|
moment = momentItem,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//单个新闻项
|
||||||
|
@Composable
|
||||||
|
fun NewsItem(
|
||||||
|
moment: MomentEntity,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val AppColors = LocalAppTheme.current
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(700.dp)
|
||||||
|
.background(AppColors.background)
|
||||||
|
.padding(vertical = 8.dp),
|
||||||
|
verticalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
// 新闻图片
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(300.dp)
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
) {
|
||||||
|
if (moment.images.isNotEmpty()) {
|
||||||
|
CustomAsyncImage(
|
||||||
|
context = context,
|
||||||
|
imageUrl = moment.images[0].thumbnail,
|
||||||
|
contentDescription = "新闻图片",
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
blurHash = moment.images[0].blurHash,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.clip(RoundedCornerShape(8.dp))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Image(
|
||||||
|
painter = androidx.compose.ui.res.painterResource(id = R.drawable.default_moment_img),
|
||||||
|
contentDescription = "默认图片",
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.clip(RoundedCornerShape(8.dp))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// 新闻标题
|
||||||
|
Text(
|
||||||
|
text = moment.nickname, // 暂时使用用户名作为标题
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
|
fontSize = 20.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = AppColors.text,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// 新闻内容
|
||||||
|
Text(
|
||||||
|
text = moment.momentTextContent, // 使用动态内容作为新闻内容
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = AppColors.text,
|
||||||
|
lineHeight = 20.sp
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// 新闻信息
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
// 来源和时间
|
||||||
|
Text(
|
||||||
|
text = "${moment.nickname} • ${moment.time.timeAgo(context)}",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = AppColors.secondaryText
|
||||||
|
)
|
||||||
|
|
||||||
|
// 查看全文
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.read_full_article),
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = AppColors.text
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
Image(
|
||||||
|
painter = androidx.compose.ui.res.painterResource(id = R.drawable.rider_pro_nav_search),
|
||||||
|
contentDescription = "箭头",
|
||||||
|
modifier = Modifier.size(12.dp),
|
||||||
|
colorFilter = ColorFilter.tint(AppColors.text)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 互动栏
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.padding(bottom = 50.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceEvenly
|
||||||
|
) {
|
||||||
|
// 点赞
|
||||||
|
NewsActionButton(
|
||||||
|
icon = R.drawable.rider_pro_moment_like,
|
||||||
|
count = moment.likeCount.toString(),
|
||||||
|
isActive = moment.liked
|
||||||
|
)
|
||||||
|
|
||||||
|
// 评论
|
||||||
|
NewsActionButton(
|
||||||
|
icon = R.mipmap.icon_comment,
|
||||||
|
count = moment.commentCount.toString(),
|
||||||
|
isActive = false
|
||||||
|
)
|
||||||
|
|
||||||
|
// 收藏
|
||||||
|
NewsActionButton(
|
||||||
|
icon = R.mipmap.icon_collect,
|
||||||
|
count = moment.favoriteCount.toString(),
|
||||||
|
isActive = moment.isFavorite
|
||||||
|
)
|
||||||
|
|
||||||
|
// 分享
|
||||||
|
NewsActionButton(
|
||||||
|
icon = R.mipmap.icon_share,
|
||||||
|
count = "",
|
||||||
|
isActive = false,
|
||||||
|
text = stringResource(R.string.share),
|
||||||
|
textSize = 8.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 互动栏按钮
|
||||||
|
@Composable
|
||||||
|
fun NewsActionButton(
|
||||||
|
icon: Int,
|
||||||
|
count: String,
|
||||||
|
isActive: Boolean,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
text: String? = null,
|
||||||
|
textSize: androidx.compose.ui.unit.TextUnit = 12.sp
|
||||||
|
) {
|
||||||
|
val AppColors = LocalAppTheme.current
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = modifier
|
||||||
|
.width(60.dp)
|
||||||
|
.background(
|
||||||
|
color = AppColors.secondaryBackground,
|
||||||
|
shape = RoundedCornerShape(16.dp)
|
||||||
|
)
|
||||||
|
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = androidx.compose.ui.res.painterResource(id = icon),
|
||||||
|
contentDescription = "操作图标",
|
||||||
|
modifier = Modifier.size(16.dp),
|
||||||
|
colorFilter = ColorFilter.tint(
|
||||||
|
if (isActive) AppColors.background else AppColors.text
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (count.isNotEmpty()) {
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
Text(
|
||||||
|
text = count,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = if (isActive) AppColors.background else AppColors.text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (text != null) {
|
||||||
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
fontSize = textSize,
|
||||||
|
color = AppColors.text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -193,6 +193,7 @@
|
|||||||
<string name="agent_find">発見</string>
|
<string name="agent_find">発見</string>
|
||||||
<string name="text_error_password_too_long">パスワードは%1$d文字を超えることはできません</string>
|
<string name="text_error_password_too_long">パスワードは%1$d文字を超えることはできません</string>
|
||||||
<string name="block">ブロック</string>
|
<string name="block">ブロック</string>
|
||||||
|
<string name="read_full_article">全文を読む</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- Create Bottom Sheet -->
|
<!-- Create Bottom Sheet -->
|
||||||
|
|||||||
@@ -196,6 +196,7 @@
|
|||||||
<string name="text_error_password_too_long">密码不能超过 %1$d 个字符</string>
|
<string name="text_error_password_too_long">密码不能超过 %1$d 个字符</string>
|
||||||
<string name="chatting_now">人正在热聊…</string>
|
<string name="chatting_now">人正在热聊…</string>
|
||||||
<string name="block">拉黑</string>
|
<string name="block">拉黑</string>
|
||||||
|
<string name="read_full_article">查看全文</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- Create Bottom Sheet -->
|
<!-- Create Bottom Sheet -->
|
||||||
|
|||||||
@@ -192,6 +192,7 @@
|
|||||||
<string name="agent_find">Discover</string>
|
<string name="agent_find">Discover</string>
|
||||||
<string name="text_error_password_too_long">Password cannot exceed %1$d characters</string>
|
<string name="text_error_password_too_long">Password cannot exceed %1$d characters</string>
|
||||||
<string name="block">Block</string>
|
<string name="block">Block</string>
|
||||||
|
<string name="read_full_article">Read full article</string>
|
||||||
|
|
||||||
<!-- Create Bottom Sheet -->
|
<!-- Create Bottom Sheet -->
|
||||||
<string name="create_title">Create</string>
|
<string name="create_title">Create</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user