5

I want to start using viewBinding in our project but the mere addition of the configuration results in a compile error:

android {
    buildFeatures {
        dataBinding true
        viewBinding true // new line and only change
    }

results in:

e: /home/leo/StudioProjects/android-wallet/mbw/build/generated/source/kapt/btctestnetDebug/com/mycelium/wallet/DataBinderMapperImpl.java:37: error: cannot find symbol
import com.mycelium.wallet.databinding.FragmentBequantAccountBindingImpl;
                                      ^
  symbol:   class FragmentBequantAccountBindingImpl
  location: package com.mycelium.wallet.databinding




Cannot find a setter for <com.mycelium.wallet.databinding.ItemBequantSearchBinding app:visibility> that accepts parameter type 'int'

If a binding adapter provides the setter, check that the adapter is annotated correctly and that the parameter type matches.

The offending code is:

    <data>

        <import type="android.view.View" />

        <variable
            name="viewModel"
            type="com.mycelium.bequant.market.viewmodel.AccountViewModel" />
    </data>
...
<include
    android:id="@+id/searchBar"
    layout="@layout/item_bequant_search"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:visibility="@{viewModel.searchMode ? View.VISIBLE : View.GONE}" removing="this line fixes compilation"
    app:layout_constraintTop_toBottomOf="@id/hideZeroBalance" />

Changing the offending line to any of

android:visibility="@{viewModel.searchMode ? `visible` : `gone`}"
app:visibility="@{viewModel.searchMode ? View.VISIBLE : View.GONE}"

results in similar errors.

I read I might have to define a BindingAdapter but why and where?

I tried adding


    @BindingAdapter("visibility")
    fun setVisibility(target: View, visible: Boolean) {
        target.visibility = if (visible) View.VISIBLE else View.GONE
    }

to AccountFragment which inflates above xml file changing the xml to

android:visibility="@{viewModel.searchMode}"

but this appears to have no effect.

Both fragment_bequant_account.xml and item_bequant_search.xml use androidx.constraintlayout.widget.ConstraintLayout instead of androidx.constraintlayout.ConstraintLayout.

I tried to put a @BindingAdapter into the AccountViewModel as suggested here but with no success.

Giszmo
  • 1,944
  • 2
  • 20
  • 47
  • Was your data binding working before or is that something you added too? – Sarah Khan Jan 28 '21 at 05:21
  • Data binding is working since many commits. Hundreds. The error comes after adding the line marked with `// new line and only change`. – Giszmo Jan 28 '21 at 05:52
  • 1
    Databinding already includes ViewBinding. No need to add that line. https://developer.android.com/topic/libraries/view-binding#data-binding – Alpha 1 Jan 28 '21 at 05:59
  • 1
    In the linked document I don't see any claims about one including the other but I see `tools:viewBindingIgnore="true"` which added to the one problematic layout fixes my issue, so thank you for making me read that document!!! I'm happy but still curious why this one `visibility` is not like the others. – Giszmo Jan 28 '21 at 06:14

4 Answers4

3

I had the same problem in my project. I used databinding in my code and had dataBinding true in the gradle. As soon as I added viewBinding true I got the same error pointing to the xml line android:visibility="@{viewModel.searchMode ? View.VISIBLE : View.GONE}"

To fix, I added the tools:viewBindingIgnore="true" attribute to the root view of a certain layout file so that layout is ignored while generating binding classes.

You can see documentation on the tools:viewBindingIgnore="true" attribute at https://developer.android.com/topic/libraries/view-binding#data-binding

James
  • 4,573
  • 29
  • 32
  • 1
    Looks like [that's the hack we used, too](https://github.com/mycelium-com/wallet-android/commit/86f5f4d) back then. Still a hack :( – Giszmo Jun 11 '21 at 01:17
3

The problem is in the viewBinding trying to create the binding class of the layout in the include. It seems that the binding class created for the main layout(dataBinding) manages the included layout in a different way when viewBinding = true and don't understand it's attrs

As James said tools:viewBindingIgnore="true" is the solution, in this case it must be in the included layout(layout="@layout/item_bequant_search").

Every reused layout must have tools:viewBindingIgnore="true" to avoid this issues

1

I got similar error and this is my solution: You only add tag '<layout.../layout>' to all include layout like this: In main layout:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<data>

    <import type="android.view.View" />

    <variable
        name="viewId"
        type="Integer" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/bg">

    <include
        android:id="@+id/img_no_data"
        layout="@layout/layout_no_data"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:visibility="@{viewId==0? View.VISIBLE: View.GONE}"
        app:layout_constraintBottom_toTopOf="@+id/btn_camera"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

In include layout: add tag '<layout...' too:

<layout>
<androidx.constraintlayout.widget.ConstraintLayout 
xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

   <ImageView
        android:id="@+id/imageView2"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="@dimen/_16sdp"
        android:layout_marginEnd="@dimen/_16sdp"
        android:src="@drawable/bg_no_data"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="986:817"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

          
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Done! Hope this help you.

0

The problem is with this statement

app:visibility="@{viewModel.searchMode ? View.VISIBLE : View.GONE}"

it evaluates and pass View.VISIBLE or View.GONE to the binding adapter method,But

@BindingAdapter("visibility")
    fun setVisibility(target: View, visible: Boolean)

As your method signature says it expects a boolen but evaluation results in int i.e. either View.VISIBLE or View.GONE.

The issue can be solved by removing the evaluation and passing the boolean directly.

app:visibility="@{viewModel.searchMode}"

I assument viewModel.searchMode is a boolean variable.

Lets you create a kotlin file Named BindingAdapters.kt

Paste this method directly there

@BindingAdapter("visibility")
fun setVisibility(target: View, visible: Boolean) {
    target.visibility = if (visible) View.VISIBLE else View.GONE
}

else lets say you have a class BindingAdapters in a file BindingAdapters.kt

class BindingAdapters{
     
  companion object{
    
    @BindingAdapter("visibility")
    @JvmStatic// it is important
    fun setVisibility(target: View, visible: Boolean) {
        target.visibility = if (visible) View.VISIBLE else View.GONE
    }

  }

}
rahat
  • 1,840
  • 11
  • 24
  • I do not have a BindingAdapter for this property. The code you mention was an attempt at introducing one and yes, I changed my xml accordingly to set Boolean but that's in the list of failed attempts and I don't understand why in this and only this case I suddenly would need a BindingAdapter. It should, like in all the other cases, just set the standard visibility property like before enabling view binding. – Giszmo Feb 01 '21 at 15:16
  • @Giszmo can you elaborate a little on what changes you have done and still did not work – rahat Feb 01 '21 at 15:21
  • If you could just let me know that your current is the same as the code in the question or there is any change in it? – rahat Feb 01 '21 at 15:26
  • As mentioned on the comments I'm good with a little hack but still curious what was the issue. We have a big code base with hundreds of layouts, dozens of which were already using data binding. `android:visibility="@{ viewModel... ? View.VISIBLE : View.GONE}"` can be found 39 times in the project. Only one of those has above issue. – Giszmo Feb 01 '21 at 15:28
  • have you called `binding.executePendingBindings()`? for this binding? – rahat Feb 01 '21 at 15:35
  • there is no `executePendingBindings` in the project. – Giszmo Feb 01 '21 at 16:36
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/228124/discussion-between-giszmo-and-rahat). – Giszmo Feb 01 '21 at 19:56