Android WebView 音频播放控制 & 视频全屏播放

需求 1

播放是没有问题的,主要是退出页面,或者当前页面 onPause 时,要停止音乐播放

解决方案

找了好久解决方案,以下仅就我所用到且有用的,做记录:
自定义 WebView,先实例化,注册监听

    private fun audioWebViewControl() {
        audioManager = context?.getSystemService(Context.AUDIO_SERVICE) as AudioManager
        listener = AudioManager.OnAudioFocusChangeListener {
//            when (it) {
//                AudioManager.AUDIOFOCUS_LOSS -> ToastUtils.showShortToast(context, "loss")
//                AudioManager.AUDIOFOCUS_GAIN -> ToastUtils.showShortToast(context, "gain")
//            }
        }
    }

争夺音频输出的控制权(这是WebView中的 onPause 方法)

    override fun onPause() {
        var i = 0
        do {
            val result = audioManager?.requestAudioFocus(listener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
            if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
                break
            }
            i++
        } while (i < 10)
        super.onPause()
    }

需求 2

要求富文本中的视频可以全屏播放,调用的是 WebView 内核原生的视频播放器

遇到的小插曲
  1. PC可以正常全屏播放,而Android端不展示 全屏按钮
    private lateinit var rootView: FrameLayout
    fun initView(content: String, rootView: FrameLayout) {
        initView(content, null)
        this.rootView = rootView
    }
 webChromeClient = (object : WebChromeClient() {
            override fun onShowCustomView(view: View?, callback: CustomViewCallback?) {
                super.onShowCustomView(view, callback)
                customView = view
                customViewCallback = callback
//                ToastUtils.showShortToast(context, "show")
                visibility = View.GONE
                rootView.addView(customView)
            }

            override fun onHideCustomView() {
                super.onHideCustomView()
                if (customViewCallback != null) {
//                    ToastUtils.showShortToast(context, "hide")
                    customViewCallback!!.onCustomViewHidden()
                    visibility = View.VISIBLE
                    rootView.removeView(customView)
                }
            }
        })

或者

 webChromeClient = (object : WebChromeClient() {
            override fun onShowCustomView(view: View?, callback: CustomViewCallback?) {
                super.onShowCustomView(view, callback)
                customView = view
                customViewCallback = callback
                rootView.addView(customView)

            }

            override fun onHideCustomView() {
                super.onHideCustomView()
                if (customViewCallback != null) {
                    rootView.removeView(customView)
                }
            }
        })

设置完成就可以展示出来了,需要注意的是:
一定要先实例化 WebChromeClient,否则有可能仍不展示

        webChromeClient = WebChromeClient()

小技巧

可能上述已经解决大部分问题。但是我遇到了特殊情况:

页面结构

现在我在最底层,也就是单个子任务页,其父容器也就是ViewPage,未铺满全屏且自适应高度。当我将最外层布局 传入时,就将ViewPage撑了1.5个屏左右,且向上滑动还有其他内容区。

怎么能让视频全屏的时候浮在所有的view上面,而不随着全屏拉伸内容区呢?
即使是在多层嵌套的最底层。

解决

灵感来自传入的类型 FrameLayout
我忽然意识到整个View树的最顶端就是 FrameLayout ,也就是 DecorView

    fun initView(content: String, rootView: FrameLayout) {
        initView(content, null)
        this.rootView = rootView
    }

于是,问题迎刃而解,所谓 以无厚入有间,恢恢乎其于游刃必有余地矣。

    wv_task_sub_topic.initView(it, activity!!.window.decorView as FrameLayout)

项目中用到的地方

//  设置题干富文本
     subtaskEntity.description?.let {
//   需要在父容器中展示,而不是当前view,所以传入 DecorView 会让全屏播放脱离父view的束缚
         wv_task_sub_topic.initView(it, activity!!.window.decorView as FrameLayout)
     }

传入 DecorView 会让全屏播放脱离父view的束缚,这是没有错的,但是又遇到了新的问题。

