Android Kotlin+Data Bindingでビルドエラーに陥った話

AndroidアプリでWebViewを使う際、URLをViewModelのurlと連動させるというデータバインディングをしたかった。

最低限の実装は以下の通り。


web_view_fragment.xml

<?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>



WebView.kt

@BindingAdapter("url")

fun WebView.bindUrl(url: String?) {
if (url != null) {
loadUrl(url)
}
}


WebViewViewModel.kt

class WebViewViewModel: ViewModel() {

val url = MutableLiveData<String>()
}


WebViewFragment.kt

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"
}
}



app/build.gradle

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の記述もしていた。

わからなくて小一時間悩みまくった。


原因

以下の記述が足りなかった。


app/build.gragle

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