WebViewで動画をシークさせたい
Youtube等の動画サイトにはシークバーやスキップボタンがあり、再生位置の移動ができるような仕様になっていることが多いです。
普通はデフォルトの機能を使えばそれで十分だと思いますが、時にはシークボタンを自作して動画の再生位置を操作したい.....なんて思うことがあるかもしれません。
本記事では、WebView内で動画のシークボタンを実装するサンプルを紹介しようと思います
基本機能
- WebView内で動画が再生されている時、独自で実装したシークボタンをタップすることで、ユーザーが動画の再生位置を調整できる
シークのためのJavaScript
webViewで再生位置を操作するにはJavaScriptが必要です。
まず、WebView内で動画を操作するためのJavaScriptをkotlinで作成します。以下のコードは、<video>
または <audio>
要素を対象に、現在の再生位置から指定された時間だけ進めたり戻したりするためのスクリプトです。
object Script {
fun seekToTime(seekTime: Double) =
"""
(function() {
const media = document.querySelector('video, audio');
if (!media) {
console.log('No media element found');
return;
}
const currentTime = media.currentTime;
media.currentTime = $seekTime + currentTime;
})();
"""
}
webViewの実装
javascriptの実装が終わったら、webViewを実装していきます。
今回はwebViewの下にシークボタンを配置します。
シークボタンはSeekButton
で実装しています。
(サンプルなのでデザインは適当です......)
@SuppressLint("SetJavaScriptEnabled")
@Composable
private fun BrowserWebView(url: String) {
val isInspecting = LocalInspectionMode.current
var webView: WebView? by remember { mutableStateOf(null) }
Column {
AndroidView(
modifier = Modifier
.weight(1f)
.clipToBounds(),
factory = {
WebView(it).apply {
if (!isInspecting) {
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
}
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
webViewClient = WebViewClient()
webChromeClient = WebChromeClient()
loadUrl(url)
}
},
update = { newWebView ->
if (isInspecting) return@AndroidView
webView = newWebView
}
)
// シークボタン
Row(
modifier = Modifier
.height(80.dp)
.fillMaxWidth()
.background(Color.LightGray)
.padding(horizontal = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
// 1分戻す
SeekButton(
text = "<< 1m",
seekTime = -60.0,
webView = webView
)
// 10秒戻す
SeekButton(
text = "<< 10s",
seekTime = -10.0,
webView = webView
)
// 10秒進む
SeekButton(
text = ">> 10s",
seekTime = 10.0,
webView = webView
)
// 1分進む
SeekButton(
text = ">> 1m",
seekTime = 60.0,
webView = webView
)
}
}
}
@Composable
private fun SeekButton(
text: String,
modifier: Modifier = Modifier,
seekTime: Double,
webView: WebView?,
) {
Text(
text = text,
modifier = modifier
.size(height = 40.dp, width = 70.dp)
.background(Color.White, shape = RoundedCornerShape(20.dp))
.padding(vertical = 10.dp)
.clickable(
// タップ時のリップルをなくす
interactionSource = remember { MutableInteractionSource() },
indication = null
) {
webView?.evaluateJavascript(Script.seekToTime(seekTime), null)
},
textAlign = TextAlign.Center,
)
}
これでYoutubeなどで動画を再生して、各ボタンを押すとシークします。
ちなみに、需要があるかわかりませんが、動画再生直後にあらかじめ指定した時間へ移動させることもできます。
WebViewClient
のdoUpdateVisitedHistory()
をoverrideして、javascriptを実行することでできます。
(onPageFinished()
だとYoutubeではうまく動作しません。)
...
webViewClient = object: WebViewClient() {
override fun doUpdateVisitedHistory(
view: WebView?,
currentUrl: String?,
isReload: Boolean,
) {
super.doUpdateVisitedHistory(view, currentUrl, isReload)
// 任意の時間をセットしておく
view?.evaluateJavascript(Script.seekToTime(30.0), null)
}
...
上のコードだと、動画を再生した直後に30秒のところに再生位置が移動します。