Kotlin addTextChangeListener Lambda?

103

Wie erstellt man einen Lambda-Ausdruck für den EditText addTextChangeListener in Kotlin? Unten gibt es einen Fehler:

passwordEditText.addTextChangedListener { charSequence  ->
    try {
        password = charSequence.toString()
    } catch (error: Throwable) {
        raise(error)
    }
}
LEMUEL ADANE
quelle
2
Welchen Fehler gibt es?
Voddan

Antworten:

244

addTextChangedListener()nimmt eine, TextWatcherdie eine Schnittstelle mit 3 Methoden ist. Was Sie geschrieben haben, würde nur funktionieren, wenn Sie TextWatchernur eine Methode hätten. Ich gehe davon aus, dass der Fehler, den Sie erhalten, damit zusammenhängt, dass Ihr Lambda die anderen beiden Methoden nicht implementiert. Sie haben 2 Optionen für die Zukunft.

  1. Lassen Sie das Lambda fallen und verwenden Sie einfach eine anonyme innere Klasse
    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. Erstellen Sie eine Erweiterungsmethode, damit Sie einen Lambda-Ausdruck verwenden können:
    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())
          }
        })
    }

Und dann benutze die Erweiterung so:

editText.afterTextChanged { doSomethingWithText(it) }
Andrew Orobator
quelle
4
Nicht sicher, ob persönliche Präferenz oder besserer Stil, aber Ihre Erweiterungsfunktion könnte in einen Ausdruckskörper ( fun foo() = ...) umgewandelt werden
F. George
6
@ mEQ5aNLrK3lqs3kfSa5HbvsTWe0nIu Sie haben Recht, dass es konvertiert werden kann. Bei Funktionen, die länger als eine Zeile sind, möchte ich jedoch, dass umgebende Klammern deutlich markieren, wo die Funktion startet und stoppt. Ich glaube, es erhöht die Lesbarkeit, aber es ist eine Stilvorliebe. Ich denke, es könnte in beide Richtungen argumentiert werden :)
Andrew Orobator
2
Aus Interesse: Warum anrufen afterTextChanged.invoke(...)statt afterTextChanged(...)?
Felix D.
Das hat bei mir funktioniert. Ich habe die 2. Option für die Wiederverwendbarkeit bevorzugt.
Onie Maniego
21

Ein bisschen alt, aber mit Kotlin Android-Erweiterungen können Sie so etwas tun:

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

Kein zusätzlicher Code erforderlich, einfach hinzufügen:

implementation 'androidx.core:core-ktx:1.0.0'
Efi MK
quelle
4
Das funktioniert bei mir nicht, selbst nachdem ich auf Android X umgestaltet habe. Ratet mal, was ich falsch machen könnte?
Nícolas Schirmer
3
Funktioniert auch bei mir nicht. Es sieht so aus, als würde KTX diese Erweiterung nicht mehr anbieten, die KAndroid Lösung funktioniert jedoch einwandfrei.
Igor Wojda
15

Fügen Sie diese Kern-ktx-Abhängigkeit hinzu

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

Du musst es einfach tun

passwordEditText.doAfterTextChanged{ }

Alaa
quelle
12

Ich hoffe, dieses KotlinBeispiel hilft dabei, es klar zu machen:

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

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

Und das 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
quelle
12

Probier es aus :

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

Wenn Sie verwenden implementation 'androidx.core:core-ktx:1.1.0-alpha05', können Sie verwenden

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

Entschuldigung für die Verspätung!

Wenn Sie implementation 'androidx.core:core-ktx:1.1.0'die build.gradle-Datei Ihres Moduls hinzufügen , können Sie verwenden

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

Eine andere Alternative ist die KAndroidBibliothek -

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

Dann könnten Sie so etwas tun ...

editText.textWatcher { afterTextChanged { doSomething() } }

Natürlich ist es übertrieben, eine ganze Bibliothek zu verwenden, um Ihr Problem zu lösen, aber es enthält auch eine Reihe anderer nützlicher Erweiterungen, die den Boilerplate-Code im Android SDK eliminieren.

nsc9012
quelle
2

Sie können die benannten Parameter von kotlin verwenden:

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

Fügen Sie die Kern-ktx-Abhängigkeit hinzu

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

Und Sie können einfach so implementieren

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

Das sieht ordentlich aus:

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

    if (keyCode == DONE) {                       
         // your code here
    }
    false
}
LEMUEL ADANE
quelle
1
Ich weiß es wirklich nicht mit euch, aber meine Antwort hat für mich funktioniert und ist kürzer als die Antwort oben und unten.
LEMUEL ADANE
1
Dies funktioniert so, wie es der Code beabsichtigt, und führt eine Aktion aus, sobald DONE gedrückt wird. Ich habe den Code geändert, indem ich einen Toast außerhalb der Bedingung platziert habe, und er scheint nur beim Drücken von TAB / DONE / etc. Auszulösen. aber nicht auf andere Charaktere.
Onie Maniego