后续补充(2018/10/09)

忽略了个别机型的 Navigation Bar (导航栏)。
DecorView 是包含 Navigation Bar (导航栏) 的,
**全屏后会出现被 导航栏 遮挡 退出全屏的按钮,而无法退出全屏。
固然可以通过各种测量来解决导航栏的问题,但我们应从根源上解决问题!

传入一个不包含导航栏的父View不就可以了?这就是答案!

Window的RootView,表示当前应用程序的有效高度。即去掉状态栏和导航栏后的高度;

// 获取方式
activity!!.window.findViewById(Window.ID_ANDROID_CONTENT)

最后修改代码如下:

//        设置题干富文本
        subtaskEntity.description?.let {
            //            需要在父容器中展示,而不是当前view,所以传入 DecorView 会让全屏播放脱离父view的束缚
//            wv_task_sub_topic.initView(it, activity!!.window.decorView as FrameLayout)
            wv_task_sub_topic.initView(it, activity!!.window.findViewById(Window.ID_ANDROID_CONTENT) as FrameLayout)
        }

附自定义WebView代码

/**
 * <pre>
 *     author : jake
 *     time   : 2018/07/31
 *     function   :  webview 简装
 *     version: 1.0
 * </pre>
 */
class TaskTitleWebView(context: Context?, attrs: AttributeSet? = null) : WebView(context, attrs) {

    private lateinit var rootView: FrameLayout
    private var customViewCallback: WebChromeClient.CustomViewCallback? = null
    private var customView: View? = null

    private var audioManager: AudioManager? = null
    private lateinit var listener: AudioManager.OnAudioFocusChangeListener


    fun initView(content: String) {
        initView(content, null)
    }

    fun initView(content: String, rootView: FrameLayout) {
        initView(content, null)
        this.rootView = rootView
    }

    @SuppressLint("SetJavaScriptEnabled")
    fun initView(content: String, callBack: WebViewClientCallBack?) {
        audioWebViewControl()
        setBackgroundColor(0) //设置背景色
        background.alpha = 0 //设置填充透明度(布局中一定要设置background,不然getbackground会是null)

        val webSettings = settings
        webSettings.javaScriptEnabled = true
        webSettings.builtInZoomControls = true
        webSettings.displayZoomControls = false
        scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY //取消滚动条白边效果
        webViewClient = WebViewClient()
        webChromeClient = WebChromeClient()
        webSettings.defaultTextEncodingName = "UTF-8"
        webSettings.blockNetworkImage = false
//        loadData(content, "text/html", "UTF-8")
        loadData(content, "text/html; charset=UTF-8", null)


        webChromeClient = (object : WebChromeClient() {
            override fun onShowCustomView(view: View?, callback: CustomViewCallback?) {
                super.onShowCustomView(view, callback)
                customView = view
                customViewCallback = callback
//                ToastUtils.showShortToast(context, "show")
                visibility = View.GONE
                rootView.addView(customView)
            }

            override fun onHideCustomView() {
                super.onHideCustomView()
                if (customViewCallback != null) {
//                    ToastUtils.showShortToast(context, "hide")
                    customViewCallback!!.onCustomViewHidden()
                    visibility = View.VISIBLE
                    rootView.removeView(customView)
                }
            }
        })

    }

    override fun onPause() {
        var i = 0
        do {
            val result = audioManager?.requestAudioFocus(listener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
            if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
                break
            }
            i++
        } while (i < 10)
        super.onPause()
    }



    private fun audioWebViewControl() {
        audioManager = context?.getSystemService(Context.AUDIO_SERVICE) as AudioManager
        listener = AudioManager.OnAudioFocusChangeListener {
//            when (it) {
//                AudioManager.AUDIOFOCUS_LOSS -> ToastUtils.showShortToast(context, "loss")
//                AudioManager.AUDIOFOCUS_GAIN -> ToastUtils.showShortToast(context, "gain")
//            }
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容