Kotlin addTextChangeListener lambda?

103

¿Cómo se construye una expresión lambda para EditText addTextChangeListener en Kotlin? A continuación se muestra un error:

passwordEditText.addTextChangedListener { charSequence  ->
    try {
        password = charSequence.toString()
    } catch (error: Throwable) {
        raise(error)
    }
}
LEMUEL ADANE
fuente
2
¿Qué error da?
voddan

Respuestas:

244

addTextChangedListener()toma una TextWatcherque es una interfaz con 3 métodos. Lo que escribiste solo funcionaría si TextWatchersolo tuvieras un método. Supongo que el error que está recibiendo se relaciona con su lambda que no implementa los otros 2 métodos. Tienes 2 opciones en el futuro.

  1. Deshazte de la lambda y solo usa una clase interna anónima
    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. Cree un método de extensión para que pueda usar una expresión 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())
          }
        })
    }

Y luego usa la extensión así:

editText.afterTextChanged { doSomethingWithText(it) }
Andrew Orobator
fuente
4
No estoy seguro si es preferencia personal o mejor estilo, pero su función de extensión podría convertirse en un cuerpo de expresión ( fun foo() = ...)
F. George
6
@ mEQ5aNLrK3lqs3kfSa5HbvsTWe0nIu Tienes razón en que se puede convertir. Sin embargo, para funciones de más de una línea, me gusta tener corchetes alrededor para marcar claramente dónde comienza y termina la función. Creo que aumenta la legibilidad, pero es totalmente una preferencia de estilo. Creo que se podría argumentar en ambos sentidos :)
Andrew Orobator
2
Fuera de interés: ¿Por qué llamar en afterTextChanged.invoke(...)lugar de afterTextChanged(...)?
Felix D.
Esto funcionó para mí. Preferí la segunda opción para la reutilización.
Onie Maniego
21

Un poco antiguo, pero con las extensiones de Android de Kotlin puedes hacer algo así:

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

No se necesita código adicional, solo agregue:

implementation 'androidx.core:core-ktx:1.0.0'
Efi MK
fuente
4
Eso no funciona para mí, incluso después de la refactorización a Android X. ¿Alguna idea de lo que podría estar haciendo mal?
Nícolas Schirmer
3
No funciona para mí también. Parece que KTX ya no proporciona esta extensión, sin embargo, la KAndroid solución funciona perfectamente.
Igor Wojda
16

Agregue esta dependencia central de ktx

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

Simplemente tienes que hacer

passwordEditText.doAfterTextChanged{ }

Alaa
fuente
12

Espero que este Kotlinejemplo ayude a dejarlo claro:

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 este XMLdiseño:

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

Y esto 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
Hasan A Yousef
fuente
12

Pruébalo :

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

    }

})
Reza Khammary
fuente
10

si lo usa implementation 'androidx.core:core-ktx:1.1.0-alpha05', puede usar

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

ingyesid
fuente
4

¡Perdón por llegar tarde!

Si agrega implementation 'androidx.core:core-ktx:1.1.0'al archivo build.gradle de su módulo, puede usar

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

Otra alternativa es la KAndroidbiblioteca -

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

Entonces podrías hacer algo como esto ...

editText.textWatcher { afterTextChanged { doSomething() } }

Obviamente, es excesivo usar una biblioteca completa para resolver su problema, pero también viene con una gama de otras extensiones útiles que eliminan el código repetitivo en el SDK de Android.

nsc9012
fuente
2

Puede hacer uso de los parámetros con nombre de 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)
    }
})
Dmitrii Bychkov
fuente
0

Agregue la dependencia principal de ktx

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

Y simplemente puedes implementar así

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

Esto se ve bien:

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

    if (keyCode == DONE) {                       
         // your code here
    }
    false
}
LEMUEL ADANE
fuente
1
Realmente no sé con ustedes, pero mi respuesta me funcionó y es más corta que la respuesta de arriba y de abajo ..
LEMUEL ADANE
1
Esto funciona como lo que el código pretende ser, realiza una acción una vez que se presiona DONE. Modifiqué el código colocando un brindis fuera de la condición y parece que solo se dispara al presionar TAB / DONE / etc. pero no en otros personajes.
Onie Maniego