0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Android】rememberUpdateStateを使おう

Last updated at Posted at 2025-03-23

はじめに

先日の記事でwebViewでの動画のシークを取り上げましたが、その続き。

doUpdateVisitedHistory()を使って動画の再生開始位置を指定する方法を載せました。
これを利用して秒数を入力して、動画開始時の再生位置を自由に操作できるようにしようとしました。

作成コード(問題がある例)

@SuppressLint("SetJavaScriptEnabled")
@Composable
private fun BrowserWebView(url: String, seekTime: Double = 0.0) {
    val isInspecting = LocalInspectionMode.current
    var webView: WebView? by remember { mutableStateOf(null) }
    Column {
        AndroidView(
            modifier = Modifier.clipToBounds().weight(1f),
            factory = {
                WebView(it).apply {
                    if (!isInspecting) {
                        settings.javaScriptEnabled = true
                        settings.domStorageEnabled = true
                    }
                    layoutParams = ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT
                    )
                    webViewClient = object : WebViewClient() {
                        override fun doUpdateVisitedHistory(
                            view: WebView?,
                            currentUrl: String?,
                            isReload: Boolean,
                        ) {
                            super.doUpdateVisitedHistory(view, currentUrl, isReload)
                            view?.evaluateJavascript(Script.seekToTime(seekTime), null)
                        }
                    }
                    webChromeClient = WebChromeClient()
                    loadUrl(url)
                }
            },
            update = { newWebView ->
                if (isInspecting) return@AndroidView
                webView = newWebView
            }
        )
    }
}

// SeekButton, Script.seekToTimeは前回の記事を参照のこと(省略)

@Composable
@Preview
private fun BrowserWebViewScreen() {
    var seekTime by remember { mutableDoubleStateOf(0.0) }
    var value: String by remember { mutableStateOf("") }
    Column {
        // 秒数を入力する欄
        // サンプルです
        TextField(
            value = value,
            onValueChange = { value = it },
            modifier = Modifier
                .fillMaxWidth()
                .padding(10.dp),
            placeholder = { Text("動画開始時に遷移させたい秒数を入力") },
            keyboardOptions = KeyboardOptions.Default.copy(
                imeAction = ImeAction.Done,
                keyboardType = KeyboardType.Text,
            ),
            keyboardActions = KeyboardActions(
                onDone = {
                    seekTime = value.toDoubleOrNull() ?: 0.0
                    Log.d("testComment", "seekTime: $seekTime")
                }
            ),
            singleLine = true,
        )
        BrowserWebView(url = "https://www.youtube.com/", seekTime = seekTime)
    }
}

しかし、これだとうまく動作せず、秒数を指定しても動画は最初から再生されてしまいます。

問題の原因

doUpdateVisitedHistory メソッドの evaluateJavascript で渡している seekTimeBrowserWebView の初回コンポーズ時の値で固定されていて、再コンポーズが発生しても更新されません。

解決策:rememberUpdatedState を使用する

ではどうするか。
Jetpack Compose では rememberUpdatedState を使用すると、外部から渡された値を適切に更新できます。

修正後のコード

@SuppressLint("SetJavaScriptEnabled")
@Composable
private fun BrowserWebView(url: String, seekTime: Double = 0.0) {
    val isInspecting = LocalInspectionMode.current
    var webView: WebView? by remember { mutableStateOf(null) }
    val updateSeekTime by rememberUpdatedState(seekTime) // 外からもらってきた値をrememberする
    Column {
        AndroidView(
            modifier = Modifier.clipToBounds().weight(1f),
            factory = {
                WebView(it).apply {
                    if (!isInspecting) {
                        settings.javaScriptEnabled = true
                        settings.domStorageEnabled = true
                    }
                    layoutParams = ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT
                    )
                    webViewClient = object : WebViewClient() {
                        override fun doUpdateVisitedHistory(
                            view: WebView?,
                            currentUrl: String?,
                            isReload: Boolean,
                        ) {
                            super.doUpdateVisitedHistory(view, currentUrl, isReload)
                            view?.evaluateJavascript(Script.seekToTime(updateSeekTime), null)
                        }
                    }
                   ... // 以下省略

rememberUpdatedState(seekTime) を使用すると、seekTime の最新の値を doUpdateVisitedHistory で参照できるようになります。
これにより、外部から seekTime を変更した際に、適切に再生開始位置が更新されるようになります。

まとめ

Jetpack Compose の WebViewClient 内の関数をoverrideするときは、rememberUpdatedState を使用する必要がある場合もある、ということを覚えておきたいですね。
もちろんそれ以外でもrememberUpdatedState が有用なケースはあると思うので気をつけていきたいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?