修改若干bug调整暗色模式适配以及界面尺寸修改

修复进入应用后的深色模式下的底部导航框在不同标签下会出现透明

动态-动态/关注界面暗色模式适配

修复上下滑动切换短视频,部分视频会被缩放

修复点击底部导航栏的标签切换界面后视频还在播放

修改短视频评论框大小以及我的派币界面大小
This commit is contained in:
2025-11-12 18:24:42 +08:00
parent 88968c7437
commit bc647119df
11 changed files with 310 additions and 140 deletions

View File

@@ -111,7 +111,7 @@ class DarkThemeColors : AppThemeData(
chatActionColor = Color(0xFF3D3D3D), chatActionColor = Color(0xFF3D3D3D),
brandColorsColor = Color(0xffD80264), brandColorsColor = Color(0xffD80264),
tabSelectedBackground = Color(0xffffffff), tabSelectedBackground = Color(0xffffffff),
tabUnselectedBackground = Color(0x2E7C7480), tabUnselectedBackground = Color(0xFF1C1C1C),
tabSelectedText = Color(0xff000000), tabSelectedText = Color(0xff000000),
tabUnselectedText = Color(0xffffffff), tabUnselectedText = Color(0xffffffff),
bubbleBackground = Color(0xff2d2c2e), bubbleBackground = Color(0xff2d2c2e),

View File

@@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.ime
@@ -157,6 +159,7 @@ fun CommentModalContent(
} }
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize()
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
@@ -196,12 +199,12 @@ fun CommentModalContent(
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp)
.weight(1f) .weight(1f)
) { ) {
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxSize()
.padding(horizontal = 16.dp)
) { ) {
item { item {
CommentContent( CommentContent(

View File

@@ -64,6 +64,7 @@ fun AnimatedFavouriteIcon(
modifier = modifier.graphicsLayer { modifier = modifier.graphicsLayer {
rotationZ = animatableRotation.value rotationZ = animatableRotation.value
}, },
colorFilter = if (!isFavourite) ColorFilter.tint(AppColors.text) else null
) )
} }
} }

View File

