如何修改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




