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.