30. July 2015

JavaFX mit Scala

JavaFX with Scala

Einer der großen Vorteile von Scala ist die sehr gute Interoperabilität mit Java. Wäre es nicht toll, wenn man eine JavaFX Applikation mit Scala schreiben könnte? Nun, man kann (sonst wäre dieser Artikel auch arg kurz). Im Folgenden zeige ich, was man beachten muss, wenn man JavaFX und Scala verheiraten will.

Ziel ist es, eine kleine JavaFX Applikation zu schreiben, deren Oberfläche in FXML-Dateien definiert ist, und daher mittels SceneBuilder erstellt werden kann. Weiterhin wollen wir den Komfort der Dependency Injection mittels @FXML Attribut nutzen können, was dank der guten Interoperabilität glücklicherweise relativ problemlos funktioniert. Der Einfachheit halber nutzen wir den View-First Ansatz, d.h. die View bestimmt welcher Controller zu laden ist und der FXMLLoader kümmert sich darum diesen Controller zu initialisieren.

Zunächst müssen wir JavaFX laden. Hierzu wird analog zu Java eine Klasse erstellt, die von Application erbt.

// File: eu.derprogrammierer.jfx.FxApp.scala
package eu.derprogrammierer.jfx

import javafx.application.Application
import javafx.stage.Stage

class FxApp extends Application {
override def start(primaryStage: Stage): Unit = {
primaryStage.setTitle("Fx App")
primaryStage.show()
}
}

In diesem Beispiel wird der Fenstertitel gesetzt und das Fenster geöffnet - später werden wir es auch noch mit Inhalt füllen.

In einer üblichen JavaFX Anwendung gibt es zudem eine statische main()-Funktion. Da Scala aber keine statischen Funktionen kennt, werden wir stattdessen ein Companion Object für die FxApp-Klasse erstellen. Dabei ist zu beachten, dass ein Companion Object nicht automatisch die Funktionen der Basisklasse implementiert. Daher funktioniert die folgende direkte Übersetzung des normalen Java-Codes nicht.

object FxApp extends Application {
def main (args: Array[String]): Unit = {
launch(args:_*) // Cannot resolve symbol launch
}
}

Glücklicherweise bietet Application eine Funktion an, die für unsere Zwecke genutzt werden kann. Sie erlaubt es uns, launch() auf der Basisklasse aufzurufen und den Typ unserer FxApp anzugeben.

// File: eu.derprogrammierer.jfx.FxApp.scala

...

object FxApp {
def main (args: Array[String]): Unit = {
Application.launch(classOf[FxApp], args:_*)
}
}

Die Applikation kann nun gestartet werden und zeigt ein leeres JavaFX(!) Fenster. Das ist schön ung gut, aber wir wollen das Fenster noch mit leben füllen und dann war da auch noch die Sache mit FXML, nicht oder?

Nun, bevor wir uns mit FXML beschäftigen können, müssen wir aber einen Controller anlegen, den wir dann in der View nutzen können. Eine einfache Controller-Implementierung ist der Java-Version sehr ähnlich.

// File: eu.derprogrammierer.jfx.view.MainViewController.scala
package eu.derprogrammierer.jfx.view

import javafx.fxml.FXML
import javafx.scene.control.{Label}

class MainViewController {
@FXML private var testLabel: Label = _

def initialize(): Unit = {
testLabel.setText("Initialized!")
}

def handleTest(): Unit = {
testLabel.setText("Test successful!")
}
}

Wie man sieht, wird die View ein Label "testLabel" beinhalten. Dessen Text wird in der initialize()-Funktion verändert. Zudem gibt es den Handler handleTest(), der den Text erneut verändert. Dieser soll durch einen Button ausgelöst werden.

IntelliJ IDEA meint man könnte testLabel zu einem val umwandeln, allerdings funktioniert die JavaFX Injection dann nicht mehr. Zudem sei an dieser Stelle erwähnt, dass es in Scala nicht möglich ist, das @FXML Attribut für Felder wegzulassen, wenn man sie public macht. Das ist der Fall, weil Scala für ein public Field in Wirklichkeit Getter- und Setter-Methoden implementiert, die auf ein privates Feld zugreifen. Dieser für normale Programmierung unmerksame Unterschied hat bei Reflection, wie sie für FMXL genutzt wird, große Auswirkungen!

Main.fxml in SceneBuilder

Da unser Controller jetzt fertig ist, können wir die View erstellen. Hierzu wird im Package "view" eine Datei "MainView.fxml" angelegt. Im SceneBuilder wird eine AnchorPane in das leere Fenster gezogen. Mittig in diese werden nun ein Button und ein Label platziert.

Action handleTestLabel fxid

Der Button erhält die Action "handleTest", das Label wird mit der fx:id "testLabel" versehen.

MainView Controller ClassAußerdem möchte unsere View noch einen Controller haben. Wir geben den gesamten Namen unseres Controllers in das Feld Controller class mit.

Was jetzt noch bleibt, ist das Laden der View in unserer FxApp-Klasse. Dazu wird die start()-Funktion wie folgt angepasst:

