El Marce
El Marce

Reputation: 3344

Proper way to add a custom listener to a Kotlin class

I'm a Kotlin noob (third day) and trying to add a listener to my class.

I'm currently using lambdas as in the following example and they work fine.

// Declare callback
class Controller {
   var onAction = { -> }
}

// Use callback
myController.onAction = {
   ...
}

However, I really like the way Android's Button::setOnClickListener is consumed in Kotlin code as follows (without having to use =):

myButton.setOnClickListener {
    awesomeObject.doSomething()
}

This makes me wonder:

How do I declare my listener so it can be used the Button::setOnClickListener way?

What is the most 'Kotlinic'(*) way? Mine or Button::setOnClickListener way?

(*) As in Pythonic :)

Upvotes: 4

Views: 7896

Answers (3)

El Marce
El Marce

Reputation: 3344

For all you TL;DR people, here is a full example based on @Tenfour04's answer on how to use it without needing the = sign (emulating SAM conversion):

// Declare callback using SAM's 

class Controller {

    // The listener
    private var onAction: () -> Unit = {}

    // This will allow you to use without the '=' 
    // (as in required in Sergeys answer)
    fun setOnAction(block: () -> Unit) { onAction = block }

    // A listener with params
    private var onActionWithParam: (String) -> Unit = {}

    // This will allow you to use without the '=' 
    // (as in required in Sergeys answer)
    fun setOnActionWithParam(block: (String) -> Unit) { onActionWithParam = block }

}

// use the listener without '='
controller.setOnAction {
  something.awesome()
}

controller.setOnActionWithParam { paramName ->
  something.awesomeRequireStr(paramName)
}

However, bear in mind that the most Kotlin-idiomatic may be actually @Sergey's answer.

Upvotes: 1

Tenfour04
Tenfour04

Reputation: 93639

fun setOnAction(block: () -> Unit) { onAction = block } and keep your onAction var.

But I suppose it's more Kotlin-idiomatic to just keep what you have. Properties are usually preferred over setter funs. The Android Button syntax is the result of SAM conversion of Java code. setOnClickListener ends up more concise than onClickListener = only because with the latter, the listener is a Java interface you have to name rather than a Kotlin function. It's really verbose to write:

button.onClickListener = OnClickListener {
    //...
}

so the SAM conversion is nicer to use.

Upvotes: 3

Sergio
Sergio

Reputation: 30655

You can use next syntax to add a custom listener:

class Controller {
    var onAction1: () -> Unit = {} // listener with default implementation without params and returns Unit

    var onAction2: (() -> Unit)? = null // nullable listener without params and returns Unit

    var onAction3: ((Int) -> String)? = null // listener with param of type Int and returns String object
}

Initializing listeners:

val controller = Controller()
controller.onAction1 = {
    // your action
}

controller.onAction2 = {
    // your action
}

controller.onAction3 = { intParam ->
    // your action
    "result" // return some String result
}

Calling listeners:

val controller = Controller()
controller.onAction1()
controller.onAction2?.invoke()
val result: String? = controller.onAction3?.invoke(20)

Upvotes: 4

Related Questions