diff --git a/app/src/main/java/com/aiosman/ravenow/ui/account/edit2.kt b/app/src/main/java/com/aiosman/ravenow/ui/account/edit2.kt index 6d99c70..856abb9 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/account/edit2.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/account/edit2.kt @@ -70,6 +70,8 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.text.TextLayoutResult import com.aiosman.ravenow.ConstVars import com.aiosman.ravenow.ui.composables.pickupAndCompressLauncher import android.widget.Toast @@ -407,7 +409,7 @@ fun AccountEditScreen2(onUpdateBanner: ((Uri, File, Context) -> Unit)? = null,) ProfileInfoCard( label = stringResource(R.string.personal_intro), value = model.bio, - placeholder = stringResource(R.string.bio_placeholder), + placeholder = "", onValueChange = { onBioChange(it) }, isMultiline = true ) @@ -564,20 +566,30 @@ fun ProfileInfoCard( isMultiline: Boolean = false ) { val appColors = LocalAppTheme.current + var isFocused by remember { mutableStateOf(false) } + var lineCount by remember { mutableStateOf(1) } + + // 根据行数决定对齐方式:单行时居中,多行时顶部对齐 + val verticalAlignment = if (isMultiline) { + if (lineCount <= 1) Alignment.CenterVertically else Alignment.Top + } else { + Alignment.CenterVertically + } + Box( modifier = Modifier .fillMaxWidth() .height(if (isMultiline) 66.dp else 56.dp) // 昵称框高度56dp,个人简介66dp .clip(RoundedCornerShape(16.dp)) .background(appColors.secondaryBackground), - contentAlignment = if (isMultiline) Alignment.TopStart else Alignment.CenterStart + contentAlignment = if (isMultiline && lineCount > 1) Alignment.TopStart else Alignment.CenterStart ) { Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp) - .padding(vertical = if (isMultiline) 11.dp else 0.dp), - verticalAlignment = if (isMultiline) Alignment.Top else Alignment.CenterVertically + .padding(vertical = if (isMultiline && lineCount > 1) 11.dp else 0.dp), + verticalAlignment = verticalAlignment ) { // 标签 Box( @@ -600,10 +612,26 @@ fun ProfileInfoCard( Box( modifier = Modifier.weight(1f) ) { - if (value.isEmpty()) { + // 对于个人简介(isMultiline = true),当值为空且没有焦点时显示图标 + if (value.isEmpty() && isMultiline && !isFocused) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(44.dp), + contentAlignment = Alignment.CenterStart + ) { + Icon( + painter = painterResource(id = R.mipmap.icons_infor_edit), + contentDescription = null, + modifier = Modifier.size(16.dp), + tint = appColors.secondaryText + ) + } + } else if (value.isEmpty() && !isMultiline && placeholder.isNotEmpty()) { + // 对于非多行输入框,仍然显示 placeholder 文字 Text( text = placeholder, - fontSize = if (isMultiline) 15.sp else 17.sp, + fontSize = 17.sp, fontWeight = FontWeight.Normal, color = appColors.secondaryText, modifier = Modifier.fillMaxWidth() @@ -613,7 +641,11 @@ fun ProfileInfoCard( BasicTextField( value = value, onValueChange = onValueChange, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .onFocusChanged { focusState -> + isFocused = focusState.isFocused + }, textStyle = androidx.compose.ui.text.TextStyle( fontSize = if (isMultiline) 15.sp else 17.sp, fontWeight = FontWeight.Normal, @@ -621,7 +653,12 @@ fun ProfileInfoCard( ), cursorBrush = SolidColor(appColors.text), maxLines = if (isMultiline) Int.MAX_VALUE else 1, - singleLine = !isMultiline + singleLine = !isMultiline, + onTextLayout = { textLayoutResult: TextLayoutResult -> + if (isMultiline) { + lineCount = textLayoutResult.lineCount + } + } ) } } diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/recommend/VideoRecommendationItem.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/recommend/VideoRecommendationItem.kt index 310fd82..cc663bd 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/recommend/VideoRecommendationItem.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/moment/tabs/recommend/VideoRecommendationItem.kt @@ -16,10 +16,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState @@ -35,6 +32,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale @@ -99,6 +97,7 @@ fun VideoRecommendationItem( skipPartiallyExpanded = true ) var pauseIconVisibleState by remember { mutableStateOf(false) } + var shouldResumeAfterLifecyclePause by remember { mutableStateOf(false) } // 防抖:记录上次双击时间,防止快速重复双击 val lastDoubleTapTime = remember { mutableStateOf(0L) } val doubleTapDebounceTime = 500L // 500ms 防抖时间 @@ -202,13 +201,13 @@ fun VideoRecommendationItem( ) if (pauseIconVisibleState) { - Icon( - imageVector = Icons.Default.PlayArrow, + Image( + painter = painterResource(R.mipmap.dt_ts_sp_bf_btn), contentDescription = null, modifier = Modifier .align(Alignment.Center) .size(80.dp), - tint = Color.White + colorFilter = ColorFilter.tint(Color.White) ) } } @@ -341,10 +340,12 @@ fun VideoRecommendationItem( val observer = LifecycleEventObserver { _, event -> when (event) { Lifecycle.Event.ON_PAUSE -> { + shouldResumeAfterLifecyclePause = exoPlayer.isPlaying && !pauseIconVisibleState exoPlayer.pause() } Lifecycle.Event.ON_RESUME -> { - if (isVisible) { + if (isVisible && shouldResumeAfterLifecyclePause) { + pauseIconVisibleState = false exoPlayer.play() } } @@ -420,3 +421,4 @@ private fun VideoBtn( ) } } + diff --git a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/shorts/ShortViewCompose.kt b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/shorts/ShortViewCompose.kt index 6b4c093..21375d6 100644 --- a/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/shorts/ShortViewCompose.kt +++ b/app/src/main/java/com/aiosman/ravenow/ui/index/tabs/shorts/ShortViewCompose.kt @@ -26,10 +26,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState @@ -321,9 +318,9 @@ private fun SingleVideoItemContent( isPageVisible: Boolean = true ) { // 将暂停状态移到每个视频项内部,使用 remember 保存,避免在点赞/关注时被重置 - val pauseIconVisibleState = remember(pager) { - mutableStateOf(false) - } + val pauseIconVisibleState = remember(pager) { mutableStateOf(false) } + // 记录进入后台前是否在播放,用于决定是否需要自动恢复播放 + val shouldResumeAfterLifecyclePause = remember(pager) { mutableStateOf(false) } // 当页面切换时,重置暂停状态 LaunchedEffect(pager, pagerState.currentPage) { @@ -343,6 +340,7 @@ private fun SingleVideoItemContent( pagerState = pagerState, pager = pager, pauseIconVisibleState = pauseIconVisibleState, + shouldResumeAfterLifecyclePause = shouldResumeAfterLifecyclePause, onLikeClick = onLikeClick, onCommentClick = onCommentClick, onCommentAdded = onCommentAdded, @@ -375,6 +373,7 @@ fun VideoPlayer( pagerState: PagerState, pager: Int, pauseIconVisibleState: MutableState, + shouldResumeAfterLifecyclePause: MutableState, onLikeClick: ((MomentEntity) -> Unit)? = null, onCommentClick: ((MomentEntity) -> Unit)? = null, onCommentAdded: ((MomentEntity) -> Unit)? = null, @@ -507,8 +506,8 @@ fun VideoPlayer( } if (pauseIconVisibleState.value) { - Icon( - imageVector = Icons.Default.PlayArrow, + Image( + painter = painterResource(R.mipmap.dt_ts_sp_bf_btn), contentDescription = null, modifier = Modifier .align(Alignment.Center) @@ -535,15 +534,21 @@ fun VideoPlayer( when (event) { Lifecycle.Event.ON_PAUSE -> { // 应用进入后台时暂停 + shouldResumeAfterLifecyclePause.value = exoPlayer.isPlaying && !pauseIconVisibleState.value exoPlayer.playWhenReady = false exoPlayer.pause() } Lifecycle.Event.ON_RESUME -> { // 返回前台且为当前页面时恢复播放 - if (pager == pagerState.currentPage) { + if ( + pager == pagerState.currentPage && + isPageVisible && + shouldResumeAfterLifecyclePause.value + ) { exoPlayer.playWhenReady = true exoPlayer.play() + pauseIconVisibleState.value = false } } diff --git a/app/src/main/res/mipmap-hdpi/dt_ts_sp_bf_btn.png b/app/src/main/res/mipmap-hdpi/dt_ts_sp_bf_btn.png new file mode 100644 index 0000000..d80d3bd Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/dt_ts_sp_bf_btn.png differ diff --git a/app/src/main/res/mipmap-mdpi/dt_ts_sp_bf_btn.png b/app/src/main/res/mipmap-mdpi/dt_ts_sp_bf_btn.png new file mode 100644 index 0000000..d02bf7e Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/dt_ts_sp_bf_btn.png differ diff --git a/app/src/main/res/mipmap-xhdpi/dt_ts_sp_bf_btn.png b/app/src/main/res/mipmap-xhdpi/dt_ts_sp_bf_btn.png new file mode 100644 index 0000000..7be2a34 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/dt_ts_sp_bf_btn.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/dt_ts_sp_bf_btn.png b/app/src/main/res/mipmap-xxhdpi/dt_ts_sp_bf_btn.png new file mode 100644 index 0000000..21ae51d Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/dt_ts_sp_bf_btn.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/dt_ts_sp_bf_btn.png b/app/src/main/res/mipmap-xxxhdpi/dt_ts_sp_bf_btn.png new file mode 100644 index 0000000..ce4521f Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/dt_ts_sp_bf_btn.png differ diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 9d906a8..e756bb2 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -334,7 +334,6 @@ 自己紹介の長さは100文字を超えることはできません ユーザープロフィールの読み込みに失敗しました。もう一度お試しください ニックネームを入力 - 私の世界へようこそ。魔法について何かお見せします 保存 MBTIを選択 MBTIは心理学理論に基づく人格評価ツールです。人々がエネルギーを獲得する方法(内向-外向)、情報を収集する方法(感覚-直感)、意思決定を行う方法(思考-感情)、生活様式(判断-知覚)の好みを理解することで、人格タイプを16種類に分類します。 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index d66c4eb..f8b5e68 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -326,7 +326,6 @@ 加载用户资料失败,请重试 网络错误,请检查网络 请输入昵称 - 欢迎来到我的世界,我会向你展示一些关于魔法的内容 保存 选择 MBTI MBTI是基于心理学理论的人格测评工具。了解人们获取能量方式(内向-外向)、收集信息方式(感觉-直觉)、做决策方式(思维-情感)、生活方式(判断-知觉)的偏好,将人格类型分为16种。 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d62e13d..b8db9da 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -334,7 +334,6 @@ Failed to load user profile, please try again Network error, please check your network Value - Welcome to my fantiac word i will show you something about magic Save Choose MBTI MBTI is a personality assessment tool based on psychological theory. By understanding people\'s preferences in how they acquire energy (introversion-extraversion), collect information (sensing-intuition), make decisions (thinking-feeling), and live their lives (judging-perceiving), personality types are divided into 16 kinds.