在Databinding中使用键盘上的“完成”按钮

17

我想使用软键盘的“完成”按钮通过数据绑定来激活一个方法,就像onClick一样。有没有办法实现这个功能?

示例:

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

onOkInSoftKeyboard不存在... 有什么方法可以实现这个功能吗?

谢谢!


你是在寻找类似于https://dev59.com/IXI-5IYBdhLWcg3wHUrP这样的东西,还是其他的什么? - Firoz Memon
这显然是另外一回事... 你提供的答案没有使用数据绑定,而是使用了监听器。我想要数据绑定来激活一个行为。请不要在确定重复问题之前就对问题进行投票。 - Leandro Borges Ferreira
只是为了明确,我没有downvote你的问题..并且通过数据绑定,你究竟想要做什么还不清楚,能否请你解释一下你想实现什么(如果你能展示一下你已经尝试过的会更好)。 - Firoz Memon
抱歉,Firoz,我并不是指责你投了反对票...我只是针对任何读者进行说明。感谢你试图帮助我解决这个问题!我更新了问题以使其更加清晰。 - Leandro Borges Ferreira
6个回答

16

我不会声称自己是onEditorAction()或者软键盘方面的专家。假设你使用了Firoz Memon在Stack Overflow上提出的解决方案,那么你可以实现它。即使存在其他更好的解决方案,这也可以让你了解如何添加自己的事件处理程序。

你需要一个绑定适配器来接受某种类型的处理程序。假设你有一个空监听器像这样:

public class OnOkInSoftKeyboardListener {
    void onOkInSoftKeyboard();
}

那么您需要一个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();
                }
            }
        });
    }
}

很棒的解决方案! - Leandro Borges Ferreira
@LeandroBorgesFerreira,能在这里发布完整的代码吗?我在尝试实现它时遇到了数据绑定错误。 - mdb
嘿,如何在XML中传递监听器? - skyshine
如果您使用TextViewBindingAdapter.java生成的适配器,请忽略此处的所有样板内容-请参见此答案https://dev59.com/1VcO5IYBdhLWcg3wvUMp#49375084 - androidguy

15

使用 Kotlin,kapt 会生成:

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

由于 viewModel::signIn 是类型为KFunction1,所以我们无法使用方法引用。但是,如果我们在viewModel中创建一个明确类型的变量,那么我们就可以将该变量作为绑定参数传递。(或者只需使用类)

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"/>

同时,您可以像使用lambda表达式一样,从viewModel中删除signIn Function1。fun EditText.onEditorEnterAction(block: ((password: String) -> Unit)?) { ... block(password) } - Omar Abdan
@OmarAbdan,那么如何在XML中使用这个函数? - CoolMind
谢谢,它有效。别忘了删除 android:inputType="textMultiLine",可能设置 android:maxLines="1"android:singleLine="true" - CoolMind
对于多行文本,请参见https://dev59.com/iXA75IYBdhLWcg3w_uqD。 - CoolMind

9

正如我自己看到的那样,这里有一个更简单的版本,其中函数直接从数据绑定中调用:

在您的ViewModel中使用此函数:

 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
}

在布局中:
android:onEditorAction="@{(view,actionId,event) -> viewModel.onEditorAction(view,actionId,event)}"

2
onEditorAction @Tosa 上的未知属性 - hushed_voice
当应用程序运行时,虽然编辑器中会显示那样的信息,但在我看来它仍能正常工作。 - Tosa
@Tosa,事件始终为空。 - Sajad Rahmanipour
1
这是正确的做法,因为它利用了数据绑定提供的适配器,在TextViewBindingAdapter.java中:@BindingMethod(type = TextView.class, attribute = "android:onEditorAction", method = "setOnEditorActionListener") - androidguy
1
这样不行:无法找到方法 onEditorAction(android.widget.TextView, int, android.view.KeyEvent),因为我们传递的是 EditText 而不是 TextView。即使它继承了 TextView,它仍然失败了。 - DevinM
显示剩余2条评论

3

使用Kotlin编写自定义绑定适配器的方法

在布局文件中,

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

视图模型

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

注意:event可能为null,因此通过在KeyEvent后面加上?来使其可为空。


1
@AhmadAbdullah 对我来说,这种方法有效。可能有些东西遗漏了或者还有其他问题。 - gprathour
1
谢谢 @gprathour,它起作用了,我只是在函数返回类型上犯了一个错误。 - Ahmad Abdullah
@AhmadAbdullah 非常好的信息。数据绑定出现的错误有时候很奇怪,不容易理解具体是什么错误。祝你好运! - gprathour

0

Android 框架已经实现了这个功能。看一下TextViewBindingAdapter

你会看到这些属性,文档有点对这意思进行了概述,但简而言之:

  • attribute = 当这个属性出现在布局文件中时
  • type = 然后在这个类中查找实现
  • method = 在类型定义的类中使用这个名称的方法

更多内容,请参见此博客文章


0

您可以通过在EditText上实现setOnEditorActionListener并参考绑定类,直接调用ViewModel中的login方法。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接