17

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!

Leandro Borges Ferreira
  • 12,422
  • 10
  • 53
  • 73
  • are you looking something like https://stackoverflow.com/questions/2004344/how-do-i-handle-imeoptions-done-button-click or is it some thing else? – Firoz Memon Jul 24 '17 at 18:56
  • It is clearly something else... The answer you presented doesn't use databinding, it uses a listener. I would like databinding to active a behavior. Please don't down vote a question unless you are really sure it is duplicated – Leandro Borges Ferreira Jul 24 '17 at 18:59
  • Just to be clear I DID NOT downvote your question.. and by databinding what exactly you are trying to do is still unclear, could you please explain what you are trying to achieve (also it would be better if you show us what you have tried) – Firoz Memon Jul 24 '17 at 19:02
  • Sorry Firoz, I did not mean you as the down voter... I was just saying for anyone reading the coment. Thanks for trying to help me in this question! I updated the question to make it more clear – Leandro Borges Ferreira Jul 24 '17 at 19:05

6 Answers6

16

I won't claim to be an expert in onEditorAction() or soft keyboard. That said, assuming you use the solution to the stack overflow question Firoz Memon suggested, you can make it happen. Even if there is another solution that works better, this can give you an idea on how to add your own event handlers.

You'd need a binding adapter that takes some kind of handler. Let's assume you have an empty listener like this:

public class OnOkInSoftKeyboardListener {
    void onOkInSoftKeyboard();
}

Then you need a BindingAdapter:

@BindingAdapter("onOkInSoftKeyboard") // I like it to match the listener method name
public static void setOnOkInSoftKeyboardListener(TextView view,
        final OnOkInSoftKeyboardListener listener) {
    if (listener == null) {
        view.setOnEditorActionListener(null);
    } else {
        view.setOnEditorActionListener(new OnEditorActionListener() {
            @Override
            public void onEditorAction(TextView v, int actionId, KeyEvent event) {
                // ... solution to receiving event
                if (somethingOrOther) {
                    listener.onOkInSoftKeyboard();
                }
            }
        });
    }
}
George Mount
  • 20,708
  • 2
  • 73
  • 61
15

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"/>
ersin-ertan
  • 2,283
  • 2
  • 18
  • 27
  • also, you can use lambda like and remove the signIn Function1 from viewModle fun EditText.onEditorEnterAction(block: ((password: String) -> Unit)?) { ... block(password) } – Omar Abdan Oct 21 '20 at 13:08
  • @OmarAbdan, then how to use this function in XML? – CoolMind Jun 10 '21 at 11:33
  • Thanks, it works. Don't forget to remove `android:inputType="textMultiLine"`, maybe set `android:maxLines="1"` and `android:singleLine="true"`. – CoolMind Jun 10 '21 at 12:14
  • For multiline see https://stackoverflow.com/questions/2986387/multi-line-edittext-with-done-action-button. – CoolMind Jun 10 '21 at 12:42
9

Just as i was looking at this myself, here a simpler version where the function is directly called from the data binding:

In your ViewModel use this function:

 public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
return false; // if you want the default action of the actionNext or so on
return true; // if you want to intercept
}

And in the layout:

android:onEditorAction="@{(view,actionId,event) -> viewModel.onEditorAction(view,actionId,event)}"
Tosa
  • 658
  • 5
  • 15
  • KeyEvent event return null! – Sajad Rahmanipour Apr 22 '18 at 15:49
  • 2
    unknown attribute onEditorAction @Tosa – hushed_voice May 09 '18 at 06:19
  • it does say that in the editor but works fine like this for me when the app is running.@free_style – Tosa Jun 14 '18 at 02:03
  • @Tosa, event is null always – Sajad Rahmanipour Jun 18 '18 at 09:01
  • @SajadRahmanipour and it is not null when you implement the listener like from the marked answer? https://developer.android.com/reference/android/widget/TextView.OnEditorActionListener – Tosa Jun 19 '18 at 04:12
  • 1
    This is the correct thing to do since it leverages the adapters provided by data binding, in TextViewBindingAdapter.java: ` @BindingMethod(type = TextView.class, attribute = "android:onEditorAction", method = "setOnEditorActionListener")` – androidguy Nov 29 '19 at 17:33
  • 1
    This wont work: cannot find method onEditorAction(android.widget.TextView, int, android.view.KeyEvent) because we are passing in EditText not TextView. Even though it extends TextView it still fails. – DevinM Jan 23 '20 at 17:18
3

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.

gprathour
  • 14,813
  • 5
  • 66
  • 90
  • 1
    @AhmadAbdullah For me it worked this way. There might be something missing or may be some other concern. – gprathour May 21 '21 at 14:16
  • 1
    Thank you @gprathour , it's worked, I just had a mistake in the function return type – Ahmad Abdullah May 23 '21 at 13:50
  • @AhmadAbdullah That's good to know. The errors shown for data binding are sometimes weird and not easy to understand what exactly is the error. Good Luck! – gprathour May 25 '21 at 03:56
0

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
    })
Naveen Kumar M
  • 7,497
  • 7
  • 60
  • 74
0

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.

Filipe Bezerra de Sousa
  • 2,874
  • 1
  • 17
  • 17