AndroidアプリでWebViewを使う際、URLをViewModelのurlと連動させるというデータバインディングをしたかった。
最低限の実装は以下の通り。
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.vitantonio.nagauzzi.unusedappfinder.viewmodel.WebViewViewModel"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".view.WebViewFragment">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:url="@{viewModel.url}"/>
</LinearLayout>
</layout>
@BindingAdapter("url")
fun WebView.bindUrl(url: String?) {
if (url != null) {
loadUrl(url)
}
}
class WebViewViewModel: ViewModel() {
val url = MutableLiveData<String>()
}
class WebViewFragment : Fragment(), KodeinAware {
override val kodein by closestKodein()
private val viewModel by lazy {
ViewModelProviders.of(this, direct.instance()).get(WebViewViewModel::class.java)
}
private lateinit var binding: WebViewFragmentBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = WebViewFragmentBinding.inflate(inflater, container, false).also {
it.lifecycleOwner = this
}
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
binding.viewModel = viewModel
// URLを直接指定(汎用化する際はthis.argumentsで外部から指定できる)
viewModel.url.value = "file:///android_asset/licenses.html"
}
}
android {
...
dataBinding {
enabled = true
}
}
これでいけるだろうと思ったら、ビルドエラーが発生。
Found data binding errors.
****/ data binding error ****msg:Cannot find the setter for attribute 'app:url' with parameter type androidx.lifecycle.MutableLiveData<java.lang.String> on android.webkit.WebView. file:/Users/nagauchi/Project/UnusedAppFinder/app/src/main/res/layout/web_view_fragment.xml loc:22:27 - 22:39 ****\ data binding error ****
とりあえずググってStackOverflowに辿り着いた。
https://stackoverflow.com/questions/44301545/2-way-databinding-for-webview-and-progressbar
けど、@BindingAdapterの記述は間違っていないし、build.gradleにdataBindingの記述もしていた。
わからなくて小一時間悩みまくった。
原因
以下の記述が足りなかった。
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
足りなかったのはapply plugin: 'kotlin-kapt'
の部分。
apply pluginする順番も重要らしい。
とりあえずこれでビルドが通るようになった。
会社のチームプロジェクトではいつの間にかapply plugin: 'kotlin-kapt'
が付いてたが、
個人環境だとすっかり忘れており、Javaで実装されたData Bindingのソースコードを見よう見まねで進めていた。
わかったこと
KotlinでData Binding、というかアノテーションを用いたコード生成全般を扱うには、
Kaptというコンパイラープラグインが必要。
https://kotlinlang.org/docs/reference/kapt.html