Kotlin addTextChangeListener lambda?

103

Como você constrói uma expressão lambda para EditText addTextChangeListener em Kotlin? Abaixo dá um erro:

passwordEditText.addTextChangedListener { charSequence  ->
    try {
        password = charSequence.toString()
    } catch (error: Throwable) {
        raise(error)
    }
}
LEMUEL ADANE
fonte
2
Que erro isso dá?
voddan

Respostas:

244

addTextChangedListener()leva um TextWatcherque é uma interface com 3 métodos. O que você escreveu só funcionaria se TextWatchertivesse apenas 1 método. Vou supor que o erro que você está obtendo está relacionado ao seu lambda não implementar os outros 2 métodos. Você tem 2 opções daqui para frente.

  1. Abandone o lambda e use apenas uma classe 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. Crie um método de extensão para que possa usar uma expressão 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 então use a extensão assim:

editText.afterTextChanged { doSomethingWithText(it) }
Andrew Orobator
fonte
4
Não tenho certeza se preferência pessoal ou estilo melhor, mas sua função de extensão pode ser convertida em um corpo de expressão ( fun foo() = ...)
F. George
6
@ mEQ5aNLrK3lqs3kfSa5HbvsTWe0nIu Você está certo que pode ser convertido. No entanto, para funções com mais de uma linha, gosto de colocar colchetes para marcar claramente onde a função começa e termina. Acredito que aumenta a legibilidade, mas é totalmente uma preferência de estilo. Eu acho que pode ser argumentado de ambas as maneiras :)
Andrew Orobator
2
Sem interesse: por que ligar em afterTextChanged.invoke(...)vez de afterTextChanged(...)?
Felix D.
Isso funcionou para mim. Eu preferi a 2ª opção para reutilização.
Onie Maniego
21

Um pouco antigo, mas usando as extensões Kotlin do Android, você pode fazer algo assim:

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

Nenhum código extra necessário, basta adicionar:

implementation 'androidx.core:core-ktx:1.0.0'
Efi MK
fonte
4
Isso não está funcionando para mim, mesmo depois de refatorar para o Android X. Alguma idéia do que eu poderia estar fazendo de errado?
Nícolas Schirmer
3
Não funciona para mim também. Parece que o KTX não oferece mais essa extensão, no entanto, a KAndroid solução funciona perfeitamente.
Igor Wojda
2
com ktx: 1.0.1 você pode usar developer.android.com/reference/kotlin/androidx/core/widget/…
ingyesid
16

Adicione esta dependência ktx principal

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

Você simplesmente tem que fazer

passwordEditText.doAfterTextChanged{ }

Alaa
fonte
12

espero que este Kotlinexemplo ajude a deixar 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
   }
}

Com este 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 este 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
fonte
12

Teste-o :

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

se você usar, implementation 'androidx.core:core-ktx:1.1.0-alpha05'você pode 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
fonte
4

Desculpe por estar atrasado!

Se você adicionar implementation 'androidx.core:core-ktx:1.1.0'ao arquivo build.gradle do seu módulo, você pode usar

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

Outra alternativa é a KAndroidbiblioteca -

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

Então você poderia fazer algo assim ...

editText.textWatcher { afterTextChanged { doSomething() } }

Obviamente, é excessivo usar uma biblioteca inteira para resolver seu problema, mas também vem com uma variedade de outras extensões úteis que eliminam o código clichê no Android SDK.

nsc9012
fonte
2

Você pode usar os parâmetros nomeados do 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
fonte
0

Adicione a dependência ktx principal

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

E você pode simplesmente implementar assim

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

Isso parece legal:

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

    if (keyCode == DONE) {                       
         // your code here
    }
    false
}
LEMUEL ADANE
fonte
1
Eu realmente não sei com vocês, mas minha resposta funcionou para mim e é mais curta do que a resposta acima e abaixo ..
LEMUEL ADANE
1
Isso funciona como o código pretende ser, executa a ação uma vez que DONE é pressionado. Modifiquei o código colocando um brinde fora da condição e parece que só dispara ao pressionar TAB / DONE / etc. mas não em outros personagens.
Onie Maniego