LoginSignup
3
2
Android強化月間 - Androidアプリ開発の知見を共有しよう -

【Jetpack Compose】WebViewにpull-to-refreshを実装しようとしたらハマったこと

Last updated at Posted at 2023-10-10

はじめに

現在(2023/10/10)Jetpack Composeでは、WebViewを使うにはAndroidViewで囲む必要があります。対して、pull-to-refreshはJetpackComposeに実装されているPullRefreshと以前のViewのSwipeRefreshLayoutがあります。

結論、SwipeRefreshLayoutで解決しました。

WebView

Jetpack Composeでは、WebViewを使用するためにはAndroidViewというラッパーを用いてその中にWebViewを配置する必要があります。AndroidViewはJetpack Composeのコンポーザブルから従来のViewシステムを使うための架け橋的存在です。
以下にコード例を示します。

@Composable
fun WebViewContainer() {
    AndroidView(factory = ::WebView) { webView ->
        // WebViewの設定をここで行う
        webView.loadUrl("https://google.com")
    }
}

WebViewContainerというコンポーザブル関数の中でAndroidViewを設定しています。factoryの引数でWebViewを生成しています。

pull-to-refreshの選択肢

pull-to-refreshはViewのSwipeRefreshLayoutとJetpack ComposeはexperimentalですがPullRefreshが実装されています。

[View] SwipeRefreshLayout

@Composable
fun WebViewWithSwipeRefresh() {
    val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = false)
    AndroidView(factory = {
        SwipeRefreshLayout(it).apply {
            setOnRefreshListener { /* 処理を記述 */ }
            addView(/* Viewを追加 */)
        }
    }) { swipeRefreshLayout ->
        swipeRefreshLayout.isRefreshing = swipeRefreshState.isRefreshing
    }
}

[JetpackCompose] PullRefresh

val refreshScope = rememberCoroutineScope()
var refreshing by remember { mutableStateOf(false) }
var itemCount by remember { mutableStateOf(15) }

fun refresh() = refreshScope.launch {
    refreshing = true
    delay(1500)
    itemCount += 5
    refreshing = false
}

val state = rememberPullRefreshState(refreshing, ::refresh)

Box(Modifier.pullRefresh(state)) {
    LazyColumn(Modifier.fillMaxSize()) {
        if (!refreshing) {
            items(itemCount) {
                ListItem { Text(text = "Item ${itemCount - it}") }
            }
        }
    }

    PullRefreshIndicator(refreshing, state, Modifier.align(Alignment.TopCenter))
}

起きた問題

なるべくJetpack Composeを使おうと機運が高まっていたのでPullRefreshとWebView(AndroidView)の組み合わせで実装したところ、うまく機能しませんでした。
原因としては、Jetpack ComposeとAndroidViewを横断してスクロール情報が伝達しなかったようです。

上手く行った解決策

WebViewとpull-to-refresh機能を組み合わせる場合はどちらもAndroidView内で実装することで上手くいきました。
SwipeRefreshLayoutとWebViewを同一のAndroidView内に配置することで、SwipeRefreshLayoutの持つスクロールの挙動とWebViewが連携できました。

@Composable
fun WebViewWithPullRefresh(
    webView: WebView
) {
    AndroidView(
        factory = {
            SwipeRefreshLayout(it).apply {
                setOnRefreshListener {
                    webView.reload()
                }
                addView(webView)
            }
        },
        update = { swipeRefreshLayout ->
            swipeRefreshLayout.isRefreshing = /* Refresh状態の制御 */
        }
    )
}

これでWebViewのスクロールがSwipeRefreshLayoutと同期して動作してくれました。
WebViewの読み込み状態については工夫して取得しないといけないので、これについてはまたいつか記事にしたいなと思っています。

結論

結局、今はJetpack Composeに移行している期間なので、Jetpack ComposeとViewを横断すると失敗するということでした。その時は、AndroidViewを使って、Viewを実装するというやり方が今は無難だと思われます。

3
2
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
3
2