我使用LaunchedEffect和while来按时获取currentTime,但是当我按下暂停和播放按钮时,视频会重复
PlayerScreen.kt
@OptIn(UnstableApi::class)
@Composable
fun PlayerScreen(
navController: NavHostController,
modifier: Modifier = Modifier
) {
val videoUrl = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
val context = LocalContext.current
val player = remember { ExoPlayer.Builder(context).build() }
val playerView = remember { PlayerView(context) }
val mediaItem = remember { MediaItem.fromUri(videoUrl) }
var isPlaying by rememberSaveable { mutableStateOf(true) }
player.setMediaItem(mediaItem)
playerView.player = player
LaunchedEffect(player){
player.prepare()
player.playWhenReady = isPlaying
}
var currentTime by remember { mutableLongStateOf(1L) }
var maxDuration by remember { mutableLongStateOf(50L) }
if (isPlaying) {
LaunchedEffect(isPlaying) { // Menggunakan isPlaying sebagai dependencies
while(true) {
currentTime = player.currentPosition
if(player.isPlaying){
maxDuration = player.duration
}
delay(1.seconds / 30)
}
}
}
DisposableEffect(player) {
onDispose {
player.release()
}
}
Box(modifier = modifier) {
AndroidView(factory = {
playerView.apply {
// resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
useController = false
layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
}
})
ControllerPlayer(
isPlaying = { isPlaying },
onReplayClick = { player.seekBack() },
onPauseToggle = {
if (player.isPlaying) {
player.pause()
} else {
player.play()
}
isPlaying = isPlaying.not()
},
onForwardClick = { player.seekForward() },
totalDuration = { maxDuration },
currentTime = { currentTime },
bufferPercentage = { player.bufferedPercentage },
onSeekChanged = { newPosition ->
player.seekTo(newPosition.toLong())
},
isVisibleController = {true}
)
}
}
我不认为代码有问题,但我还是给你代码 ControllerScreen.kt
@SuppressLint("RememberReturnType")
@OptIn(UnstableApi::class)
@Composable
fun BoxScope.ControllerPlayer(
modifier: Modifier = Modifier,
isPlaying: () -> Boolean,
onReplayClick: () -> Unit,
onPauseToggle: () -> Unit,
onForwardClick: () -> Unit,
totalDuration: () -> Long,
currentTime: () -> Long,
bufferPercentage: () -> Int,
onSeekChanged: (timeMs: Float) -> Unit,
isVisibleController: () -> Boolean,
) {
val isVideoPlaying = remember(isPlaying()) { isPlaying() }
val visible = remember(isVisibleController()) { isVisibleController() }
val duration = remember(totalDuration()) { totalDuration() }
val videoTime = remember(currentTime()) { currentTime() }
val buffer = remember(bufferPercentage()) { bufferPercentage() }
AnimatedVisibility(
visible = if(AppConfig.isProduction()) visible else true,
enter = fadeIn(tween(200)),
exit = fadeOut(tween(200))
) {
Box(modifier = modifier.fillMaxSize()){
Row(
modifier = modifier
.align(Alignment.Center),
horizontalArrangement = Arrangement.SpaceEvenly
) {
IconButton(modifier = Modifier.size(40.dp), onClick = onReplayClick) {
Image(
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
painter = painterResource(id = androidx.media3.ui.R.drawable.exo_ic_chevron_left),
contentDescription = "Replay 5 seconds"
)
}
IconButton(modifier = Modifier.size(40.dp), onClick = onPauseToggle) {
Image(
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
painter =
if (isVideoPlaying) {
painterResource(id = androidx.media3.ui.R.drawable.exo_icon_pause)
} else {
painterResource(id = androidx.media3.ui.R.drawable.exo_icon_play)
},
contentDescription = "Play/Pause"
)
}
IconButton(modifier = Modifier.size(40.dp), onClick = onForwardClick) {
Image(
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
painter = painterResource(id = androidx.media3.ui.R.drawable.exo_ic_chevron_right),
contentDescription = "Forward 10 seconds"
)
}
}
Column(
modifier = modifier
.align(Alignment.BottomCenter)
.padding(bottom = 32.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.Bottom
) {
Text(
modifier = Modifier.padding(horizontal = 16.dp),
text = "${videoTime.formatMinSec()} / ${duration.formatMinSec()}",
color = DarkTheme.onSecondary
)
IconButton(
modifier = Modifier.padding(horizontal = 16.dp),
onClick = {}
) {
Image(
contentScale = ContentScale.Crop,
painter = painterResource(id = androidx.media3.ui.R.drawable.exo_icon_fullscreen_enter),
contentDescription = "Enter/Exit fullscreen"
)
}
}
Box(modifier = Modifier.fillMaxWidth()) {
// buffer bar
Slider(
value = buffer.toFloat(),
enabled = false,
onValueChange = { /*do nothing*/ },
valueRange = 0f..100f,
colors =
SliderDefaults.colors(
disabledThumbColor = Color.Transparent,
disabledActiveTrackColor = Color.Gray
)
)
// seek bar
Slider(
modifier = Modifier.fillMaxWidth(),
value = videoTime.toFloat(),
onValueChange = onSeekChanged,
valueRange = 0f..duration.toFloat(),
colors =
SliderDefaults.colors(
thumbColor = DarkTheme.tertiary,
activeTickColor = DarkTheme.onSecondary
)
)
}
}
}
}
}
fun Long.formatMinSec(): String {
return if (this == 0L) {
"..."
} else {
String.format(
"%02d:%02d",
TimeUnit.MILLISECONDS.toMinutes(this),
TimeUnit.MILLISECONDS.toSeconds(this) -
TimeUnit.MINUTES.toSeconds(
TimeUnit.MILLISECONDS.toMinutes(this)
)
)
}
}
我希望如果我按下暂停按钮并播放视频就不会重复
我将你的代码运行到我的系统中,找到了一个解决方案。
当您单击暂停按钮时,您将切换 isPlaying 的值,但它也会影响其他地方。所以我只是创建其他变量,当单击暂停时,我只是更改其他变量的值,然后它工作完美。
只需创建另一个变量
var isVideoContinue by rememberSaveable { mutableStateOf(true) }
将此 isVideoContinue 变量放入 ControllerPlayer 中
ControllerPlayer(
isPlaying = { isVideoContinue },
onReplayClick = { player.seekBack() },
onPauseToggle = {
if (player.isPlaying) {
player.pause()
} else {
player.play()
}
isVideoContinue = isVideoContinue.not()
},
onForwardClick = { player.seekForward() },
totalDuration = { maxDuration },
currentTime = { currentTime },
bufferPercentage = { player.bufferedPercentage },
onSeekChanged = { newPosition ->
player.seekTo(newPosition.toLong())
},
isVisibleController = { true }
)
player.playWhenReady = isPlaying
我不确定,但您在 playWhenReady 上使用 isPlaying,因此可能的原因是,每当单击暂停按钮时 isPlaying 变量发生变化,它就会重新启动视频。