Kotlin addTextChangeListener lambda?


103

Come si crea un'espressione lambda per EditText addTextChangeListener in Kotlin? Di seguito viene fornito un errore:

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

2
Che errore dà?
— voddan

Risposte:


244

addTextChangedListener()prende una TextWatcherche è un'interfaccia con 3 metodi. Quello che hai scritto funzionerebbe solo se TextWatcheravessi un solo metodo. Immagino che l'errore che stai ricevendo si riferisca al tuo lambda che non implementa gli altri 2 metodi. Hai 2 opzioni in futuro.

  1. Abbandona il lambda e usa solo una classe interna anonima
    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. Crea un metodo di estensione in modo da poter utilizzare un'espressione 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())
          }
        })
    }

E poi usa l'estensione in questo modo:

editText.afterTextChanged { doSomethingWithText(it) }

4
Non sono sicuro se la preferenza personale o uno stile migliore, ma la tua funzione di estensione potrebbe essere convertita in un corpo di espressione ( fun foo() = ...)
— F. George

6
@ mEQ5aNLrK3lqs3kfSa5HbvsTWe0nIu Hai ragione che può essere convertito. Tuttavia, per le funzioni più lunghe di una riga, mi piace avere parentesi quadre per contrassegnare chiaramente dove inizia e finisce la funzione. Credo che aumenti la leggibilità, ma è totalmente una preferenza di stile. Penso che potrebbe essere discusso in entrambi i modi :)
— Andrew Orobator

2
Per interesse: perché chiamare afterTextChanged.invoke(...)invece di afterTextChanged(...)?
— Felix D.

Questo ha funzionato per me. Ho preferito la seconda opzione per la riutilizzabilità.
— Onie Maniego

21

Un po 'vecchio, ma usando le estensioni di Kotlin per Android puoi fare qualcosa del genere:

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

Nessun codice aggiuntivo necessario, basta aggiungere:

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

4
Non funziona per me, anche dopo il refactoring su Android X. Qualche idea su cosa potrei fare di sbagliato?
— Nícolas Schirmer

3
Non funziona anche per me. Sembra che KTX non fornisca più questa estensione, tuttavia, la KAndroid soluzione funziona perfettamente.
— Igor Wojda


16

Aggiungi questa dipendenza principale da ktx

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

Devi semplicemente fare

passwordEditText.doAfterTextChanged{ }


12

spero che questo Kotlinesempio aiuti a chiarire:

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
   }
}

Con questo XMLlayout:

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

E questo 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

12

Provalo :

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

    }

})

10

se usi implementation 'androidx.core:core-ktx:1.1.0-alpha05'puoi usare

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


4

Scusate per il ritardo!

Se aggiungi implementation 'androidx.core:core-ktx:1.1.0'al file build.gradle del tuo modulo, puoi usare

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

2

Un'altra alternativa è la KAndroidbiblioteca -

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

Allora potresti fare qualcosa del genere ...

editText.textWatcher { afterTextChanged { doSomething() } }

Ovviamente è eccessivo utilizzare un'intera libreria per risolvere il tuo problema, ma include anche una serie di altre utili estensioni che eliminano il codice boilerplate nell'SDK di Android.


2

Puoi utilizzare i parametri denominati di 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)
    }
})

0

Aggiungi la dipendenza principale di ktx

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

E puoi semplicemente implementare in questo modo

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

-9

Questo sembra pulito:

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

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

1
Non so davvero con voi ragazzi, ma la mia risposta ha funzionato per me ed è più breve della risposta sopra e sotto ..
— LEMUEL ADANE

1
Funziona come quello che il codice intende essere, esegue l'azione una volta premuto DONE. Ho modificato il codice inserendo un brindisi al di fuori della condizione e sembra che si attivi solo quando si preme TAB / DONE / ecc. ma non su altri personaggi.
— Onie Maniego
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.