LoginSignup
12
5

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-06-09

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

12
5
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
12
5