我正在使用 Jetpack Compose 构建一个采用模块化和 Clean 架构的 Android 应用。有时使用 ExoPlayer 播放视频。我的屏幕顶部有 ExoPlayer,播放器下方有...
我正在使用 Jetpack Compose 构建采用模块化和 Clean 架构的 Android 应用。有时使用 ExoPlayer 播放视频。
我的屏幕顶部有 ExoPlayer,播放器下方有一个带有选项卡的简单 UI。当我在选项卡之间切换时,视频会变得混乱。
@Composable
internal fun LessonDetailsScreenRoute(
) {
var selectedVideoUrl by remember { mutableStateOf<String?>(dummyUrl) }
var selectedTab by remember {
mutableStateOf(LessonDetailsScreenTabs.CONTENT)
}
Column (modifier = Modifier.fillMaxSize()){
// Moving the player outside the recomposable part
PlayerContainer(selectedVideoUrl)
LessonDetailsScreen(
selectedTab = selectedTab,
onTabSelected = { newTab -> selectedTab = newTab },
)
}
}
@Composable
internal fun LessonDetailsScreen(
selectedTab: LessonDetailsScreenTabs,
onTabSelected: (LessonDetailsScreenTabs) -> Unit,
) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.background)
) {
// Lesson details AppBar
LessonAppBar(chapterName = chapterName, lessonName = lessonName, onBackPressed = onBackPressed)
// Tabs
UtkorshoTabs(
titles = lessonDetailsTabsTitle,
tabIdentifier = LessonDetailsScreenTabs.entries,
tabSelected = selectedTab,
onTabSelected = onTabSelected
)
// Handle the selected tab
when (selectedTab) {
LessonDetailsScreenTabs.CONTENT -> {
Text(text = "Content Tab")
}
LessonDetailsScreenTabs.QNA -> {
Box(modifier = Modifier.size(50.dp)) // Another simple UI element
}
}
}
}
@OptIn(UnstableApi::class)
@Composable
fun PlayerContainer(videoUrl: String?) {
// Isolate the player from the recompositions of other UI elements
// RecorderPlayerScreen(videoUrl = videoUrl)
val context = LocalContext.current
// Keep the player instance stable across recompositions
val playerInstance = remember {
ExoPlayer.Builder(context).build().apply {
playWhenReady = true
}
}
// Only prepare the player when the URL changes
LaunchedEffect(videoUrl) {
Log.d("PROBLEM_DIAGNOSIS", "Preparing player with URL: $videoUrl")
videoUrl?.let {
val mediaSource = createDataSource(it)
playerInstance.setMediaSource(mediaSource)
playerInstance.prepare()
}
}
// Only dispose of the player when the entire screen is disposed
DisposableEffect(Unit) {
onDispose {
Log.d("PROBLEM_DIAGNOSIS", "Releasing player as screen is disposed")
playerInstance.release()
}
}
// Render the Player View
AndroidView(
factory = { ctx ->
Log.d("PROBLEM_DIAGNOSIS", "PlayerView created")
PlayerView(ctx).apply {
player = playerInstance
}
},
update = { view ->
Log.d("PROBLEM_DIAGNOSIS", "PlayerView updated")
view.player = playerInstance
},
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
)
}
when (selectedTab) {
LessonDetailsScreenTabs.CONTENT -> {
Box(modifier = Modifier.size(50.dp))
}
LessonDetailsScreenTabs.QNA -> {
Box(modifier = Modifier.size(50.dp)) // Another simple UI element
}
}
如果我不保留内容和 Qna 中的任何东西,它不会给播放器带来问题。如果我只保留 Box 或 Spacer,它不会给播放器带来任何问题。但如果我使用简单的文本视图,它就会开始产生上述问题。