はじめに
現在(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を実装するというやり方が今は無難だと思われます。