移除 Android WebView 中 <video> 默认播放按钮

June 30, 2021

前言

有些时日没写文章了,这是因为最近换了新部门,一直投身于工作,这段时间学到了蛮多东西,但在公司的工作时间也直线上升,看来 "Work Lift Balance" 终究是理想主义。

新部门的主要工作内容是从事移动端的定制化活动开发,面向 C 端用户,涉及的知识点十分广泛,以前只是拿来当面经背,现在真正派上了用场。

一场 S 级活动,要考虑用户的机型、网络情况,从而进行代码适配和优化(活动页的性能指标非常重要),建立完整的监控上报体系,利用 JS Bridge 调用原生能力,每次开发都需要重写 UI 组件和业务逻辑……

不过好像扯远了,这篇文章的主要内容是我在 Android WebView 中使用 <video> 标签时踩到的坑,觉得挺有意思,故记录在此。

video 在安卓手机(小米 10)WebView 中出现奇怪的 “播放按钮”

事情的起因是我需要在 WebView 中播放一段 .mp4 格式的视频,理所当然想到了用 <video> 标签来展示。

测试代码如下:

html
<style>
video {
border: 2px solid #ff4040;
}
</style>
<video id="video">
<source
src="https://cdn.baobangdong.cn/SampleVideo_360x240_1mb.mp4"
type="video/mp4"
/>
</video>
html
<style>
video {
border: 2px solid #ff4040;
}
</style>
<video id="video">
<source
src="https://cdn.baobangdong.cn/SampleVideo_360x240_1mb.mp4"
type="video/mp4"
/>
</video>

在 iOS 的 WebView 上是正常显示的。

但是在 Android 的 WebView 上显示如下:

发现莫名其妙多了一张类似 “播放按钮” 的图片浮在 video 之上,只有当 video 初次加载时会出现,播放过一次后便会消失,这是怎么一回事?

查阅资料后,才明白这是 Android WebView 的默认行为,其根本原因是 <video> 没有设置 poster 属性,所以自作聪明的 Android WebView 给它加了默认 poster(海报、封面)。

下面我介绍两种方法,从不同的视角去解决这个问题。

注意:千万不是在浏览器中进行测试,因为 WebView 和浏览器有关系,但并不完全相同,如果你在浏览器中进行测试,会发现没有设置 poster 属性的 <video> 默认展示视频的第一帧。

如果您希望在客户端应用中提供 Web 应用(或只是网页),则可以使用 WebView 执行该操作。WebView 类是 Android 的 View 类的扩展,可让您将网页显示为 Activity 布局的一部分。它不会包含功能全面的网络浏览器的任何功能,例如导航控件或地址栏。WebView 默认只显示网页。

如下是小米 10 开发者模式下的 WebView 实现:

前端视角

站在前端的视角上出发,我们能控制的只能是 <video> HTML 元素,对它做一些手脚。

为 video 添加 poster 属性

方法很简单,就是设置 <video>poster 属性。根据 MDN 的解释,它的值为一张图片的 URL,在以下情况中会被展示,外在表现是看起来替换了视频的第一帧,好比一张“海报”。

  • 视频加载中
  • 视频加载完但未触发播放
  • 视频播放完毕

举个例子,poster="https://via.placeholder.com/1x1" or poster="noposter"

注意,空值将会被忽略 poster="",所以赋值为空字符串是没有用的。

Native(客户端)视角

说完了前端视角,在“大前端”趋势下,我们从客户端视角(这里主要指 Android)出发,学习点 Java(Kotlin)语言也是不错的,所谓技多不压身。

重写 WebChromeClient 的 getDefaultVideoPoster() 方法

我是用 Android Studio 搭配 Kotlin 语言进行开发,AVD 是 Pixel XL API 30.

具体文档参照 WebChromeClient | getDefaultVideoPoster

代码如下所示(MainActivity.kt):

kotlin
class MainActivity : AppCompatActivity() {
// 重写 WebChromeClientCustomPoster 方法
private class WebChromeClientCustomPoster : WebChromeClient() {
override fun getDefaultVideoPoster(): Bitmap? {
return Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myWebView: WebView = findViewById(R.id.webview)
myWebView.webChromeClient = WebChromeClientCustomPoster()
// 加载前端页面
myWebView.loadUrl("http://10.94.62.121:5000")
}
}
kotlin
class MainActivity : AppCompatActivity() {
// 重写 WebChromeClientCustomPoster 方法
private class WebChromeClientCustomPoster : WebChromeClient() {
override fun getDefaultVideoPoster(): Bitmap? {
return Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myWebView: WebView = findViewById(R.id.webview)
myWebView.webChromeClient = WebChromeClientCustomPoster()
// 加载前端页面
myWebView.loadUrl("http://10.94.62.121:5000")
}
}

没重写方法前:

重写方法后:

大功告成,通过修改原生代码,从根源上解决了这个问题。

Reference


B2D1 (包邦东)

Written by B2D1 (包邦东)