0

I'm having a difficult time trying how to figure out how to create a callback in Kotlin using lambdas. I have a custom TextInputEditText and I want to implement a function that the activity can call when text changes.

Here is my custom EditText:

class EditTextEx : TextInputEditText, TextWatcher {
    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)


    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
      // Call the callback onTextAvailable with the EditText's text (s.toString)
    }

    override fun afterTextChanged(p0: Editable?) {
    }

    override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
    }

}

In my activity I want to have a callback that gets called when the onTextChanged event gets called. The callback in the custom control sends only the text back to the client. So in my activity, I want something like this:

editText.onTextAvailable(text -> do something )

3 Answers 3

1

It's actually quite easy to do, look:

inline fun EditText.onTextChanged(crossinline onTextChange: (String) -> Unit): TextWatcher {
    val textWatcher = object: TextWatcher {
        override fun afterTextChanged(editable: Editable) {
            onTextChange(editable.toString())
        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
    }
    this.addTextChangeListener(textWatcher)
    return textWatcher
}

Now you can call

editText.onTextChanged { text -> /* do something */ }
Sign up to request clarification or add additional context in comments.

3 Comments

So callbacks are primarily done using extension functions? I came across this but couldn't figure it out: stackoverflow.com/a/48761541/11647945 Is that a different approach that accomplishes the same thing?
Not exactly, extension is just a way to provide new functionalities to a given class, callbacks are just interfaces that you can put it anywhere
You can wrap callback methods with your preferred signature using extension functions.
0

In addition to the solution by @EpicPandaForce, there are a couple other solutions. If you want to stick with using a class as you've shown in your example, then you can do this:

class EditTextEx : TextInputEditText, TextWatcher {
    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    private var mOnTextWatcherCallback: (m: String) -> Unit = {}

    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        if (mOnTextWatcherCallback != null)
            mOnTextWatcherCallback(s.toString())
    }

    override fun afterTextChanged(p0: Editable?) {
    }

    override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
    }

    fun onTextChange(callback: (text: String) -> Unit) {
        mOnTextWatcherCallback = callback
    }
}

Then in your activity create a function:

fun onTextChange(text: String) {
  // Do something with the text.
}

And then setup your callback as follows:

my_edittext.onTextChange(::onTextChange)

This solution allows you to re-use the same onTextChange function for other controls that want to use it as well.

If you prefer to use an interface to define the callback, do this:

class EditTextEx : TextInputEditText, TextWatcher {
    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    private var mOnTextWatcherCallback: ITextWatcher? = null

    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        if (mOnTextWatcherCallback != null)
            mOnTextWatcherCallback!!.onTextChanged(s.toString())
    }

    override fun afterTextChanged(p0: Editable?) {
    }

    override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
    }

    fun onTextChange(callback: ITextWatcher) {
        mOnTextWatcherCallback = callback
    }
}

Then in your activity, create the callback as follows:

val textChangeHandler = object: ITextWatcher {
    override fun onTextChanged(text: String) {
        var t = text
    }
}

And then setup your callback for your edittext controls as follows:

my_edittext.onTextChange(textChangeHandler)

Comments

0

Try something like this:

fun getEditTextChange(editText: EditText, onTextChange: (String) -> Unit){
        val tw = object: TextWatcher {
            private var region = Locale.getDefault().language

            override fun afterTextChanged(s: Editable?) {
                onTextChange.invoke(s.toString())
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}

        }
        editText.addTextChangedListener(tw)
    }

Hope it helps

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.