Stufe 6Werkzeuge und Oberflächen · Lektion 33 von 34
Swing: Oberflächen mit scala-swing
Swing ist das klassische Oberflächen-Toolkit der Java-Plattform. Die Bibliothek scala-swing legt eine schlanke Scala-Schicht darüber: Fenster, Layouts und Komponenten werden knapper beschrieben, Ereignisse mit Pattern Matching behandelt.
Einordnung
Swing ist seit Jahrzehnten Teil des JDK und läuft ohne weitere Installation auf Windows, macOS und Linux. Für neue Desktop-Anwendungen greifen viele heute zu JavaFX (in Scala: ScalaFX) oder zu Weboberflächen. Swing bleibt aber wertvoll für interne Werkzeuge, zum Lernen und für die Pflege bestehender Programme – und scala-swing ist ein gutes Beispiel dafür, wie Scala eine Java-Bibliothek angenehmer macht.
scala-swing ist nicht mehr Teil der Standardbibliothek, sondern ein eigenes Modul:
libraryDependencies += "org.scala-lang.modules" %% "scala-swing" % "3.0.0"Mit Scala CLI genügt die Direktive //> using dep org.scala-lang.modules::scala-swing:3.0.0 am Anfang der Datei.
Das Anwendungsgerüst
import scala.swing.*
import scala.swing.event.ButtonClicked
object Zaehler extends SimpleSwingApplication:
def top: Frame = new MainFrame:
title = "Zähler"
preferredSize = new Dimension(360, 180)
private var klicks = 0
private val anzeige = new Label("Klicks: 0")
private val knopf = new Button("Plus eins")
contents = new BoxPanel(Orientation.Vertical):
contents += anzeige
contents += knopf
border = Swing.EmptyBorder(20, 20, 20, 20)
listenTo(knopf)
reactions += {
case ButtonClicked(`knopf`) =>
klicks += 1
anzeige.text = s"Klicks: $klicks"
}SimpleSwingApplication erzeugt den Einstiegspunkt und zeigt das Fenster an, das top liefert. MainFrame ist ein Hauptfenster, das beim Schließen das Programm beendet. Die Eigenschaften title, preferredSize und contents werden wie Variablen gesetzt – scala-swing übersetzt das in die entsprechenden Java-Aufrufe.
Ereignisse
Statt anonymer Listener-Klassen wie in Java hört eine Komponente mit listenTo(…) auf andere und reagiert in reactions mit Pattern Matching auf Ereignisse. Die Backticks um knopf bedeuten: Vergleiche mit dem vorhandenen Wert knopf, statt eine neue Variable einzuführen. So können mehrere Knöpfe im selben Block unterschieden werden.
Häufige Ereignisse sind ButtonClicked, ValueChanged (Textfelder, Schieberegler), SelectionChanged (Listen, ComboBox) und EditDone (Textfeld verlassen oder Enter gedrückt).
Layout-Panels
Wie die Komponenten angeordnet werden, bestimmt das Panel, in dem sie liegen:
| Panel | Anordnung |
|---|---|
FlowPanel | nebeneinander, bricht bei Platzmangel um |
BoxPanel(Orientation.Vertical) | untereinander (oder Horizontal: nebeneinander) |
BorderPanel | fünf Bereiche: Norden, Süden, Osten, Westen, Mitte |
GridPanel(zeilen, spalten) | gleich große Zellen im Raster |
GridBagPanel | flexibles Raster mit Gewichtungen |
ScrollPane | macht großen Inhalt scrollbar |
Panels lassen sich verschachteln – ein BorderPanel mit einer Werkzeugleiste oben (FlowPanel) und einem Formular in der Mitte (GridPanel) ist ein typischer Aufbau.
Wichtige Komponenten
val name = new TextField(20) // einzeiliges Textfeld
val notiz = new TextArea(5, 30) // mehrzeilig
val aktiv = new CheckBox("Newsletter") // an/aus
val stufe = new ComboBox(List("Einstieg", "Sprachkern", "Collections"))
val gruppe = new ButtonGroup()
val klein = new RadioButton("klein")
val gross = new RadioButton("groß")
gruppe.buttons ++= List(klein, gross) // nur einer wählbar
val schalter = new ToggleButton("Dunkelmodus")
val liste = new ListView(List("Scala", "Java", "Kotlin"))Zugriff auf die Werte: name.text, aktiv.selected, stufe.selection.item, liste.selection.items. Damit die ComboBox Änderungen meldet, hören Sie auf ihre Auswahl: listenTo(stufe.selection).
Swing und Java mischen
Jede scala-swing-Komponente verpackt eine Java-Swing-Komponente, erreichbar über .peer. Umgekehrt lässt sich eine beliebige Java-Komponente mit Component.wrap(javaKomponente) einbinden. So können Sie Java-Bibliotheken für Diagramme oder Tabellen weiterverwenden.