移除 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"><sourcesrc="https://cdn.baobangdong.cn/SampleVideo_360x240_1mb.mp4"type="video/mp4"/></video>
html
<style>video {border: 2px solid #ff4040;}</style><video id="video"><sourcesrc="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")}}
没重写方法前:

重写方法后:

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