Kotlin addTextChangeListener lambda?

Как построить лямбда-выражение для addTextChangeListener EditText в Kotlin? Ниже выдает ошибку:

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

14 ответов

Решение

Это выглядит аккуратно:

passwordEditText.setOnEditorActionListener { 
    textView, keyCode, keyEvent ->
    val DONE = 6

    if (keyCode == DONE) {                       
         // your code here
    }
    false
}

addTextChangedListener() занимает TextWatcher который является интерфейсом с 3 методами. То, что вы написали, будет работать, только если TextWatcher был только 1 метод. Я предполагаю, что ошибка, которую вы получаете, связана с тем, что ваша лямбда не реализует другие 2 метода. У вас есть 2 варианта вперед.

1) Откажитесь от лямбды и просто используйте анонимный внутренний класс

editText.addTextChangedListener(object : TextWatcher {
  override fun afterTextChanged(p0: Editable?) {
  }

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

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

2) Создайте метод расширения, чтобы вы могли использовать лямбда-выражение:

fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
    this.addTextChangedListener(object : TextWatcher {
      override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
      }

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

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

А затем используйте расширение следующим образом:

editText.afterTextChanged { doSomethingWithText(it) }

Добавьте эту базовую зависимость ktx

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

Вам просто нужно сделать

passwordEditText.doAfterTextChanged{ }

Немного устаревший, но используя расширения Kotlin для Android, вы можете сделать что-то вроде этого:

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

Дополнительный код не требуется, просто добавьте:

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

Извините за опоздание!

Если вы добавите implementation 'androidx.core:core-ktx:1.1.0' в файл build.gradle вашего модуля, вы можете использовать

etPlayer1.doOnTextChanged { text, start, count, after -> // Do stuff }

Попробуй это:

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) {

    }

})

Надеюсь это 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

Если вы используете текстовое поле с заполненным материалом или текстовое поле с контуром , попытайтесь отреагировать на изменение вводимого текста , как указано в документации, соответственно:

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

и

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

Если вы используете 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

Добавить основную зависимость ktx

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

И вы можете просто реализовать вот так

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

Это лямбда-функция с текстовым полем редактирования с TextWatcher.

      searchField.addTextChangedListener(
        afterTextChanged = {

        },
        onTextChanged = {s, start, before, count->
            TODO("DO your code")
        },
        beforeTextChanged = {s, start, before, count->
           TODO("DO your code")
        }
    )

Вы можете использовать именованные параметры kotlin:

private val beforeTextChangedStub: (CharSequence, Int, Int, Int) -> Unit = { _, _, _, _ -> }
private val onTextChangedStub: (CharSequence, Int, Int, Int) -> Unit = { _, _, _, _ -> }
private val afterTextChangedStub: (Editable) -> Unit = {}

fun EditText.addChangedListener(
        beforeTextChanged: (CharSequence, Int, Int, Int) -> Unit = beforeTextChangedStub,
        onTextChanged: (CharSequence, Int, Int, Int) -> Unit = onTextChangedStub,
        afterTextChanged: (Editable) -> Unit = afterTextChangedStub
) = addTextChangedListener(object : TextWatcher {
    override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
        beforeTextChanged(charSequence, i, i1, i2)
    }

    override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
        onTextChanged(charSequence, i, i1, i2)
    }

    override fun afterTextChanged(editable: Editable) {
        afterTextChanged(editable)
    }
})

Другой альтернативой является KAndroid библиотека -

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

Тогда вы могли бы сделать что-то вроде этого...

editText.textWatcher { afterTextChanged { doSomething() } }

Очевидно, что для решения вашей проблемы чрезмерно использовать целую библиотеку, но она также поставляется с рядом других полезных расширений, которые устраняют шаблонный код в Android SDK.

Несколько примеров:

Rx

    RxTextView
        .afterTextChangeEvents(editText)
        .subscribe { presenter.onTextChanged(it.editable().toString())}

Это круто сделать что-то подобное

    RxTextView
        .afterTextChangeEvents(editText)
        .skipInitialValue()
        .debounce(500L, TimeUnit.MILLISECONDS)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe { presenter.onTextChanged(it.editable().toString())}
        .autoDispose()

У меня есть пыльный класс, но вы можете легко его изменить (или нет)

public class SimpleTextWatcher implements TextWatcher {

    private CallBackAfterTextChanged callBackAfterTextChanged;

    public interface CallBackAfterTextChanged {
        void afterTextChanged(Editable s);
    }

    public SimpleTextWatcher(CallBackAfterTextChanged callBackAfterTextChanged) {
        this.callBackAfterTextChanged = callBackAfterTextChanged;
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {}

    @Override
    public void afterTextChanged(Editable s) {
        callBackAfterTextChanged.afterTextChanged(s);
    }
}

в Котлине

editSearch.addTextChangedListener(SimpleTextWatcher{ presenter.onTextChanged(it.toString())})

или расширение, но я не буду повторяться

Другие вопросы по тегам