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

Android TV中Jetpack Compose的AndroidView内WebView D-pad聚焦问题

解决Jetpack Compose TV应用中YouTube广告Skip按钮的D-pad焦点问题

针对Compose TV应用里WebView加载YouTube广告时,遥控器D-pad无法聚焦到“Skip ad”按钮的问题,以下是具体解决方案:

核心问题分析

Compose焦点系统与原生Android View焦点系统相互独立,直接用Compose的FocusRequester无法穿透到WebView内部的DOM元素;同时WebView在TV设备上的焦点传递、样式显示需要额外配置,FrameLayout默认的焦点转发逻辑也会干扰内部WebView的焦点获取。

具体解决步骤

1. 修复YouTubePlayerWrapper的焦点传递与WebView配置

重写自定义FrameLayout的焦点相关方法,确保焦点直接转发给内部WebView,并配置WebView的TV焦点属性:

class YouTubePlayerWrapper(context: Context) : FrameLayout(context) {
    private lateinit var webView: WebView

    init {
        setupWebView()
    }

    private fun setupWebView() {
        webView = WebView(context).apply {
            // 基础配置:必须开启JS支持YouTube iframe API
            settings.javaScriptEnabled = true
            settings.mediaPlaybackRequiresUserGesture = false
            settings.builtInZoomControls = false
            settings.displayZoomControls = false

            // TV焦点关键配置
            isFocusable = true
            isFocusableInTouchMode = true
            defaultFocusHighlightEnabled = true // 强制显示焦点框

            // 焦点变化时触发WebView内部元素的焦点处理
            setOnFocusChangeListener { _, hasFocus ->
                if (hasFocus) requestFocusFromTouch()
            }
        }
        addView(webView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
    }

    // 重写焦点请求方法,直接转发给WebView
    override fun requestFocus(direction: Int, previouslyFocusedRect: Rect?): Boolean {
        return webView.requestFocus(direction, previouslyFocusedRect)
    }

    override fun requestFocus(): Boolean = webView.requestFocus()
    override fun requestFocusFromTouch(): Boolean = webView.requestFocusFromTouch()

    // 加载视频、释放资源的逻辑保持原有实现
    fun loadVideo(videoId: String) {
        // 你的YouTube iframe加载代码
    }

    fun release() {
        webView.loadUrl("about:blank")
        webView.destroy()
    }
}

2. 调整Composable中的焦点切换逻辑

放弃使用Compose的FocusRequester,直接操作原生WebView的焦点,并配合Compose的FocusManager处理焦点切换:

@Composable
fun YoutubePlayer(
    onAdFinish: () -> Unit = {},
    onAdStarted: () -> Unit = {},
    focusManager: FocusManager,
    modifier: Modifier = Modifier
) {
    var youtubePlayerViewWrapper: YouTubePlayerWrapper? by remember { mutableStateOf(null) }

    val listener = remember {
        object : AbstractYouTubePlayerListener() {
            override fun onAdStarted() {
                onAdStarted()
                // 让Compose焦点系统释放焦点,避免抢占原生View的焦点
                focusManager.clearFocus()
                // 直接请求WebView获取焦点,触发内部元素的焦点处理
                youtubePlayerViewWrapper?.requestFocus()
            }

            override fun onAdCompleted() {
                onAdFinish()
                // 广告结束后,将焦点交还给Compose的父布局
                focusManager.moveFocus(FocusDirection.Enter)
                youtubePlayerViewWrapper?.clearFocus()
            }
        }
    }

    AndroidView(
        factory = { context ->
            YouTubePlayerWrapper(context).also {
                youtubePlayerViewWrapper = it
                // 初始化播放器并设置listener的逻辑
            }
        },
        modifier = modifier.fillMaxSize(),
        onRelease = { it.release() }
    )
}

3. 外层布局的焦点配置

确保父布局能在广告结束后重新获取焦点:

@Composable
fun VideoPlayerScreen(
    player: YouTubePlayer,
    videoPlayerState: VideoPlayerState,
    viewModel: VideoPlayerViewModel,
    focusManager: FocusManager = LocalFocusManager.current
) {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .dPadEvents(player, videoPlayerState, viewModel.adState, focusManager)
            .focusable() // 允许父布局重新获取焦点
    ) {
        YoutubePlayer(
            onAdFinish = {
                focusManager.moveFocus(FocusDirection.Enter)
            },
            onAdStarted = { /* 你的广告开始回调逻辑 */ },
            focusManager = focusManager
        )
    }
}

额外注意事项

  • 确保设备的系统WebView版本为最新,旧版本可能存在TV焦点处理的bug。
  • 避免同时混用Compose焦点系统和原生View焦点系统的API,防止焦点冲突。
  • 测试时注意不同品牌TV设备的WebView行为差异,部分设备可能需要额外调整WebSettings的参数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 08:40:59