需求 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 内核原生的视频播放器
遇到的小插曲
- 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")
// }
}
}
}