@@ -468,6 +468,7 @@ fun MomentOperateBtn(@DrawableRes icon: Int, count: String) {
.size(width = 24.dp, height = 24.dp), .size(width = 24.dp, height = 24.dp),
painter = painterResource(id = icon), painter = painterResource(id = icon),
contentDescription = "", contentDescription = "",
colorFilter = ColorFilter.tint(AppColors.text)
) )
if (count.isNotEmpty()) { if (count.isNotEmpty()) {
Text( Text(

View File

@@ -269,7 +269,7 @@ fun IndexScreen() {
) { page -> ) { page ->
when (page) { when (page) {
0 -> Agent() 0 -> Agent()
1 -> Home() 1 -> Home(isPageVisible = pagerState.currentPage == 1)
2 -> Add() 2 -> Add()
3 -> Notifications() 3 -> Notifications()
4 -> Profile() 4 -> Profile()
@@ -332,7 +332,9 @@ fun IndexScreen() {
} }
@Composable @Composable
fun Home() { fun Home(
isPageVisible: Boolean = true
) {
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()
val context = LocalContext.current val context = LocalContext.current
@@ -348,7 +350,7 @@ fun Home() {
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
MomentsList() MomentsList(isPageVisible = isPageVisible)
} }
} }

View File

@@ -71,7 +71,9 @@ import com.aiosman.ravenow.ui.index.tabs.moment.tabs.shorts.ShortVideoScreen
*/ */
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class) @OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@Composable @Composable
fun MomentsList() { fun MomentsList(
isPageVisible: Boolean = true
) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
val navController = LocalNavController.current val navController = LocalNavController.current
val navigationBarPaddings = val navigationBarPaddings =
@@ -437,7 +439,7 @@ fun MomentsList() {
} }
1 -> { 1 -> {
// 短视频页面 // 短视频页面
ShortVideoScreen() ShortVideoScreen(isPageVisible = pagerState.currentPage == 1 && isPageVisible)
} }
2 -> { 2 -> {
// 动态页面 - 暂时显示时间线内容 // 动态页面 - 暂时显示时间线内容

View File

@@ -1,5 +1,6 @@
package com.aiosman.ravenow.ui.index.tabs.moment.tabs.dynamic package com.aiosman.ravenow.ui.index.tabs.moment.tabs.dynamic
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@@ -21,6 +22,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.aiosman.ravenow.GuestLoginCheckOut import com.aiosman.ravenow.GuestLoginCheckOut
import com.aiosman.ravenow.GuestLoginCheckOutScene import com.aiosman.ravenow.GuestLoginCheckOutScene
import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.ui.NavigationRoute import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.composables.MomentCard import com.aiosman.ravenow.ui.composables.MomentCard
@@ -36,6 +38,7 @@ fun Dynamic() {
val model = DynamicViewModel val model = DynamicViewModel
val moments = model.moments val moments = model.moments
val navController = LocalNavController.current val navController = LocalNavController.current
val AppColors = LocalAppTheme.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(model.refreshing, onRefresh = { val state = rememberPullRefreshState(model.refreshing, onRefresh = {
@@ -88,7 +91,7 @@ fun Dynamic() {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(AppColors.background)
) { ) {
Box(Modifier.pullRefresh(state)) { Box(Modifier.pullRefresh(state)) {
LazyColumn( LazyColumn(

View File

@@ -31,7 +31,9 @@ import kotlinx.coroutines.launch
* 短视频页面 * 短视频页面
*/ */
@Composable @Composable
fun ShortVideoScreen() { fun ShortVideoScreen(
isPageVisible: Boolean = true
) {
val viewModel = ShortVideoViewModel val viewModel = ShortVideoViewModel
val allMoments = viewModel.moments val allMoments = viewModel.moments
val navController = LocalNavController.current val navController = LocalNavController.current
@@ -187,7 +189,8 @@ fun ShortVideoScreen() {
) )
) )
}, },
onPageChanged = { idx -> currentIndex.value = idx } onPageChanged = { idx -> currentIndex.value = idx },
isPageVisible = isPageVisible
) )
} }
} }

View File

@@ -86,6 +86,7 @@ fun TimelineMomentsList() {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(AppColors.background)
.padding(top = 188.dp), .padding(top = 188.dp),
contentAlignment = Alignment.TopCenter contentAlignment = Alignment.TopCenter
) { ) {
@@ -126,6 +127,7 @@ fun TimelineMomentsList() {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(AppColors.background)
.padding(top = 188.dp), .padding(top = 188.dp),
contentAlignment = Alignment.TopCenter contentAlignment = Alignment.TopCenter
) { ) {
@@ -176,6 +178,7 @@ fun TimelineMomentsList() {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(AppColors.background)
) { ) {
Box(Modifier.pullRefresh(state)) { Box(Modifier.pullRefresh(state)) {
LazyColumn( LazyColumn(

View File

@@ -76,12 +76,129 @@ import com.aiosman.ravenow.entity.MomentEntity
import com.aiosman.ravenow.ui.comment.CommentModalContent import com.aiosman.ravenow.ui.comment.CommentModalContent
import com.aiosman.ravenow.ui.composables.CustomAsyncImage import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
// 激活状态的颜色(点赞/收藏时的红色) // 激活状态的颜色(点赞/收藏时的红色)
private val ActiveIconColor = Color(0xFFD80264) private val ActiveIconColor = Color(0xFFD80264)
/**
* 创建视频播放器视图
*/
private fun createVideoPlayerView(
context: android.content.Context,
exoPlayer: ExoPlayer,
onViewCreated: (PlayerView) -> Unit
): FrameLayout {
return FrameLayout(context).apply {
setBackgroundColor(Color.Black.toArgb())
layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
val playerView = PlayerView(context).apply {
hideController()
useController = false
player = exoPlayer
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
setShutterBackgroundColor(Color.Black.toArgb())
setKeepContentOnPlayerReset(false)
layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
Gravity.CENTER
)
// 布局完成后固定内容帧尺寸 - 使用更可靠的方法
var layoutListener: android.view.ViewTreeObserver.OnGlobalLayoutListener? = null
layoutListener = object : android.view.ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
try {
val frame = findViewById<AspectRatioFrameLayout>(androidx.media3.ui.R.id.exo_content_frame)
if (frame != null && width > 0 && height > 0) {
// 确保内容帧的尺寸和缩放模式正确设置
val layoutParams = FrameLayout.LayoutParams(
width,
height,
Gravity.CENTER
)
frame.layoutParams = layoutParams
frame.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM)
// 强制重新布局
frame.requestLayout()
}
} catch (e: Exception) {
// 忽略异常
}
}
}
// 使用 post 确保在布局完成后执行
post {
viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
// 延迟移除监听器,确保多次布局变化都能处理
postDelayed({
try {
layoutListener?.let { viewTreeObserver.removeOnGlobalLayoutListener(it) }
} catch (e: Exception) {
// 忽略异常
}
}, 500)
}
}
// 添加视频准备监听,确保视频准备就绪时也设置正确的缩放
exoPlayer.addListener(object : Player.Listener {
override fun onVideoSizeChanged(videoSize: androidx.media3.common.VideoSize) {
playerView.post {
try {
val frame = playerView.findViewById<AspectRatioFrameLayout>(androidx.media3.ui.R.id.exo_content_frame)
if (frame != null && playerView.width > 0 && playerView.height > 0) {
frame.layoutParams = FrameLayout.LayoutParams(
playerView.width,
playerView.height,
Gravity.CENTER
)
frame.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM)
frame.requestLayout()
}
} catch (e: Exception) {
// 忽略异常
}
}
}
})
onViewCreated(playerView)
addView(playerView)
}
}
/**
* 处理视频点击事件(播放/暂停)
*/
private fun handleVideoClick(
pauseIconVisibleState: MutableState<Boolean>,
exoPlayer: ExoPlayer,
scope: CoroutineScope
) {
scope.launch {
if (exoPlayer.isPlaying) {
// 正在播放:暂停
exoPlayer.pause()
pauseIconVisibleState.value = true
} else {
// 已暂停:恢复播放
exoPlayer.playWhenReady = true
exoPlayer.play()
pauseIconVisibleState.value = false
}
}
}
@Composable @Composable
fun ShortViewCompose( fun ShortViewCompose(
videoItemsUrl: List<String> = emptyList(), videoItemsUrl: List<String> = emptyList(),
@@ -95,7 +212,8 @@ fun ShortViewCompose(
onFavoriteClick: ((MomentEntity) -> Unit)? = null, onFavoriteClick: ((MomentEntity) -> Unit)? = null,
onShareClick: ((MomentEntity) -> Unit)? = null, onShareClick: ((MomentEntity) -> Unit)? = null,
onAvatarClick: ((MomentEntity) -> Unit)? = null, onAvatarClick: ((MomentEntity) -> Unit)? = null,
onPageChanged: ((Int) -> Unit)? = null onPageChanged: ((Int) -> Unit)? = null,
isPageVisible: Boolean = true
) { ) {
// 优先使用 videoMoments如果没有则使用 videoItemsUrl // 优先使用 videoMoments如果没有则使用 videoItemsUrl
val items = if (videoMoments.isNotEmpty()) { val items = if (videoMoments.isNotEmpty()) {
@@ -144,35 +262,40 @@ fun ShortViewCompose(
.clip(RectangleShape), .clip(RectangleShape),
state = pagerState, state = pagerState,
orientation = Orientation.Vertical, orientation = Orientation.Vertical,
offscreenLimit = 1 offscreenLimit = 1 // 只预加载相邻页面,减少内存占用
) { ) {
pauseIconVisibleState.value = false // 使用 key 确保每个页面独立,避免状态混乱
val currentMoment = if (videoMoments.isNotEmpty() && page < videoMoments.size) { androidx.compose.runtime.key(page) {
videoMoments[page] pauseIconVisibleState.value = false
} else { val currentMoment = if (videoMoments.isNotEmpty() && page < videoMoments.size) {
null videoMoments[page]
} } else {
null
}
// 同步页码到外部(用于返回时恢复进度) // 同步页码到外部(用于返回时恢复进度)
LaunchedEffect(pagerState.currentPage) { LaunchedEffect(pagerState.currentPage) {
onPageChanged?.invoke(pagerState.currentPage) onPageChanged?.invoke(pagerState.currentPage)
}
SingleVideoItemContent(
videoUrl = items[page],
moment = currentMoment,
pagerState = pagerState,
pager = page,
initialLayout = initialLayout,
pauseIconVisibleState = pauseIconVisibleState,
VideoHeader = videoHeader,
VideoBottom = videoBottom,
onLikeClick = onLikeClick,
onCommentClick = onCommentClick,
onCommentAdded = onCommentAdded,
onFavoriteClick = onFavoriteClick,
onShareClick = onShareClick,
onAvatarClick = onAvatarClick,
isPageVisible = isPageVisible
)
} }
SingleVideoItemContent(
videoUrl = items[page],
moment = currentMoment,
pagerState = pagerState,
pager = page,
initialLayout = initialLayout,
pauseIconVisibleState = pauseIconVisibleState,
VideoHeader = videoHeader,
VideoBottom = videoBottom,
onLikeClick = onLikeClick,
onCommentClick = onCommentClick,
onCommentAdded = onCommentAdded,
onFavoriteClick = onFavoriteClick,
onShareClick = onShareClick,
onAvatarClick = onAvatarClick
)
} }
LaunchedEffect(clickItemPosition) { LaunchedEffect(clickItemPosition) {
@@ -197,7 +320,8 @@ private fun SingleVideoItemContent(
onCommentAdded: ((MomentEntity) -> Unit)? = null, onCommentAdded: ((MomentEntity) -> Unit)? = null,
onFavoriteClick: ((MomentEntity) -> Unit)? = null, onFavoriteClick: ((MomentEntity) -> Unit)? = null,
onShareClick: ((MomentEntity) -> Unit)? = null, onShareClick: ((MomentEntity) -> Unit)? = null,
onAvatarClick: ((MomentEntity) -> Unit)? = null onAvatarClick: ((MomentEntity) -> Unit)? = null,
isPageVisible: Boolean = true
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
@@ -215,7 +339,8 @@ private fun SingleVideoItemContent(
onCommentAdded = onCommentAdded, onCommentAdded = onCommentAdded,
onFavoriteClick = onFavoriteClick, onFavoriteClick = onFavoriteClick,
onShareClick = onShareClick, onShareClick = onShareClick,
onAvatarClick = onAvatarClick onAvatarClick = onAvatarClick,
isPageVisible = isPageVisible
) )
VideoHeader.invoke() VideoHeader.invoke()
if (moment != null && VideoBottom != null) { if (moment != null && VideoBottom != null) {
@@ -247,95 +372,89 @@ fun VideoPlayer(
onFavoriteClick: ((MomentEntity) -> Unit)? = null, onFavoriteClick: ((MomentEntity) -> Unit)? = null,
onShareClick: ((MomentEntity) -> Unit)? = null, onShareClick: ((MomentEntity) -> Unit)? = null,
onAvatarClick: ((MomentEntity) -> Unit)? = null, onAvatarClick: ((MomentEntity) -> Unit)? = null,
isPageVisible: Boolean = true
) { ) {
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
val configuration = androidx.compose.ui.platform.LocalConfiguration.current val configuration = androidx.compose.ui.platform.LocalConfiguration.current
val screenHeight = configuration.screenHeightDp.dp val screenHeight = configuration.screenHeightDp.dp
val sheetHeight = screenHeight * 0.7f // 屏幕的一大半高度 val sheetHeight = screenHeight * 0.7f // 屏幕的70%高度
var showCommentModal by remember { mutableStateOf(false) } var showCommentModal by remember { mutableStateOf(false) }
var sheetState = rememberModalBottomSheetState( var sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true skipPartiallyExpanded = true
) )
val exoPlayer = remember {
ExoPlayer.Builder(context) // 确保弹窗从下往上展开
.build() LaunchedEffect(showCommentModal) {
.apply { if (showCommentModal) {
// 创建带有认证头的 HttpDataSource.Factory sheetState.expand()
val httpDataSourceFactory = DefaultHttpDataSource.Factory() } else {
.setUserAgent(Util.getUserAgent(context, context.packageName)) sheetState.hide()
.setDefaultRequestProperties( }
mapOf( }
"Authorization" to "Bearer ${com.aiosman.ravenow.AppStore.token ?: ""}", // 使用 key 确保每个视频的 ExoPlayer 完全独立,避免状态残留
"DEVICE-OS" to "Android" val exoPlayer = remember(videoUrl) {
) ExoPlayer.Builder(context).build().apply {
// 创建带有认证头的数据源
val httpDataSourceFactory = DefaultHttpDataSource.Factory()
.setUserAgent(Util.getUserAgent(context, context.packageName))
.setDefaultRequestProperties(
mapOf(
"Authorization" to "Bearer ${com.aiosman.ravenow.AppStore.token ?: ""}",
"DEVICE-OS" to "Android"
) )
// 创建 DataSource.Factory使用自定义的 HttpDataSource.Factory
val dataSourceFactory: DataSource.Factory = DefaultDataSourceFactory(
context,
httpDataSourceFactory
) )
val source = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(Uri.parse(videoUrl)))
this.prepare(source) val dataSourceFactory = DefaultDataSourceFactory(context, httpDataSourceFactory)
} val source = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(Uri.parse(videoUrl)))
prepare(source)
videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
repeatMode = Player.REPEAT_MODE_ONE
}
} }
if (pager == pagerState.currentPage) {
exoPlayer.playWhenReady = true // 根据页面状态控制播放
exoPlayer.play() LaunchedEffect(pager, pagerState.currentPage, isPageVisible) {
} else { val isCurrentPage = pager == pagerState.currentPage && isPageVisible
exoPlayer.pause() if (isCurrentPage) {
// 当前页面且页面可见:恢复播放
exoPlayer.playWhenReady = true
exoPlayer.play()
pauseIconVisibleState.value = false
} else {
// 非当前页面或页面不可见:立即暂停并停止准备播放
exoPlayer.playWhenReady = false
exoPlayer.pause()
pauseIconVisibleState.value = false
}
} }
exoPlayer.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT // 视频播放器容器 - 确保每个视频页面都被正确裁剪
exoPlayer.repeatMode = Player.REPEAT_MODE_ONE
// player box
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize(), .fillMaxSize()
contentAlignment = Alignment.TopCenter .clip(RectangleShape)
) { ) {
var playerView by remember { mutableStateOf<PlayerView?>(null) } // Store reference to PlayerView var playerView by remember { mutableStateOf<PlayerView?>(null) }
AndroidView( // 使用 key 强制每个视频的 PlayerView 完全独立,避免布局状态残留
factory = { context -> androidx.compose.runtime.key(videoUrl) {
// 创建一个 FrameLayout 作为容器 AndroidView(
FrameLayout(context).apply { factory = { ctx ->
// 设置背景颜色为黑色,用于显示黑边 createVideoPlayerView(ctx, exoPlayer) { view ->
setBackgroundColor(Color.Black.toArgb()) playerView = view
// 创建 PlayerView 并添加到 FrameLayout 中
val view = PlayerView(context).apply {
hideController()
useController = false
player = exoPlayer
resizeMode =
AspectRatioFrameLayout.RESIZE_MODE_FIXED_HEIGHT // 或 RESIZE_MODE_ZOOM
layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
Gravity.CENTER
)
} }
addView(view) },
} modifier = Modifier
}, .fillMaxSize()
modifier = Modifier.noRippleClickable { .clip(RectangleShape)
pauseIconVisibleState.value = true .noRippleClickable {
exoPlayer.pause() handleVideoClick(pauseIconVisibleState, exoPlayer, scope)
scope.launch {
delay(100)
if (exoPlayer.isPlaying) {
exoPlayer.pause()
} else {
pauseIconVisibleState.value = false
exoPlayer.play()
} }
} )
} }
)
if (pauseIconVisibleState.value) { if (pauseIconVisibleState.value) {
Icon( Icon(
@@ -348,11 +467,16 @@ fun VideoPlayer(
} }
// Release ExoPlayer when the videoUrl changes or the composable leaves composition // Release ExoPlayer when the videoUrl changes or the composable leaves composition
// 使用 key 后DisposableEffect 会在 videoUrl 变化时自动触发
DisposableEffect(videoUrl) { DisposableEffect(videoUrl) {
onDispose { onDispose {
exoPlayer.release() try {
playerView?.player = null exoPlayer.release()
playerView = null // Release the reference to the PlayerView playerView?.player = null
playerView = null // Release the reference to the PlayerView
} catch (e: Exception) {
// 忽略释放时的异常
}
} }
} }
@@ -360,12 +484,16 @@ fun VideoPlayer(
val observer = LifecycleEventObserver { _, event -> val observer = LifecycleEventObserver { _, event ->
when (event) { when (event) {
Lifecycle.Event.ON_PAUSE -> { Lifecycle.Event.ON_PAUSE -> {
exoPlayer.pause() // 应用进入后台时暂停 // 应用进入后台时暂停
exoPlayer.playWhenReady = false
exoPlayer.pause()
} }
Lifecycle.Event.ON_RESUME -> { Lifecycle.Event.ON_RESUME -> {
// 返回前台且为当前页面时恢复播放
if (pager == pagerState.currentPage) { if (pager == pagerState.currentPage) {
exoPlayer.play() // 返回前台且为当前页面时恢复播放 exoPlayer.playWhenReady = true
exoPlayer.play()
} }
} }
@@ -496,20 +624,29 @@ fun VideoPlayer(
if (showCommentModal && moment != null) { if (showCommentModal && moment != null) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = { showCommentModal = false }, onDismissRequest = {
scope.launch {
sheetState.hide()
}
showCommentModal = false
},
containerColor = AppColors.background, containerColor = AppColors.background,
sheetState = sheetState, sheetState = sheetState,
modifier = Modifier shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
.fillMaxWidth()
.height(sheetHeight)
) { ) {
CommentModalContent( Box(
postId = moment.id, modifier = Modifier
commentCount = moment.commentCount, .fillMaxWidth()
onCommentAdded = { .height(sheetHeight)
onCommentAdded?.invoke(moment) ) {
} CommentModalContent(
) postId = moment.id,
commentCount = moment.commentCount,
onCommentAdded = {
onCommentAdded?.invoke(moment)
}
)
}
} }
} }
} }

View File

@@ -74,7 +74,7 @@ fun PointsBottomSheet(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.fillMaxHeight(0.98f) .fillMaxHeight(0.9f)
.padding(horizontal = 16.dp, vertical = 8.dp) .padding(horizontal = 16.dp, vertical = 8.dp)
) { ) {
// 头部 // 头部
@@ -162,18 +162,21 @@ fun PointsBottomSheet(
TabItem( TabItem(
text = stringResource(R.string.transaction_history), text = stringResource(R.string.transaction_history),
isSelected = tab == 0, isSelected = tab == 0,
onClick = { tab = 0 } onClick = { tab = 0 },
modifier = Modifier.weight(1f)
) )
TabSpacer() TabSpacer()
TabItem( TabItem(
text = stringResource(R.string.how_to_earn), text = stringResource(R.string.how_to_earn),
isSelected = tab == 1, isSelected = tab == 1,
onClick = { tab = 1 } onClick = { tab = 1 },
modifier = Modifier.weight(1f)
) )
} }
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(8.dp))
// 列表区域
if (tab == 0) { if (tab == 0) {
PointsHistoryList( PointsHistoryList(
items = PointsViewModel.logs, items = PointsViewModel.logs,
@@ -183,8 +186,6 @@ fun PointsBottomSheet(
} else { } else {
HowToEarnList() HowToEarnList()
} }
Spacer(Modifier.height(24.dp))
} }
} }
} }
@@ -210,7 +211,10 @@ private fun PointsHistoryList(
) { ) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
val numberFormat = remember { NumberFormat.getNumberInstance(Locale.getDefault()) } val numberFormat = remember { NumberFormat.getNumberInstance(Locale.getDefault()) }
LazyColumn {
LazyColumn(
modifier = Modifier.fillMaxWidth()
) {
items(items) { item -> items(items) { item ->
Row( Row(
modifier = Modifier modifier = Modifier
@@ -260,6 +264,7 @@ private fun PointsHistoryList(
@Composable @Composable
private fun HowToEarnList() { private fun HowToEarnList() {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
@Composable @Composable
fun RowItem(title: String, desc: String, amount: String) { fun RowItem(title: String, desc: String, amount: String) {
Row( Row(
@@ -288,13 +293,23 @@ private fun HowToEarnList() {
} }
} }
Column { LazyColumn(
RowItem(stringResource(R.string.new_user_reward), stringResource(R.string.new_user_reward_desc), "+500") modifier = Modifier.fillMaxWidth()
RowItem(stringResource(R.string.daily_check_in), stringResource(R.string.daily_check_in_desc), "+10-50") ) {
RowItem(stringResource(R.string.invite_friends), stringResource(R.string.invite_friends_desc), "+100") item {
RowItem(stringResource(R.string.complete_tasks), stringResource(R.string.complete_tasks_desc), "+20-200") RowItem(stringResource(R.string.new_user_reward), stringResource(R.string.new_user_reward_desc), "+500")
RowItem(stringResource(R.string.recharge_pai_coin), stringResource(R.string.recharge_pai_coin_desc), ">") }
item {
RowItem(stringResource(R.string.daily_check_in), stringResource(R.string.daily_check_in_desc), "+10-50")
}
item {
RowItem(stringResource(R.string.invite_friends), stringResource(R.string.invite_friends_desc), "+100")
}
item {
RowItem(stringResource(R.string.complete_tasks), stringResource(R.string.complete_tasks_desc), "+20-200")
}
item {
RowItem(stringResource(R.string.recharge_pai_coin), stringResource(R.string.recharge_pai_coin_desc), ">")
}
} }
} }