You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Kotlin中ExoPlayer画中画及Navigation Component实现方案咨询

如何在Android Kotlin中为ExoPlayer创建画中画(Pip)功能?

1. 配置Manifest与Activity属性

首先在AndroidManifest.xml中为播放页面的Activity添加画中画支持,同时配置配置变更参数避免Activity重建:

<activity
    android:name=".PlayerActivity"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" />

2. 初始化ExoPlayer并处理画中画逻辑

在Activity中完成ExoPlayer初始化,添加进入画中画的触发逻辑,并监听画中画状态切换:

class PlayerActivity : AppCompatActivity() {
    private lateinit var player: ExoPlayer
    private var isInPipMode = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_player)

        // 初始化ExoPlayer
        player = ExoPlayer.Builder(this).build()
        val playerView = findViewById<PlayerView>(R.id.player_view)
        playerView.player = player

        // 设置播放源(替换为实际视频地址)
        val mediaItem = MediaItem.fromUri("https://example.com/video.mp4")
        player.setMediaItem(mediaItem)
        player.prepare()
        player.play()

        // 触发进入画中画
        findViewById<Button>(R.id.enter_pip_btn).setOnClickListener {
            enterPictureInPictureMode()
        }
    }

    override fun onPictureInPictureModeChanged(
        isInPictureInPictureMode: Boolean,
        newConfig: Configuration?
    ) {
        super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
        isInPipMode = isInPictureInPictureMode
        // 画中画状态下隐藏播放控制器
        val playerView = findViewById<PlayerView>(R.id.player_view)
        playerView.useController = !isInPictureInPictureMode
    }

    override fun onDestroy() {
        super.onDestroy()
        player.release()
    }
}

结合Navigation Component实现画中画功能(Pip显示时应用不关闭)

Navigation Component基于单Activity多Fragment架构,核心是确保画中画状态下宿主Activity不被销毁,同时适配Fragment的显示逻辑。

1. 配置宿主Activity

和第一个问题一致,在AndroidManifest.xml中为宿主Activity添加画中画支持:

<activity
    android:name=".MainActivity"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" />

2. 宿主Activity处理画中画与Navigation联动

class MainActivity : AppCompatActivity() {
    private lateinit var navController: NavController
    private var isInPipMode = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        navController = findNavController(R.id.nav_host_fragment)
        val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav)
        bottomNav.setupWithNavController(navController)
    }

    override fun onPictureInPictureModeChanged(
        isInPictureInPictureMode: Boolean,
        newConfig: Configuration?
    ) {
        super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
        isInPipMode = isInPictureInPictureMode

        // 画中画状态下隐藏底部导航栏
        val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav)
        bottomNav.visibility = if (isInPictureInPictureMode) View.GONE else View.VISIBLE

        // 若当前不在播放Fragment,自动切换到播放Fragment
        val currentDestId = navController.currentDestination?.id
        if (isInPictureInPictureMode && currentDestId != R.id.playerFragment) {
            navController.navigate(R.id.playerFragment)
        }
    }

    // 画中画状态下返回不关闭应用,而是退出画中画
    override fun onBackPressed() {
        if (isInPipMode) {
            exitPictureInPictureMode()
        } else {
            super.onBackPressed()
        }
    }

    // 用户切换到其他应用时,若当前在播放Fragment则自动进入画中画
    override fun onUserLeaveHint() {
        super.onUserLeaveHint()
        val currentDestId = navController.currentDestination?.id
        if (currentDestId == R.id.playerFragment && !isInPipMode) {
            enterPictureInPictureMode()
        }
    }
}

3. 播放Fragment中的ExoPlayer逻辑

class PlayerFragment : Fragment() {
    private lateinit var player: ExoPlayer
    private var isPlayerStarted = false

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_player, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 初始化ExoPlayer
        player = ExoPlayer.Builder(requireContext()).build()
        val playerView = view.findViewById<PlayerView>(R.id.player_view)
        playerView.player = player

        // 设置播放源
        val mediaItem = MediaItem.fromUri("https://example.com/video.mp4")
        player.setMediaItem(mediaItem)
        player.prepare()

        // 手动触发进入画中画
        view.findViewById<Button>(R.id.enter_pip_btn).setOnClickListener {
            requireActivity().enterPictureInPictureMode()
        }
    }

    override fun onStart() {
        super.onStart()
        if (isPlayerStarted) player.play()
    }

    override fun onPause() {
        super.onPause()
        isPlayerStarted = player.isPlaying
        player.pause()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        player.release()
    }
}

内容的提问来源于stack exchange,提问作者Mohammad Javad Habibpour

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 21:15:13