喷气背包Compose - Exoplayer全屏

3

我正在尝试在使用Jetpack Compose制作的Android应用中播放视频。为了进行流媒体传输,我使用ExoPlayer,但我无法真正理解如何实现全屏按钮,有什么建议吗?

@Composable
private fun VideoPlayer() {
    val videoURI = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
    val httpDataSourceFactory: HttpDataSource.Factory =
        DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(false)
    val dataSourceFactory: DataSource.Factory = DataSource.Factory {
        val dataSource = httpDataSourceFactory.createDataSource()
        dataSource.setRequestProperty(
            "cookie", "cookieValue"
        )
        dataSource.setRequestProperty("Range", "1-10000")
        dataSource
    }

    val mContext = LocalContext.current
    // Initializing ExoPLayer
    val mExoPlayer = remember(mContext) {
        ExoPlayer.Builder(mContext)
            .setMediaSourceFactory(DefaultMediaSourceFactory(dataSourceFactory)).build().apply {

                val mediaItem = MediaItem.Builder()
                    .setUri(Uri.parse(videoURI))
                    .build()
                setMediaItem(mediaItem)
                playWhenReady = true
                prepare()

            }

    }

    DisposableEffect(

        // Implementing ExoPlayer
        AndroidView(factory = { context ->
            StyledPlayerView(context).apply {
                player = mExoPlayer
            }
        })
    ) {
        onDispose {
            mExoPlayer.release()
        }
    }
}

编辑 添加setControllerOnFullScreenModeChangedListener属性,exo将显示一个内置的全屏按钮,我通过在此侦听器中调用全屏函数解决了我的问题。

            AndroidView(
                factory = { context ->
                    StyledPlayerView(context).apply {
                        player = mExoPlayer
                        setControllerOnFullScreenModeChangedListener {
                        if(it)
                            //fullscreen
                        else
                            //minimize
                    }                    }
                })

1
我仍然不明白如何在这里实现全屏,因为你只是添加了//fullscreen,请问你能分享一下让视频变成全屏的代码吗? - Ahmed Rabee
你能否在这里添加全屏功能,这将非常有帮助。 - Ahmed Rabee
3个回答

8
请使用这段代码。
AndroidView(factory = {
        StyledPlayerView(context).apply {
            player = exoPlayer
            setFullscreenButtonClickListener { isFullScreen ->
                with(context) {
                    if (isFullScreen) {
                         setScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
                    } else {
                         setScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
                    }
                }
            }
      }
})

使用此扩展函数来查找活动实例:

fun Context.findActivity(): Activity? = when (this) {
    is Activity       -> this
    is ContextWrapper -> baseContext.findActivity()
    else              -> null
}

使用以下扩展函数来设置屏幕方向:

fun Context.setScreenOrientation(orientation: Int) {
    val activity = this.findActivity() ?: return
    activity.requestedOrientation = orientation
    if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
       hideSystemUi()
    } else {
       showSystemUi()
    }
}

最后,使用以下函数隐藏/显示系统UI(状态栏):

fun Context.hideSystemUi() {
    val activity = this.findActivity() ?: return
    val window = activity.window ?: return
    WindowCompat.setDecorFitsSystemWindows(window, false)
    WindowInsetsControllerCompat(window, window.decorView).let { controller ->
    controller.hide(WindowInsetsCompat.Type.systemBars())
    controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
   }
}

fun Context.showSystemUi() {
    val activity = this.findActivity() ?: return
    val window = activity.window ?: return
    WindowCompat.setDecorFitsSystemWindows(window, true)
   WindowInsetsControllerCompat(
    window,
    window.decorView
   ).show(WindowInsetsCompat.Type.systemBars())
}

7
要使应用程序全屏,有以下几种方法:
with(WindowCompat.getInsetsController(window, window.decorView)) {
    systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
    hide(WindowInsetsCompat.Type.systemBars())
}

我已经根据Kotlin进行了调整,所以你只需要将其连接到ButtononClick中,然后就可以开始使用了。

Button(
  onclick = { /*Paste above Code here*/ }
){
 Text("Go full-screen") // Whatever here, per your use-case
}

如果由于某些原因此方法不起作用,或者某些内容无法通过 onClick 访问,请使用带有 MutableState<Boolean> 作为键的 LaunchedEffect 创建并更改键以触发反应。这可能不是必要的,因为 onClick 应该能正常工作。

