You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何修改Android Leanback库支持背景视频替代背景图,并在Leanback示例应用中实现卡片选中切换对应视频

搞定Leanback背景视频替代背景图片的方案

嘿,我来帮你搞定Leanback背景视频替换的问题!刚好我之前折腾过类似的需求,给你一步步拆解怎么改~

先捋捋核心逻辑

原来Leanback示例里是用BackgroundManager来切换背景图的,我们要做的就是把这个背景图容器换成视频播放器(比如系统的VideoView或者更靠谱的ExoPlayer),同时监听卡片选中事件来切换对应视频,还要保证卡片UI始终在视频上层显示。

对着Leanback示例动手改的步骤

1. 给布局加个底层视频播放器

首先找到示例里的主Activity布局(比如activity_main.xml),把原来的背景容器换成VideoView或者ExoPlayer的PlayerView,放在最底层,让Leanback的Fragment飘在上面:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 背景视频层,放在最下面 -->
    <VideoView
        android:id="@+id/background_video"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"/>

    <!-- Leanback的BrowseFragment,确保在视频上层 -->
    <fragment
        android:id="@+id/browse_fragment"
        android:name="androidx.leanback.app.BrowseSupportFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <!-- 可选:加个半透明遮罩,避免视频太亮影响卡片阅读 -->
    <View
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#80000000"/> <!-- 50%透明度的黑色 -->
</FrameLayout>

2. 替换背景切换逻辑,改成播放视频

原来的示例里是在卡片选中时调用BackgroundManager.setDrawable()换图,现在我们要改成切换视频。

用系统VideoView的版本

在你的BrowseSupportFragment里初始化VideoView,然后监听卡片选中事件:

// 找到布局里的VideoView
val backgroundVideo = requireActivity().findViewById<VideoView>(R.id.background_video)
// 设置视频播完自动循环
backgroundVideo.setOnCompletionListener { it.start() }

// 监听卡片选中事件,切换对应视频
setOnItemViewSelectedListener { _, item, _, _ ->
    // 假设你的数据模型(比如Movie类)里有视频地址字段,比如videoUrl
    val movie = item as Movie
    val videoUri = Uri.parse(movie.videoUrl)
    backgroundVideo.setVideoURI(videoUri)
    backgroundVideo.start()
}

用ExoPlayer的版本(更推荐,兼容性更好)

如果你的视频格式比较多,或者想更稳定,用ExoPlayer更靠谱:

// 初始化ExoPlayer
val exoPlayer = SimpleExoPlayer.Builder(requireContext()).build()
val playerView = requireActivity().findViewById<PlayerView>(R.id.background_player_view)
playerView.player = exoPlayer
playerView.useController = false // 隐藏播放控制条,不然会挡住卡片

// 选中卡片切换视频
setOnItemViewSelectedListener { _, item, _, _ ->
    val movie = item as Movie
    val videoUri = Uri.parse(movie.videoUrl)
    val mediaItem = MediaItem.fromUri(videoUri)
    exoPlayer.setMediaItem(mediaItem)
    exoPlayer.prepare()
    exoPlayer.playWhenReady = true
    exoPlayer.repeatMode = Player.REPEAT_MODE_ONE // 循环播放当前视频
}

// 记得在Fragment销毁时释放资源,避免内存泄漏
override fun onDestroy() {
    super.onDestroy()
    exoPlayer.release()
}

3. 处理异常和 fallback 情况

万一视频加载失败,或者某些卡片没有视频,得处理一下,比如切回背景图:

// VideoView的错误监听
backgroundVideo.setOnErrorListener { _, _, _ ->
    // 加载失败,显示默认背景图
    val backgroundManager = BackgroundManager.getInstance(requireActivity())
    backgroundManager.setDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.default_bg))
    backgroundVideo.visibility = View.GONE
    true
}

// 卡片选中时判断是否有视频
setOnItemViewSelectedListener { _, item, _, _ ->
    val movie = item as Movie
    if (!TextUtils.isEmpty(movie.videoUrl)) {
        // 有视频,显示播放器并播放
        backgroundVideo.visibility = View.VISIBLE
        val videoUri = Uri.parse(movie.videoUrl)
        backgroundVideo.setVideoURI(videoUri)
        backgroundVideo.start()
        // 隐藏原来的背景图
        BackgroundManager.getInstance(requireActivity()).setDrawable(null)
    } else {
        // 没有视频,切回背景图
        backgroundVideo.visibility = View.GONE
        BackgroundManager.getInstance(requireActivity()).setDrawable(ContextCompat.getDrawable(requireContext(), movie.bgImageResId))
    }
}

4. 初始状态设置

应用刚启动时,还没选中任何卡片,可以播放一个默认的背景视频:

// 启动时加载默认视频
val defaultVideoUri = Uri.parse("android.resource://${requireContext().packageName}/${R.raw.default_bg_video}")
backgroundVideo.setVideoURI(defaultVideoUri)
backgroundVideo.start()

一些小提醒

  • 视频资源尽量用TV适配的分辨率,比如1920x1080,不然拉伸后会模糊。
  • 低配置TV上尽量用硬件加速的播放器,ExoPlayer默认就支持,比系统VideoView更稳。
  • 遮罩的透明度可以自己调,比如#60000000是37.5%的黑色,根据你的卡片样式来调整。

内容的提问来源于stack exchange,提问作者Shane

火山引擎 最新活动