Eine eigene Scala-Swing-Komponente


Scala bringt eine kleine Wrapper-Bibliothek für Swing mit, und heute will ich einmal eine kleine Komponente dafür schreiben: Eine Digitaluhr.

Scala-Swing arbeitet recht gut und ist Closures sei dank deutlich kürzer als Java, aber die Dokumentation ist noch ziemlich dünn gesät. Will man dafür eigene Komponenten programmien, kommt man kaum umhin, ein wenig in den Eingeweiden zu wühlen (auch wenn im folgenden Code kaum etwas davon zu sehen ist). Ein wenig Magie ist schon im Spiel, z.B. dass paintComponent in einer Klasse „überschrieben“ werden kann, die nicht von JComponent erbt. Intern gibt es natürlich eine JComponent (namens „peer“). Ich habe eine Weile wie ein Schwein ins Uhrwerk geschaut, um zu begreifen, wie selbige mit sanfter Gewalt und einem Trait namens SuperMixin dazu gebracht wird, mein paintComponent aufzurufen. Hier nun der Code:

//Scala 2.8
import scala.swing._
import java.util.Date
import java.awt.{Color, Graphics2D }
import java.awt.event.{ActionListener, ActionEvent}

class Clock(scale:Int, middleground:Color = Color.lightGray) extends Component {

  val digitData = List("-|| ||-", "  |  | ", "- |-| -", "- |- |-", " ||- | ",
                       "-| - |-", "-| -||-", "- |  | ", "-||-||-", "-||- |-")
  val digitRects = Array((1,3,3,1),(0,4,1,3),(4,4,1,3),(1,7,3,1),
                         (0,8,1,3),(4,8,1,3),(1,11,3,1))
  val digitX = List(3,9,17,23,31,37)
  val points = List((15,6),(15,9),(29,6),(29,9))

  preferredSize = (45*scale, 15*scale)

  new javax.swing.Timer(1000, new ActionListener {
      def actionPerformed(ae:ActionEvent) { repaint }
    }).start

  override def paintComponent(g: Graphics2D) {
    def scaledRect(x:Int, y:Int, w:Int, h:Int):Unit =
      g.fillRect(x*scale, y*scale, w*scale, h*scale)
    def digit(d:Int, dx:Int):Unit = (0 to 6).foreach { k =>
      g.setColor(if (digitData(d)(k) == ' ') middleground else foreground)
      val (x,y,w,h) = digitRects(k)
      scaledRect(x + dx, y, w, h)
    }

    g.setColor(background)
    scaledRect(0,0, 45, 15)
    val date = new Date
    val (h,m,s) = (date.getHours, date.getMinutes, date.getSeconds)
    List(h/10,h%10,m/10,m%10,s/10,s%10).zip(digitX).foreach(t => digit(t._1,t._2))
    g.setColor(foreground)
    points.foreach(t => scaledRect(t._1, t._2, 1, 1))
  }
}

Und hier die Uhr in Aktion:

Screenshot der Komponente

Wieder mal wird mein Code keinen Schönheitspreis gewinnen. Die zwei vordefinierten Farben foreground und background reichen nicht aus, also habe ich zusätzlich noch middleground eingeführt (für die „leeren“ Balken in der Digitalanzeige). digitData definiert, welche Balken eine Ziffer hat, digitRects speichert die Koordinaten der Balken, digitX die horizontale Ausrichtung der sechs Ziffern und points die Koordinaten der Doppelpunkte. Ein gewöhnlicher Swing-Timer übernimmt das regelmäßige Update der Anzeige.

Die Methode paintComponent ist wieder ein hübsches Beispiel, wie lokale Methoden helfen, den Code zu gliedern. Da ich meine Uhr proportional zu einem Faktor „scale“ zeichne, implementiere ich erst einmal eine skalierte Version von Graphics.fillRect() . Dann kommt der Code zum Zeichnen einer Ziffer, der dank der vordefinierten Daten recht kurz ausfällt. Die eigentliche paintComponent-Funktion löscht nur noch den Hintergrund, holt sich das aktuelle Datum, lässt die entsprechenden Ziffern zeichnen und zum Schluss die Doppelpunkte.

Ich bin mit meiner kleinen Komponente ganz zufrieden. Natürlich könnte sie noch verbessert werden, z.B. in dem sie sich an den zur Verfügung stehenden Platz anpassen könnte. Eine kleine Einführung zu Scala-Swing findet sich hier.

Advertisements

2 Gedanken zu “Eine eigene Scala-Swing-Komponente

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s