Kotlin如何使用lambda添加文本更改监听器?

169

在Kotlin中如何构建一个lambda表达式用于EditText的addTextChangeListener?以下代码会报错:

passwordEditText.addTextChangedListener { charSequence  ->
    try {
        password = charSequence.toString()
    } catch (error: Throwable) {
        raise(error)
    }
}

3
它给出了什么错误? - voddan
我认为我的问题被编辑以适应他的答案。 - LEMUEL ADANE
13个回答

358
addTextChangedListener() 接受一个 TextWatcher,这是一个带有3个方法的接口。如果 TextWatcher 只有1个方法,则您编写的内容将无效。我猜测您收到的错误与您的lambda未实现其他2个方法有关。您有两个选择。

  1. 放弃使用lambda,只使用匿名内部类
    editText.addTextChangedListener(object : TextWatcher {
      override fun afterTextChanged(s: Editable?) {
      }
    
      override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
      }
    
      override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
      }
    })
  1. 创建一个扩展方法,以便您可以使用lambda表达式:
    fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
        this.addTextChangedListener(object : TextWatcher {
          override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
          }
    
          override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
          }
    
          override fun afterTextChanged(editable: Editable?) {
            afterTextChanged.invoke(editable.toString())
          }
        })
    }

然后像这样使用扩展:

editText.afterTextChanged { doSomethingWithText(it) }

4
不确定是个人偏好还是更好的风格,但您的扩展函数可以转换为表达式体 (fun foo() = ...)。 - F. George
7
你说得对,它可以转换。但是,对于超过一行的函数,我喜欢使用周围的括号来清晰地标记函数的起始和结束位置。我认为这增加了可读性,但这完全是个人风格偏好。我认为这两种方式都有争议 :) - Andrew Orobator
2
有趣的是:为什么要调用 afterTextChanged.invoke(...) 而不是 afterTextChanged(...) - Felix D.
这对我有用。我更喜欢第二个选项,因为它更具可重用性。 - Rod Maniego
但如果这不可能,为什么IDE还允许添加addTextChangedListener{}呢?然而,其代码如下:public inline fun TextView.addTextChangedListener( crossinline beforeTextChanged: ( text: CharSequence?, start: Int, count: Int, after: Int ) -> Unit = { _, _, _, _ -> }, [...] crossinline afterTextChanged: (text: Editable?) -> Unit = {} ): TextWatcher - Tobias Reich

99

添加此核心ktx依赖项

implementation 'androidx.core:core-ktx:1.0.0'
你只需执行:

passwordEditText.doAfterTextChanged{ }


34

虽然有些老了,但使用 Kotlin Android 扩展,你可以这样做:

editTextRequest.textChangedListener {
            afterTextChanged {
                // Do something here...
            }
}

无需额外代码,只需添加:

implementation 'androidx.core:core-ktx:1.0.0'

4
即使我已经迁移到 Android X,但这对我并没有起作用。你有什么猜测我可能做错了什么吗? - Nícolas Schirmer
3
这对我也不起作用。看起来 KTX 不再提供这个扩展,但 KAndroid 解决方案完美地解决了问题。 - Igor
2
使用ktx:1.0.1,您可以使用https://developer.android.com/reference/kotlin/androidx/core/widget/package-summary#extension-functions - ingyesid
2
implementation "androidx.core:core-ktx:1.2.0" -> edt.addTextChangedListener(afterTextChanged = { /*dosomething*/ }) - Dr.jacky
4
使用editTextRequest.doAfterTextChanged {...} 更方便。 - Monte Creasor

21
抱歉我迟到了!
如果您在模块的build.gradle文件中添加implementation 'androidx.core:core-ktx:1.1.0',那么您就可以使用。
etPlayer1.doOnTextChanged { text, start, count, after -> // Do stuff }

19

测试一下:

passwordEditText.addTextChangedListener(object:TextWatcher{
    override fun afterTextChanged(s: Editable?) { }

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

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

15

希望这个Kotlin示例可以帮助理解:

class MainFragment : Fragment() {

    private lateinit var viewModel: MainViewModel

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View {
    val view = inflater.inflate(R.layout.main_fragment, container, false)

    view.user.addTextChangedListener(object : TextWatcher {
        override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {

        }

        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {

        }

        override fun afterTextChanged(s: Editable) {
                userLayout.error =
                        if (s.length > userLayout.counterMaxLength) {
                            "Max character length is: ${userLayout.counterMaxLength}"
                        } else null
        }
    })
    return view
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
    // TODO: Use the ViewModel
   }
}

使用此XML布局:

<android.support.design.widget.TextInputLayout
    android:id="@+id/userLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:counterMaxLength="5"
    app:counterEnabled="true"
    android:hint="user_name">

    <android.support.design.widget.TextInputEditText
        android:id="@+id/user"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</android.support.design.widget.TextInputLayout>

还有这个Gradle

android {
    compileSdkVersion 'android-P'
...
}
    api 'com.android.support:design:28.0.0-alpha1'

    implementation 'com.android.support:appcompat-v7:28.0.0-alpha1' // appcompat library

14

如果您正在使用 Material Design 的填充文本框轮廓文本框,请尝试按照文档中所述响应输入文本的更改:

filledTextField.editText?.doOnTextChanged { inputText, _, _, _ ->
    // Respond to input text change
}

outlinedTextField.editText?.doOnTextChanged { inputText, _, _, _ ->
    // Respond to input text change
}

12

如果您使用implementation 'androidx.core:core-ktx:1.1.0-alpha05',则可以使用

For android.widget.TextView
TextWatcher 
TextView.doBeforeTextChanged(crossinline action: (text: CharSequence?, start: Int, count: Int, after: Int) -> Unit)
Add an action which will be invoked before the text changed.

TextWatcher 
TextView.doOnTextChanged(crossinline action: (text: CharSequence?, start: Int, count: Int, after: Int) -> Unit)
Add an action which will be invoked when the text is changing.

TextWatcher 
TextView.doAfterTextChanged(crossinline action: (text: Editable?) -> Unit)

https://developer.android.com/reference/kotlin/androidx/core/widget/package-summary#extension-functions


11

添加核心ktx依赖项

implementation 'androidx.core:core-ktx:1.3.0'

然后您可以像这样简单实现

    edit_text.addTextChangedListener { it: Editable? ->
      // Do your stuff here
    }

3

另一个选择是使用 KAndroid 库:

implementation 'com.pawegio.kandroid:kandroid:0.8.7@aar'

然后您可以像这样进行操作...

editText.textWatcher { afterTextChanged { doSomething() } }

显然,使用整个库来解决问题是过度的,但它还带有一系列其他有用的扩展,可以消除Android SDK中的样板代码。


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