Kotlin how to achieve android setOnClickListener like syntax

Viewed 251

In Android+Kotlin both of the following syntaxes are valid

view.setOnClickListener(this) and view.setOnClickListener {} I have been trying to achieve this syntax for my custom functional interface without any success. Here is what I have tried so far

interface OnClickListener {
    fun onClick(str: String)
}

class Test {
    fun setClickListener(listener: OnClickListener) {
        listener.onClick("")
    }
}

fun main() {
    val test = Test()
    test.setClickListener { str -> 

    }
}

in the above snippet code in main() fails to compile as it needs an anonymous object of OnClickListener class. How can I achieve the same syntax for client code as with the original OnClickListener?

3 Answers

This is not possible if you are using Kotlin only. You can achieve this by using @FunctionalInterface of JAVA. No matter how inconsistent it seems, its by design in language.

It seems that SAM conversion only works for Java interfaces. According to the forum discussion this is by design: Kotlin has functional types and everyone should just use them. While the argument is perfectly reasonable the restriction does feel a bit inconsistent.

Please check this link

What I like to do is:

inline fun View.onClick(crossinline clickListener: (View) -> Unit) {
    this.setOnClickListener {
        clickListener(this)
    }
}

Now you can call

button.onClick {
    // handle click
}

EDIT: ah, I misunderstood your question. The answer is

interface OnClickListener {
    fun onClick(target: Any)
}

class Test {
    private var onClickListener: OnClickListener? = null

    fun setOnClickListener(listener: OnClickListener?) {
        this.onClickListener = listener
    }

    fun doClick() {
        onClickListener?.onClick(this)
    }
}

inline fun Test.setOnClickListener(crossinline listener: (Test) -> Unit) {
    this.setOnClickListener(object: OnClickListener {
        override fun onClick(target: Any) {
            listener.invoke(target as Test)
        }
    })
}

fun main() {
    val test = Test()
    test.setOnClickListener { t -> 
        println("hello")
    }

    test.doClick()
}

Use

fun setClickListener(listener: (String) -> Unit)

and then call listener("some string")

Related