Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access data binding variable from a BindingAdapter

Say for instance I have the following variable in my data bound XML.

<layout ...>
    <data>
        <variable name="listener" type="com.xyz.Listener" />
        <!-- and other variables -->
    </data>
    ...
</layout>

I use this variable in every single one of my data-bound layouts, and I need to access it in almost every single one of my @BindingAdapter. For instance, my binding adapters mostly look like this.

@BindingAdapter("board")
fun setBoard(view: TextView, board: Board) {
    view.setText(board.name)
    view.setOnClickListener {
        listener.onBoardClicked(board)
    }
}

@BindingAdapter("topic")
fun setTopic(view: TextView, topic: Topic) {
    view.setText(topic.name)
    view.setOnClickListener {
        listener.onTopicClicked(topic)
    }
}

// and a few others like that

and I use them like this

<TextView ... app:board="@{board}" ... />
<TextView ... app:topic="@{topic}" ... />

What I need here is a way to access that listener variable declared in the data block to all of my binding adapters. Is there a way to do that without manually passing it as a second variable every single time?

// I know I can do this - looking for an alternative
@BindingAdapter({"board", "listener"})
fun setBoard(view: TextView, board: Board, listener: Listener) {
    view.setText(board.name)
    view.setOnClickListener {
        listener.onBoardClicked(board)
    }
}

I am using Kotlin here, but a solution in Java works just fine for me as well.

like image 981
Leo Aso Avatar asked Oct 29 '25 14:10

Leo Aso


1 Answers

After doing some more research, I've just discovered the DataBindingComponent interface and it solves precisely the problem I was having. Apparently, if you make your binding adapters instance methods rather than static, the compiler will take the class you declared it in, and add it as a property of DataBindingComponent. So I made my binding adapters instance methods, and injected the variable I wanted via the constructor.

class Binding(val listener: Listener) {

    @BindingAdapter("board")
    fun setBoard(view: TextView, board: Board) {
        view.setText(board.name)
        view.setOnClickListener {
            listener.onBoardClicked(board)
        }
    }

    @BindingAdapter("topic")
    fun setTopic(view: TextView, topic: Topic) {
        view.setText(topic.name)
        view.setOnClickListener {
            listener.onTopicClicked(topic)
        }
    }
}

After building, the compiler generated the following interface

package android.databinding;

public interface DataBindingComponent {
    com.my.package.Binding getBinding();
}

I then completed the cycle by making the binding class extend this interface and return itself

class Binding(val listener: Listener) : DataBindingComponent {
    override fun getBinding(): Binding {
        return this
    }
    // all the other stuff
}

This allows me to pass it as an argument when inflating views, and as such I no longer have to even declare listener as an XML variable. I can just declare the Binding instance

val bindingComponent = Binding(object : Listener {
    // implement listener methods here
})

and pass it when inflating the layout

// now I can use it in activities
DataBindingUtil.setContentView<MyActivityBinding>(
        this, R.layout.my_activity, bindingComponent)

// ...or in fragments
DataBindingUtil.inflate<MyFragmentBinding>(
        inflater, R.layout.my_fragment, parent, false)

// ...or any other place DataBindingUtil allows
like image 111
Leo Aso Avatar answered Oct 31 '25 05:10

Leo Aso



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!