https://kotlinlang.org logo
#android
Title
# android
j

Jonny

11/06/2019, 10:29 PM
How can I set a function to be used in a custom control? I can expose and bind to properties like strings, ints, etc, but how can I do the same for a function?
a

andrew

11/06/2019, 10:30 PM
Like a listener?
j

Jonny

11/06/2019, 10:30 PM
yes
a

andrew

11/06/2019, 10:30 PM
In you class, create a setter function where you can pass in a function
like this
Copy code
fun setListener(l: () -> Unit) {
    listenerVar = l;
}
then you can invoke listenerVar
I recommend setting up listenerVar like:
Copy code
private var listener: Unit -> () = {}
to avoid NPEs
j

Jonny

11/06/2019, 10:33 PM
How do I access that from my xml? So far I've been using attrs file to create properties
a

andrew

11/06/2019, 10:33 PM
Ohhh
I thought you meant an event listener
In java/kotlin
j

Jonny

11/06/2019, 10:34 PM
Well the listener is already in the button which is part of my control
but I must set it
from the control binding
a

andrew

11/06/2019, 10:35 PM
Are you using databinding?
j

Jonny

11/06/2019, 10:35 PM
yes
a

andrew

11/06/2019, 10:36 PM
Ah, not that familiar with it then
Refer here then
j

Jonny

11/06/2019, 10:39 PM
Perhaps the variable can be used
<data> <variable name="handlers" type="com.example.MyHandlers"/> <variable name="user" type="com.example.User"/> </data>
a

andrew

11/06/2019, 10:39 PM
yes, like that
j

Jonny

11/06/2019, 10:39 PM
Not sure if there's type for any function though
a

andrew

11/06/2019, 10:39 PM
Put the function in a class
j

Jonny

11/06/2019, 10:40 PM
well it's going to be in a viewmodel probably
and I was hoping to disconnect the control from viewmodel
to let it be used however I want. It's nicer code
At first it was just a view I included but I transformed it into a control and was able to expose some properties to bind to instead.
Now I just need to expose an onDeclineButtonClick property where I can set it to a function to be used
a

andrew

11/06/2019, 10:44 PM
I would personally keep it as a view
Or
have the XML and inflate it wherever it needs to go
but that’s me
I’m not as familiar with viewbinding
j

Jonny

11/06/2019, 10:46 PM
Well that worked, I did it from the start but I like this way more since you could put some behavior into the control itself, and it can be used anywhere.
It should be possible to expose the method, the Button class does it for example..
a

andrew

11/06/2019, 10:47 PM
I’m used to the oldschool way of doing things 😜
Idk how to help you with the viewbinding thing
j

Jonny

11/06/2019, 10:49 PM
Yeah I'm trying the new way with viewmodels and bindings. It's nice clean code once you get it working
a

andrew

11/06/2019, 10:49 PM
Can’t you pass in a viewmodel as a variable?
j

Jonny

11/06/2019, 10:51 PM
I did that when I just included the view
but I wanted to separate them
so I can have logic also in the control if I want
like animations etc
a

andrew

11/06/2019, 10:52 PM
You can add animation to a view 😜
j

Jonny

11/06/2019, 10:52 PM
ah
a

andrew

11/06/2019, 10:52 PM
Just inflate a layout into your view
j

Jonny

11/06/2019, 10:52 PM
well it can be separated from the viewmodel at least. What if I also want to use it in another view?
a

andrew

11/06/2019, 10:53 PM
and you can reference the view IDs and animate them, etc
j

Jonny

11/06/2019, 10:53 PM
I don't, but still. It's better to separate if possible
I wonder how the button class does it
a

andrew

11/06/2019, 10:53 PM
I guess I’d have to see the use case, dunno why you’d want to separate it
You mean like how button has onclick in XML?
j

Jonny

11/06/2019, 10:54 PM
well I have a viewmodel with currentPrice and description etc
It's not the controls viewmodel, the control just happens to in this case be inside the view that uses that viewmodel
but what if I wanted to put it in another view?
and yes
like it has onClick in XML
a

andrew

11/06/2019, 10:55 PM
They use reflection
So you pass in the member name, it has to be inside the same class
and It’ll call the method
It’s actually in View.java
j

Jonny

11/06/2019, 10:57 PM
the class that inflates the view?
a

andrew

11/06/2019, 10:58 PM
The onClick XML property code is held in the View class itself
Screen Shot 2019-11-06 at 5.58.39 PM.png
DeclaredOnClickListener will use reflection based on the current activity
or context
That’s the problem there with passing it in XML
you’d have to have the class
member
etc
Then you’d use reflection which is a mess
j

Jonny

11/06/2019, 11:02 PM
hm
They seem to pass handlerName as a string
The question is where that handler is declared
Because I could certainly declare a handlerName too
Maybe I should just pass the viewmodel for the parent view. Was hoping for a nicer solution where the control is independent of the parent
a

Anastasia Finogenova

11/06/2019, 11:19 PM
You can create an interface with the onclick implementation in your activity/fragment, then set it as a variable to the binding layout. Then call the methods from that interface directly from xml like so android:onClick="@{() -> interface.onButtonClicked()}"
And you will have a <variable name="interface"> with type MyInterface (for example) in your xml Disclaimer variable creation is psudo code
a

andrew

11/07/2019, 12:17 AM
I think you are tightly coupling more things together than you need
a

Anastasia Finogenova

11/07/2019, 12:44 AM
Xml and activity are parts of your "view" as they are , so how can you decouple them?
And the part of databinding when you are bringing expressions into xml (binding expressions) are also coupling and adding logic to xml, you can argue that it is not right but if you look at those expressions (most of them are conditional) that's what they are...logic
j

Jonny

11/07/2019, 6:57 PM
I ended up adding an interface class for the function
Copy code
class FindJobsViewModel : BaseViewModel() {
    var onDeclineButtonClickedCommand = object: ICommand {
        override fun execute() {
            nextCard()
        }
    }
in attrs file I add <attr name="onDeclineButtonClickedCommand" />
and in SellerJobCard class:
Copy code
fun setOnDeclineButtonClickedCommand(command: Command) {
        onDeclineButtonClickedCommand = command
    }
Copy code
init {
        button_decline.setOnClickListener {
            onDeclineButtonClickedCommand?.execute()
        }
    }
and then I bind to that in the xml