Moderne Android-Entwicklung mit Kotlin

Seite 3: Extension Methods, Funktionen & Beispiel

Inhaltsverzeichnis

Die APIs von Android und Java sind auf einen breiten Einsatzbereich und größtmögliche Flexibilität ausgerichtet. Im Alltag spiegelt sich das häufig in langer und repetitiver Verwendungen wider. In der eigenen Android-App wünschen sich Entwickler schnell individuelle Abkürzungen für wiederkehrende Wege. Üblicherweise führt das schnell zu einem Potpourri unterschiedlicher Util-Klassen im Projekt.

Um das zu umgehen, erlaubt Kotlin ähnlich wie C# das Erweitern fremder Klassen um neue Funktionalität ohne von ihnen ableiten oder sie in ein Decorator Pattern verpacken zu müssen. Über Extensions Methods und Extension Properties lassen sich Java- und Android-Klassen mit neuen Methoden und Properties ausbauen.

fun Fragment.toast(msg: String,
dur: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this.getActivity(), msg, dur).show()
}

var EditText.stringValue: String
get() = text.toString()
set(str) { setText(str) }

val Int.dpInPixel: Int get() = (
this * Resources.getSystem().displayMetrics.density + 0.5f
).toInt()

Das Beispiel erweitert die Android Fragments um eine Methode für Toast-Meldungen, die EditText-Widgets um eine Property für den komfortableren Direktzugriff auf den aktuellen Text und Integer-Zahlen um eine Property für die direkte Umrechnung einer dp-Einheit in Pixel. Ihre Verwendung geht ähnlich elegant von statten:

toast("Hallo!")
nameEdit.stringValue = "Unbekannter"
val p = 5.dpInPixel

Bereits Kotlins Standardbibliothek rüstet über Extensions Funktionen in Java-Klassen nach. Neben eigenen Erweiterungen bieten Bibliotheken wie KAndroid und Anko, die sich speziell an Android richten, weitere hilfreiche Extensions an.

Funktionen an Events zu knüpfen gehört zum täglich Brot der App-Entwickler. Der dafür notwendige Code präsentiert sich in der traditionellen Form unter Android noch etwas sperrig.

view.setOnClickListener(new OnClickListener() {
@Override public void onClick(View view) {
view.scrollTo(0,0);
}
});

In neueren Android-Versionen oder mit Retrolambda lassen sich die auch als Lambda oder Closure bekannten anonymen Funktionsausdrücke nutzen, um Interfaces mit nur einer fehlenden Methode zu implementieren (Single Abstract Method). Dem Ansatz folgt auch Kotlin, sodass sich der Click-Listener in der folgenden Form implementieren lässt:

view.setOnClickListener { it.scrollTo(0,0) }

Das Symbol it ist in Kotlin ein eingebauter Platzhalter. Er kann immer verwendet werden, wenn der Funktionsausdruck nur einen Parameter erwartet. Für Funktionen mit mehreren Parametern kommt die vermutlich etwas vertrautere Syntax { x,y -> x + y } zum Einsatz. Ist ein Lambda-Ausdruck der letzte Parameter eines Funktionsaufrufs, können die runden Klammern entfallen. Das vorige Beispiel ist somit unter Kotlin nur syntaktischer Zucker für die ausgeschriebene Form:

view.setOnClickListener ({ view -> view.scrollTo(0,0) })

Kotlin geht aber über die aus Java 8 bekannten Lambdas hinaus, da Funktionen in der Sprache Elemente erster Klasse sind. Sie können sowohl als Variablen, Properties, Parameter und Rückgabewerte genutzt werden. Dadurch erlauben sie eine Verschachtelung von Funktionsausdrücken, die allgemein als Funktionen höherer Ordnung bekannt sind.

Die folgende Funktion erwartet als Parameter eine andere Funktion und führt sie nur aus, wenn die aktuelle Android-Version mindestens Nougat ist:

fun ifNougatOrNewer(f: () -> Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
f()
}
}

Dank der Kurzschreibweise lässt sie sich ohne runde Klammern nutzen.

ifNougatOrNewer {
showVRButton()
}

Ein Großteil der Kotlin Standard Library ergänzt lediglich die vorhandenen Java-Klassen durch Extensions um Funktionen höherer Ordnung. Sie rüsten oft fortschrittliche, aber gemeinhin übliche Funktionen wie filter() nach.

val list = listOf("a", "b", "c", "d")
for (index in list.indices.filter { it % 2 == 0 }) {
println("$index -> ${list[index]}")
}

Zum Abschluss noch ein kleines Beispiel für einen praxisnahen Eindruck. Bis auf das im Layout-Editor leicht erstellbare Layout-XML, handelt es sich um eine vollständig in Kotlin geschriebene Android-App. Neu ist noch der when-Ausdruck als flexibeler Ersatz für den switch-Operator. Außer konstanten Werten können Entwickler in when auch Ausdrücke (isValid(id)), Bereiche (in 3..7) oder Typprüfungen (is String) verwenden.

package de.bentolor.helloworld

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.EditText
import kotlinx.android.synthetic.main.activity_hello.*
import java.util.*

class HelloActivity : AppCompatActivity() {

private val gast = Gast(name = "Publikum")

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_hello)
modelToView()

editName.setOnEditorActionListener { v, i, e ->
gast.name = editName.inhalt()
modelToView()
false
}
anredeGroup.setOnCheckedChangeListener({ g, id ->
gast.titel = when (id) {
radioHerr.id -> Titel.Herr
radioFrau.id -> Titel.Frau
else -> Titel.wertes
}
modelToView()
})
}

fun modelToView() {
labelGruss.text = gast.anrede
}
}

enum class Titel { wertes, Herr, Frau, Doktor }

data class Gast(var titel: Titel = Titel.wertes,
var name: String, val geburt: Date? = null) {
val anrede: String
get() = "Hallo $titel $name!"
}

fun EditText.inhalt(): String = text.toString()