Required DataBindingComponent is null in class AgentDetailsActivityBindingImpl
Asked Answered
V

8

23

in my agent_details_activity.xml:

 <ImageView
            android:id="@+id/imageViewPhoto"
            android:layout_width="0dp"
            android:layout_height="400dp"
            app:agentImageUrl="@{item.url}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/toolBarContainer" />

in activity:

class AgentDetailsActivity : AppCompatActivity() {
    private lateinit var binding: AgentDetailsActivityBinding

    companion object {
        val AGENT_DETAILS = AgentDetailsActivity::class.java.name + "_AGENT_DETAILS"
    }

    private val TAG = AgentDetailsActivity::class.java.name

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = AgentDetailsActivityBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.setHandler(this);
    }

  @BindingAdapter("agentImageUrl")
    fun loadImage(view: ImageView, imageUrl: String) {
        Glide.with(view.context)
            .load(imageUrl)
            .apply(RequestOptions().error(R.drawable.no_image))
            .into(view)
    }

but in runtime I get error:

Shutting down VM
 FATAL EXCEPTION: main
 Process: com.android.testproject.android.kotlin.debug, PID: 24924
 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.testproject.android.kotlin.debug/com.android.testproject.android.kotlin.coroutine_retrofit.ui.activity.AgentDetailsActivity}: java.lang.IllegalStateException: Required DataBindingComponent is null in class AgentDetailsActivityBindingImpl. A BindingAdapter in com.android.testproject.android.kotlin.coroutine_retrofit.ui.activity.AgentDetailsActivity is not static and requires an object to use, retrieved from the DataBindingComponent. If you don't use an inflation method taking a DataBindingComponent, use DataBindingUtil.setDefaultComponent or make all BindingAdapter methods static.
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
    at android.app.ActivityThread.-wrap11(ActivityThread.java)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
 Caused by: java.lang.IllegalStateException: Required DataBindingComponent is null in class AgentDetailsActivityBindingImpl. A BindingAdapter in com.android.testproject.android.kotlin.coroutine_retrofit.ui.activity.AgentDetailsActivity is not static and requires an object to use, retrieved from the DataBindingComponent. If you don't use an inflation method taking a DataBindingComponent, use DataBindingUtil.setDefaultComponent or make all BindingAdapter methods static.
    at androidx.databinding.ViewDataBinding.ensureBindingComponentIsNotNull(ViewDataBinding.java:666)
    at com.android.testproject.android.kotlin.databinding.AgentDetailsActivityBindingImpl.<init>(AgentDetailsActivityBindingImpl.java:40)
    at com.android.testproject.android.kotlin.databinding.AgentDetailsActivityBindingImpl.<init>(AgentDetailsActivityBindingImpl.java:32)
    at com.android.testproject.android.kotlin.DataBinderMapperImpl.getDataBinder(DataBinderMapperImpl.java:52)
    at androidx.databinding.MergedDataBinderMapper.getDataBinder(MergedDataBinderMapper.java:74)
    at androidx.databinding.DataBindingUtil.bind(DataBindingUtil.java:199)
    at androidx.databinding.DataBindingUtil.inflate(DataBindingUtil.java:130)
    at androidx.databinding.ViewDataBinding.inflateInternal(ViewDataBinding.java:1366)
    at com.android.testproject.android.kotlin.databinding.AgentDetailsActivityBinding.inflate(AgentDetailsActivityBinding.java:95)
    at com.android.testproject.android.kotlin.databinding.AgentDetailsActivityBinding.inflate(AgentDetailsActivityBinding.java:81)
    at com.android.testproject.android.kotlin.coroutine_retrofit.ui.activity.AgentDetailsActivity.onCreate(AgentDetailsActivity.kt:37)
    at android.app.Activity.performCreate(Activity.java:6251)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369)
Volkslied answered 22/9, 2019 at 12:24 Comment(2)
bind adapter method must be staticFrias
I wrote a detailed answer below to understand the main reason of this error also provided the possible best solutions thereKubiak
I
31

In my case I found a really simple method, I had the same error as you. I just had to add @JvmStatic annotation.

The code will look something like this:

companion object {

     @JvmStatic // add this line !!
     @BindingAdapter("agentImageUrl")
        fun loadImage(view: ImageView, imageUrl: String) {
            Glide.with(view.context)
                .load(imageUrl)
                .apply(RequestOptions().error(R.drawable.no_image))
                .into(view)
        }
}
Incorporated answered 12/11, 2019 at 12:53 Comment(2)
Placing the @BindingAdapter method outside any class also works and this one also worked, but this one is more understandable and kotliny solution. ThanksCuirbouilli
This is also working fine for me.Flosser
I
30

To elaborate on @mahdi shahbazi answer (The bind adapter method must be static):

In kotlin, place your binding method outside of any class (so in your case oustide of the Activity class). This will do the trick. In your case, it will look like:

@BindingAdapter("agentImageUrl")
fun loadImage(view: ImageView, imageUrl: String) {
    Glide.with(view.context)
        .load(imageUrl)
        .apply(RequestOptions().error(R.drawable.no_image))
        .into(view)
}

class AgentDetailsActivity : AppCompatActivity() {
    private lateinit var binding: AgentDetailsActivityBinding

