manwhotrycoding
manwhotrycoding

Reputation: 961

How I can use callback in Kotlin?

I have View and one CircleShape , which should show toast in this View. And I use it in main Activity. This is my interface

interface OnClickListenerInterface {
  fun onClick()
}

It is CircleShape( it is View in my xml) and listener in my View. I want to implement OnClick in my Activity.

 var listener: OnClickListenerInterface? = null

 mCircleShape.setOnClickListener(View.OnClickListener {
      if (listener == null) return@OnClickListener
      listener!!.onClick()
    })

I know , that in Kotlin getters and setters generic automatics, but how I can set listener if it private. It is code from my Activity, but It doesn't work

CircleShape.listener  = object :OnClickListenerInterface{
      override fun onClick() {
        ToastUtils.showSuccessMessage(getContext(),"pressed")
      }
    }

How I should to use Callback, onClickListenere in Kotlin?

Upvotes: 88

Views: 148909

Answers (8)

Binh Ho
Binh Ho

Reputation: 4936

Tested. This worked.

// Define interface
interface MyCallback {
    fun success(param: String)
    fun fail(param: String)
}

fun functionHasCallback(param: Boolean, callback: MyCallback) {
    // do somthing
    if (ok) {
        callback.success("value")
    } else {
        callback.fail("value")
    }
}

// Call function
functionHasCallback(
    boolParam, 
    object: MyCallback {
        override fun success(param: String) {
            // Your code here
        }

        override fun fail(param: String) {
            // Your code here
        }
    }
)

Upvotes: 1

Weidian Huang
Weidian Huang

Reputation: 2935

A more simpler solution by using lambda.

Inside CircleShape.kt, declare a lambda function.

var listener: (()->Unit)? = null
...
// When you want to invoke the listener
listener?.invoke()

Inside your Activity

mCircleShape.listener = {
    // Do something when you observed a call
}

Upvotes: 106

glagarto
glagarto

Reputation: 923

On CircleShape.kt.

private listener OnClickListenerInterface? = null
...
fun setOnClickListener(listener: OnClickListenerInterface) {
    this.listener = listener
}

On your Activity

mCircleShape.setOnClickListener(object: CircleShape.OnClickListenerInterface {
    override fun onClick() {
      // Do something here
    }
})

If you're gonna use lambda expression, you can use a Function Type. Here how it looks like on CirclesShapt.kt

fun setOnClickListener(listener: () -> Unit){
   listener() // or you could use optional if the lister is nullable "listener?.invoke()"
}

So in activity looks like.

mCircleShape.setOnClickListener {
  // Do something here
}

Upvotes: 41

Makari Kevin
Makari Kevin

Reputation: 131

I was desperately looking for a Kotlin solution similar to what Java interfaces offer until I bumped into the suggestions offered thereabove. Having tried all of them, I was unable to arrive at a working solution that would suit my scenario.

This led to my own implementation which worked perfectly according to my use case, and therefore thought I could share the same here, though it may not be the perfect way of doing it, apologies in advance.

The steps:

  • 1. Define your interface:
interface OnClickListenerInterface {
    fun onClick()
}
  • 2. Inside the class that will trigger the "onClick" callback i.e. "CircleShape" for your case:

Create a nullable variable.

var listener: OnClickListenerInterface? = null

Declare a function to initialise the variable above.

fun initOnClickInterface(listener: OnClickListenerInterface){
        this.listener = listener
    }

At the point where you want to trigger the "onClick" callback:

mCircleShape.setOnClickListener(View.OnClickListener {
      if (listener == null) return@OnClickListener
      listener?.onClick() // Trigger the call back
    })
  • 3. Inside the activity where you want to receive the "onClick" callback:

Make the activity implement the OnClickListenerInterface, then create an object of your CircleShape class.

class Activity : AppCompatActivity(), OnClickListenerInterface {
    val mCircleShape = CircleShape()
    // ...other stuff

Inside the onCreate function of this activity, initialise your interface using the initOnClickInterface function we created in the CircleShape class.

mCircleShape.initOnClickListenerInterface(this)

Then finish by overriding the onClick method of our interface by adding the code below in the activity.

override fun onClick() {
        // Callback received successfully. Do your stuff here
    }

The above steps worked for me.

As I said, in case of any issues with my coding, I'm a learner too 😎.

Cheers!

Upvotes: 5

Dan Alboteanu
Dan Alboteanu

Reputation: 10212

define a function like this:

  fun performWork(param1: String, myCallback: (result: String?) -> Unit) {
    // perform some network work

    // on network finished
    myCallback.invoke("result from network")
  }

use like this:

  performWork("http://..."){ result ->
  //use result
  }

Upvotes: 71

Gabriel Janucik
Gabriel Janucik

Reputation: 55

Have you tried to use a lambda expression? For example in your case:

mCircleShape.setOnClickListener( 
    { _ -> ToastUtils.showSuccessMessage(context,"pressed") }
)

Or if you want to make it more kotlin style:

mCircleShape.listener = ( 
    { _ -> ToastUtils.showSuccessMessage(context,"pressed") }
)

Upvotes: 1

Rishabh Shukla
Rishabh Shukla

Reputation: 501

to use Kotlin callbacks , I use them in my api calls for success or failure use

create enum class for state

enum class APIState(val result: Boolean) {
SUCCESS(true),
FAILURE(false)}

use call back in fun

 private fun fetchFeesList(studentID:String,call:(APIState)->Unit){
 ... do stuff here , and use call(APIState.SUCCESS) or call(APIState.FAILURE) }

when calling function fetchFeesList , call it like

fetchFeesList(studentID){
        val res = it.result
        if(res){
            toast("success")
        }else {
            toast("failure")
        }
    }

for toast("message") , use Anko Lib from GitHub : - https://github.com/Kotlin/anko

Upvotes: 9

Yev Kanivets
Yev Kanivets

Reputation: 1800

First of all you need to remove this code:

mCircleShape.setOnClickListener(View.OnClickListener {
      if (listener == null) return@OnClickListener
      listener!!.onClick()
    })

Because listener is always null at first and your code just always returns.

var listener: OnClickListenerInterface? = null is already public (this is a default access level in Kotlin). So you can just set it in your activity, when needed. Use listener?.onClick() call to trigger it from your CircleShape.

Upvotes: 0

Related Questions