override def start(primaryStage: Stage): Unit = {
primaryStage.setTitle("Fx App")

val fxmlLoader = new FXMLLoader(getClass.getResource("view/MainView.fxml"))
val mainViewRoot: Parent = fxmlLoader.load()

val scene = new Scene(mainViewRoot)
primaryStage.setScene(scene)
primaryStage.show() }

Dazu sind weitere Imports erforderlich:

import javafx.fxml.FXMLLoader
import javafx.scene.{Scene, Parent}

Wird das Programm jetzt gestartet, sieht man, dass das Label korrekt initialisiert wurde. Ein Klick auf den Button ändert den Text des Labels auf "Test successful!". Wahrlich ein erfolgreicher Test!

Wir haben nun eine vollwertige JavaScalaFX Applikation. Hieraus eine "Hello World!"-App zu machen, überlasse ich als Übung dem Leser.

24. June 2015

Freie Vektor-Icons in Material Design

Materialdesignicons.com

Ehrlich gesagt kann ich es kaum abwarten, dass 4k-Monitore endlich überall verwendet werden. Allerdings bringen hochauflösende Bildschirme, wie sie heute auch in den meisten Smartphones eingesetzt werden, eine große Herausforderung mit sich: freie Skalierbarkeit, um jeder Auflösung gerecht zu werden. Gerade in Hinblick auf Icons haben viele Anwendungen hier noch Defizite.

Allen, die Vector-Icons suchen, welche sich perfekt an jede Auflösung und DPI anpassen, kann ich materialdesignicons.com empfehlen. Dort findet sich eine große Auswahl an Icons für die häufigsten Applikationsfunktionen, Softwares und mehr. Alle Icons stehen in verschiedenen Formaten zur Verfügung, so bspw. SVG, Web-Font und sogar XAML.

Es gibt verschiedene Wege ein XAML-Icon in einem Programm einzubinden (marterialdesignicons.com zeigt einen davon). Allerdings ist der beste Weg meiner Meinung nach die Erstellung eines ResourceDictionaries, in das jedes Icon als DrawingImage eingefügt wird, das ein GeometryDrawing enthält.

<DrawingImage x:Key="IconSave">
  <DrawingImage.Drawing>
    <DrawingGroup>
      <DrawingGroup.Children>
        <GeometryDrawing Brush="Black"
                                        Geometry="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z" />
      </DrawingGroup.Children>
    </DrawingGroup>
  </DrawingImage.Drawing>
</DrawingImage>

Das Schöne an diesem Vorgehen ist die Einfachheit, mit der sich das Icon überall in der GUI einfügen lässt:

<MenuItem Name="Save"
Header="{x:Static ms:Resources.MainMenu_Save}">
<MenuItem.Icon>
<Image Source="{StaticResource IconSave}" />
</MenuItem.Icon>
</MenuItem>

Um den Pfad für das GeometryDrawing aus dem XAML Icon zu kriegen, macht man einen Rechtsklick auf das jeweilige Icon in der Vorschau von materialdesignicons. Der Menüpunkt "View XAML" zeigt den XAML-Code zum Einbinden des Icons an, aus dem das Data-Attribut des Path einfach herauskopiert werden kann.

<Viewbox Width="48" Height="48">
<Canvas Width="24" Height="24">
<Path Data="M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z" Fill="Black" />
</Canvas>
</Viewbox>

Zur Verwendung der Web-Font gibt es eine sehr gute Anleitung unter materialdesignicons.com/getting-started.

Zum Zeitpunkt der Erstellung dieses Artikels, werden alle Icons auf materialdesignicons.com unter der SIL Open Font License, Version 1.1 angeboten.

31. January 2015

Texturing Workshop mit Jamin Shoulet

Texturing Workshop mit Jamin Shoulet (Youtube)

Wer sich für das Zeichnen von Texturen für Spiele interessiert, dem kann ich den zweiteiligen Texturing Workshop mit Jamin Shoulet (u.a. Blizzard) empfehlen. Bereitgestellt von der Academy of Art University.

Jamin zeigt hier wie man einige grundlegende Texturen (bspw. Stein und Holz) erstellen kann und erklärt sehr gut, worauf man besonders achten muss, und warum. Der Fokus liegt bei diesem Workshop auf stilisierter (also Comic-artiger) Darstellung, wie man sie mittlerweile bei vielen Spielen findet.

Texturing Workshop mit Jamin Shoulet Pt. 1 (Youtube)

Texturing Workshop mit Jamin Shoulet Pt. 2 (Youtube)

Wie im professionellen Umfeld üblich, wird in den Videos Photoshop eingesetzt, allerdings lassen sich die Hauptpunkte des Workshops auch mit jedem anderen brauchbaren Zeichenprogramm wir GIMP oder Paint.NET nachvollziehen. Ein Zeichentablet ist ebenfalls hilfreich, aber nicht zwingend notwendig (auch wenn Zeichnen deutlich mehr Spaß mit einem Tablet macht).