    companion object {
        val AGENT_DETAILS = AgentDetailsActivity::class.java.name + "_AGENT_DETAILS"
    }

    private val TAG = AgentDetailsActivity::class.java.name

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = AgentDetailsActivityBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.setHandler(this);
    }

    ...

}

I have been able to come up with this answer thanks to this article: https://proandroiddev.com/custom-attributes-using-bindingadapters-in-kotlin-971ef8fcc259. There are lots of other good tips in it, I recommend looking into it for yummies about view binding.

Imperialism answered 1/11, 2019 at 13:2 Comment(2)
if as in my case the binding adapter is supposed to be in the ViewModel, it works to put it outside the class clause too.Kilmer
Solution worked for me is - place your binding method outside of any classThud
C
9

Yes, first of all place your binding method outside of any class. Second, make imageUrl nullable! Because if the imageUrl becomes null anytime, you will get crash before onError() is invoked.

@BindingAdapter("agentImageUrl")
fun loadImage(view: ImageView, imageUrl: String?) {
    Glide.with(view.context)
        .load(imageUrl)
        .apply(RequestOptions().error(R.drawable.no_image))
        .into(view)
}
Concision answered 11/4, 2020 at 22:7 Comment(0)
P
6
@BindingAdapter("android:loadImage")
fun loadImage(userImageView: ImageView, imageUrl: String): Unit {
    Glide
        .with(userImageView)
        .load(imageUrl)
        .into(userImageView)
}

This is my solution, I have written my function outside the class

Pforzheim answered 3/8, 2020 at 16:2 Comment(0)
C
3

for me nesting method inside companion object{} and adding @JvmStatic worked.

companion object{
            @JvmStatic
            @BindingAdapter("avatarBinder")
            fun loadAvatar(view: ImageView, avatar: String?):Unit{
                    Glide.with(view.context)
                            .load(avatar).into(view);
            }
    }
Cuirbouilli answered 28/4, 2021 at 7:10 Comment(0)
S
2
  1. add this annotation @JvmStatic

  2. make all variables nullable

    imageUrl:String will be imageUrl:String?

solution will be

@JvmStatic
@BindingAdapter("agentImageUrl")
fun loadImage(view: ImageView, imageUrl: String?) {
    Glide.with(view.context)
        .load(imageUrl)
        .apply(RequestOptions().error(R.drawable.no_image))
        .into(view)
}
Subtilize answered 9/9, 2021 at 16:57 Comment(1)
In that case your @BindingAdapter declaration must be in a static field like you should declare them inside companion objectKubiak
F
0

I came across this bug , when I mistakenly added this binderAdapters inside a class.

Flatwise answered 14/2, 2023 at 2:52 Comment(0)
K
0

There are different ways of declaring all @BindingAdapters in a single app. Either they should be declared in any Static field or in any Global Scope like outside of Activity or Fragment file or 😊 you can gather all @BindingAdapters in a single file and make that file DefaultComponent for Data Binding. The last approach is the more architectural way of declaring all @BindingAdapters in a single app.

Let's discuss both approaches here:

Simple way: Create a file anywhere in the project and declare all @BindingAdapters in a single companion object inside that file like following:

companion object {
    @JvmStatic
    @BindingAdapter("agentImageUrl")
    fun loadImage(view: ImageView, imageUrl: String?) {
        Glide.with(view.context)
            .load(imageUrl)
            .apply(RequestOptions().error(R.drawable.no_image))
            .into(view)
    }
}

Do not forget to declare all the @BindingAdapters as @JvmStatic

You can also declare your @BindingAdapters outside of your Activity or Fragment which is a Global Scope, that will also solve the error but there might be a risk that you may write same @BindingAdapter in multiple places in a single project.

Architectural way: This is the best approach to declare all @BindingAdapters in a single project.

  1. First create a file and declare a class not companion object in this case and declare all @BindingAdapters inside this class like following:

    class BindingAdapters {
        @BindingAdapter(value = ["imageUrl", "imageRequestListener"], requireAll = false)
        fun bindImage(imageView: ImageView, url: String?, listener: RequestListener<Drawable?>?) {
            Glide.with(imageView.context)
                .load(url)
                .listener(listener)
                .into(imageView)
        }
    }
    

Do not use @JvmStatic in this case

Create another class and extend DataBindingComponent class so that the compiler looks for binding components declaration in this file not elsewhere.

class AppDataBindingComponent : DataBindingComponent {
    private val adapter = BindingAdapters()
    override fun getBindingAdapters() = adapter
}

Finally add this following line inside the onCreate() function of your Application class

DataBindingUtil.setDefaultComponent(AppDataBindingComponent())

Like below:

class MainApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        DataBindingUtil.setDefaultComponent(AppDataBindingComponent())
    }
}

That's it 😊 now you can follow any of the above approach which seems easier to you.

N.B: Do not forget to include your Application file inside your AndroidManifest.xml file like following:

<application
    android:name=".app.MainApplication"
    android:allowBackup="true"/>

Sorry for the long discussion but hope you will learn a lot from this discussion. Thank you

Kubiak answered 8/5, 2023 at 10:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.