实现新闻界面查看全文
This commit is contained in:
@@ -0,0 +1,205 @@
|
|||||||
|
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.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
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.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
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.entity.MomentEntity
|
||||||
|
import com.aiosman.ravenow.exp.formatPostTime2
|
||||||
|
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun FullArticleModal(
|
||||||
|
moment: MomentEntity,
|
||||||
|
onDismiss: () -> Unit
|
||||||
|
) {
|
||||||
|
val appColors = LocalAppTheme.current
|
||||||
|
val context = LocalContext.current
|
||||||
|
val configuration = LocalConfiguration.current
|
||||||
|
val screenHeight = configuration.screenHeightDp.dp
|
||||||
|
val sheetHeight = screenHeight * 0.9f // 90% 高度
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
sheetState = rememberModalBottomSheetState(
|
||||||
|
skipPartiallyExpanded = true
|
||||||
|
),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(sheetHeight),
|
||||||
|
containerColor = appColors.background,
|
||||||
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||||
|
windowInsets = androidx.compose.foundation.layout.WindowInsets(0)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
) {
|
||||||
|
// 滚动内容
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
) {
|
||||||
|
// 新闻图片区域 - 固定高度和宽度
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(250.dp)
|
||||||
|
|
||||||
|
.background(color = appColors.secondaryBackground)
|
||||||
|
) {
|
||||||
|
if (moment.images.isNotEmpty()) {
|
||||||
|
val firstImage = moment.images[0]
|
||||||
|
CustomAsyncImage(
|
||||||
|
context = context,
|
||||||
|
imageUrl = firstImage.url,
|
||||||
|
contentDescription = "新闻图片",
|
||||||
|
contentScale = ContentScale.Fit,
|
||||||
|
blurHash = firstImage.blurHash,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Image(
|
||||||
|
painter = androidx.compose.ui.res.painterResource(id = R.drawable.default_moment_img),
|
||||||
|
contentDescription = "默认图片",
|
||||||
|
contentScale = ContentScale.Fit,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// 新闻标题
|
||||||
|
Text(
|
||||||
|
text = if (moment.newsTitle.isNotEmpty()) moment.newsTitle else moment.nickname,
|
||||||
|
fontSize = 20.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = appColors.text,
|
||||||
|
lineHeight = 28.sp,
|
||||||
|
modifier = Modifier.padding(horizontal = 10.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
// 新闻来源和发布时间
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 10.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
// 来源按钮
|
||||||
|
Button(
|
||||||
|
onClick = { },
|
||||||
|
modifier = Modifier.height(28.dp),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = Color(0xFF7c68ef)
|
||||||
|
),
|
||||||
|
contentPadding = androidx.compose.foundation.layout.PaddingValues(horizontal = 12.dp, vertical = 4.dp),
|
||||||
|
shape = RoundedCornerShape(14.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = if (moment.newsSource.isNotEmpty()) moment.newsSource else moment.nickname,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = Color.White,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布时间
|
||||||
|
Text(
|
||||||
|
text = moment.time.formatPostTime2(),
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = appColors.secondaryText
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// 帖子内容
|
||||||
|
NewsContent(
|
||||||
|
content = if (moment.newsContent.isNotEmpty()) moment.newsContent else moment.momentTextContent,
|
||||||
|
images = moment.images,
|
||||||
|
context = context
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(200.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun NewsContent(
|
||||||
|
content: String,
|
||||||
|
images: List<com.aiosman.ravenow.entity.MomentImageEntity>,
|
||||||
|
context: android.content.Context
|
||||||
|
) {
|
||||||
|
val appColors = LocalAppTheme.current
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = content,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
color = appColors.text,
|
||||||
|
lineHeight = 24.sp
|
||||||
|
)
|
||||||
|
|
||||||
|
// 图片内容
|
||||||
|
if (images.isNotEmpty()) {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
images.forEach { image ->
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(RoundedCornerShape(8.dp))
|
||||||
|
) {
|
||||||
|
CustomAsyncImage(
|
||||||
|
context = context,
|
||||||
|
imageUrl = image.url,
|
||||||
|
contentDescription = "内容图片",
|
||||||
|
contentScale = ContentScale.Fit,
|
||||||
|
blurHash = image.blurHash,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -72,6 +73,10 @@ fun NewsScreen() {
|
|||||||
// 评论弹窗状态
|
// 评论弹窗状态
|
||||||
var showCommentModal by remember { mutableStateOf(false) }
|
var showCommentModal by remember { mutableStateOf(false) }
|
||||||
var selectedMoment by remember { mutableStateOf<MomentEntity?>(null) }
|
var selectedMoment by remember { mutableStateOf<MomentEntity?>(null) }
|
||||||
|
|
||||||
|
// 查看全文弹窗状态
|
||||||
|
var showFullArticleModal by remember { mutableStateOf(false) }
|
||||||
|
var selectedArticleMoment by remember { mutableStateOf<MomentEntity?>(null) }
|
||||||
// 垂直翻页状态
|
// 垂直翻页状态
|
||||||
val pagerState = rememberPagerState(pageCount = { moments.size })
|
val pagerState = rememberPagerState(pageCount = { moments.size })
|
||||||
|
|
||||||
@@ -121,6 +126,10 @@ fun NewsScreen() {
|
|||||||
selectedMoment = momentItem
|
selectedMoment = momentItem
|
||||||
showCommentModal = true
|
showCommentModal = true
|
||||||
},
|
},
|
||||||
|
onReadFullClick = {
|
||||||
|
selectedArticleMoment = momentItem
|
||||||
|
showFullArticleModal = true
|
||||||
|
},
|
||||||
onLikeClick = {
|
onLikeClick = {
|
||||||
likeDebouncer {
|
likeDebouncer {
|
||||||
// 检查游客模式
|
// 检查游客模式
|
||||||
@@ -157,6 +166,16 @@ fun NewsScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查看全文弹窗
|
||||||
|
if (showFullArticleModal && selectedArticleMoment != null) {
|
||||||
|
FullArticleModal(
|
||||||
|
moment = selectedArticleMoment!!,
|
||||||
|
onDismiss = {
|
||||||
|
showFullArticleModal = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// 评论弹窗
|
// 评论弹窗
|
||||||
if (showCommentModal && selectedMoment != null) {
|
if (showCommentModal && selectedMoment != null) {
|
||||||
val configuration = LocalConfiguration.current
|
val configuration = LocalConfiguration.current
|
||||||
@@ -201,6 +220,7 @@ fun NewsItem(
|
|||||||
moment: MomentEntity,
|
moment: MomentEntity,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onCommentClick: () -> Unit = {},
|
onCommentClick: () -> Unit = {},
|
||||||
|
onReadFullClick: () -> Unit = {},
|
||||||
onLikeClick: () -> Unit = {},
|
onLikeClick: () -> Unit = {},
|
||||||
onFavoriteClick: () -> Unit = {}
|
onFavoriteClick: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
@@ -303,19 +323,21 @@ fun NewsItem(
|
|||||||
|
|
||||||
// 查看全文
|
// 查看全文
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.noRippleClickable { onReadFullClick() }
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.read_full_article),
|
text = stringResource(R.string.read_full_article),
|
||||||
fontSize = 12.sp,
|
fontSize = 13.sp,
|
||||||
color = AppColors.text
|
fontWeight = FontWeight.W600,
|
||||||
|
color = Color(0xFF7c45ed)
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(4.dp))
|
Spacer(modifier = Modifier.width(4.dp))
|
||||||
Image(
|
Image(
|
||||||
painter = androidx.compose.ui.res.painterResource(id = R.drawable.rider_pro_nav_search),
|
painter = androidx.compose.ui.res.painterResource(id = R.mipmap.arrow),
|
||||||
contentDescription = "箭头",
|
contentDescription = "箭头",
|
||||||
modifier = Modifier.size(12.dp),
|
modifier = Modifier.size(18.dp),
|
||||||
colorFilter = ColorFilter.tint(AppColors.text)
|
colorFilter = ColorFilter.tint(Color(0xFF7c45ed))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
app/src/main/res/mipmap-hdpi/arrow.png
Normal file
BIN
app/src/main/res/mipmap-hdpi/arrow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 247 B |
BIN
app/src/main/res/mipmap-mdpi/arrow.png
Normal file
BIN
app/src/main/res/mipmap-mdpi/arrow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 B |
BIN
app/src/main/res/mipmap-xhdpi/arrow.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/arrow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 259 B |
BIN
app/src/main/res/mipmap-xxhdpi/arrow.png
Normal file
BIN
app/src/main/res/mipmap-xxhdpi/arrow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 324 B |
BIN
app/src/main/res/mipmap-xxxhdpi/arrow.png
Normal file
BIN
app/src/main/res/mipmap-xxxhdpi/arrow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 368 B |
Reference in New Issue
Block a user