Use Done button on Keyboard in Databinding

Viewed 7896

I am trying to use the done button of the soft keyboard to activate a method via databinding. Just like onClick. Is there a way to do that?

example:

<EditText               
    android:id="@+id/preSignUpPg2EnterPhone"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"       
    onOkInSoftKeyboard="@{(v) -> viewModel.someMethod()}"
    />

onOkInSoftKeyboard doesn't exists... Is there something to create this behavior?

Thanks!

6 Answers

Using Kotlin, kapt produces:

e: [kapt] An exception occurred: android.databinding.tool.util.LoggedErrorException: Found data binding errors.
****/ data binding error ****msg:Listener class kotlin.jvm.functions.Function1 with method invoke did not match signature of any method viewModel::signIn

(because viewModel::signIn is of type KFunction1) so we can't use a method reference. However, if we create a variable within the viewModel that is explicit about the type, then we can pass that variable as the binding's param. (or just use a class)

Bindings.kt:

@BindingAdapter("onEditorEnterAction")
fun EditText.onEditorEnterAction(f: Function1<String, Unit>?) {

    if (f == null) setOnEditorActionListener(null)
    else setOnEditorActionListener { v, actionId, event ->

        val imeAction = when (actionId) {
            EditorInfo.IME_ACTION_DONE,
            EditorInfo.IME_ACTION_SEND,
            EditorInfo.IME_ACTION_GO -> true
            else -> false
        }

        val keydownEvent = event?.keyCode == KeyEvent.KEYCODE_ENTER 
            && event.action == KeyEvent.ACTION_DOWN

        if (imeAction or keydownEvent)
            true.also { f(v.editableText.toString()) }
        else false
    }
}

MyViewModel.kt:

fun signIn(password: String) {
    Toast.makeText(context, password, Toast.LENGTH_SHORT).show()
}

val signIn: Function1<String, Unit> = this::signIn

layout.xml:

<EditText
    android:id="@+id/password"
    app:onEditorEnterAction="@{viewModel.signIn}"
    android:imeOptions="actionDone|actionSend|actionGo"
    android:singleLine="true"/>

Kotlin, without writing custom binding adapter

In Layout,

<EditText
    ...
    android:onEditorAction="@{(view, actionId, event) -> viewModel.onDoneClicked(view, actionId, event)}" />

ViewModel

fun onDoneClicked(view: View, actionId: Int, event: KeyEvent?): Boolean {
    if(actionId == EditorInfo.IME_ACTION_DONE) {
        // handle here
        return true
    }
    return false
}

Note: event can be null, so make KeyEvent nullable by putting ? there.

You can directly call login method what inside ViewModel by implementing setOnEditorActionListener to the Edittext, taking reference from binging class

loginFragmentBinding.etPassword.setOnEditorActionListener(TextView.OnEditorActionListener { _, actionId, _ ->
        if (actionId == EditorInfo.IME_ACTION_DONE) {
            loginViewModel.login()
            return@OnEditorActionListener true
        }
        false
    })

Android framework already have this implemented. Take a look at TextViewBindingAdapter

You'll see those attributes, the documentation kind of glosses over what this means, but in a nutshell:

  • attribute = when this attribute appears in a layout file
  • type = then look for the implementation in this class
  • method = of a method with this name in the class defined in type

For more on this take a look at this blog post.

Related