嗯,是的,但这样按钮会放在播放器外面,我不能把它放在里面吗? - Ansol
请分享一些示例代码,并指出您希望放置按钮的位置。同样,如果您不想将其绑定到特定的Composable,请使用声明性范式中状态管理的全能之父,即state-hoisting。简而言之,创建一个ViewModel并将布尔值存储为MutableState<T>值,使其成为整个应用程序了解是否启用全屏模式的单一真相来源。 - Richard Onslow Roper
如果你不明白的话,请告诉我。虽然很简单,但你可以使用上面的LaunchedEffect方法将其与几乎任何代码块绑定在一起,它将触发相应的反应。 - Richard Onslow Roper
此外,为什么按钮会放在 ExoPlayer 外面呢?我想这些都是 Composables,所以可以使用像 Box 这样简单的东西轻松重叠。 - Richard Onslow Roper
是的,你说得对,使用一个框将其放置在内部。无论如何,我发现可以使用props setControllerOnFullScreenModeChangedListener将exo的默认全屏按钮添加到StyledPlayerView中。 - Ansol
我无法理解,请更详细地说明。 - Richard Onslow Roper

2

我有一个示例,使用compose-media实现了全屏切换。你不必使用这个库,但解决方案是类似的。

  1. 实现一个组合器MediaContent用于媒体播放,您可以在其中封装全屏切换相关逻辑。以下代码将工作类似于YouTube的全屏切换行为(非UI)。
@Composable
private fun MediaContent(
    mediaState: MediaState,
    isLandscape: Boolean,
    modifier: Modifier = Modifier
) {
    val activity = LocalContext.current.findActivity()!!
    val enterFullscreen = { activity.requestedOrientation = SCREEN_ORIENTATION_USER_LANDSCAPE }
    val exitFullscreen = {
        @SuppressLint("SourceLockedOrientationActivity")
        // Will reset to SCREEN_ORIENTATION_USER later
        activity.requestedOrientation = SCREEN_ORIENTATION_USER_PORTRAIT
    }
    Box(modifier) {
        Media(
            mediaState,
            modifier = Modifier.fillMaxSize(),
            showBuffering = ShowBuffering.Always,
            buffering = {
                Box(Modifier.fillMaxSize(), Alignment.Center) {
                    CircularProgressIndicator()
                }
            },
        )
        Button(
            modifier = Modifier.align(Alignment.BottomEnd),
            onClick = if (isLandscape) exitFullscreen else enterFullscreen
        ) {
            Text(text = if (isLandscape) "Exit Fullscreen" else "Enter Fullscreen")
        }
    }
    val onBackPressedCallback = remember {
        object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                exitFullscreen()
            }
        }
    }
    val onBackPressedDispatcher = activity.onBackPressedDispatcher
    DisposableEffect(onBackPressedDispatcher) {
        onBackPressedDispatcher.addCallback(onBackPressedCallback)
        onDispose { onBackPressedCallback.remove() }
    }
    SideEffect {
        onBackPressedCallback.isEnabled = isLandscape
        if (isLandscape) {
            if (activity.requestedOrientation == SCREEN_ORIENTATION_USER) {
                activity.requestedOrientation = SCREEN_ORIENTATION_USER_LANDSCAPE
            }
        } else {
            activity.requestedOrientation = SCREEN_ORIENTATION_USER
        }
    }
}


然后你可以使用它:
@Composable
fun FullscreenToggle() {
    val configuration = LocalConfiguration.current
    val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE

    // hide system ui in fullscreen mode, if you need
    val systemUiController = rememberSystemUiController()
    SideEffect {
        systemUiController.isStatusBarVisible = !isLandscape
        systemUiController.isNavigationBarVisible = !isLandscape
    }

    // create and remember a MediaState instance
    val player by rememberManagedExoPlayer()
    val mediaState = rememberMediaState(player)

    // with movableContentOf, MediaContent's node can be reused between toggling
    val mediaContent = remember {
        movableContentOf { isLandscape: Boolean, modifier: Modifier ->
            MediaContent(mediaState, isLandscape, modifier)
        }
    }
    // this is the UI for normal mode, display the MediaContent with other UI elements
    Scaffold(
        topBar = {
          ...
        },
        modifier = Modifier
            .fillMaxSize()
            .systemBarsPadding(),
    ) { padding ->
        if (!isLandscape) {
            mediaContent(
                false,
                Modifier
                    .padding(padding)
                    .fillMaxWidth()
                    .aspectRatio(16f / 9f)
            )
        }
    }
    // this is the UI for fullscreen mode, only display the MediaContent
    if (isLandscape) {
        mediaContent(
            true,
            Modifier
                .fillMaxSize()
                .background(Color.Black)
        )
    }
}

您可以在此处找到完整的代码:https://github.com/fengdai/compose-media/blob/master/sample/src/main/java/com/github/fengdai/compose/media/sample/FullscreenToggle.kt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接