Intechcore auf dem 25. Deutscher EDV-Gerichtstag 2016. Gemeinsam mit unseren Partnern im Bereich Software für die Justiz haben wir die zukünftige Lösung forumSTAR-Text Modernisierung präsentiert.
In diesem Beitrag möchten wir einige Vorteile der JavaFX-Bibliothek erläutern, die besonders im Bezug auf das Userinterface eine Rolle spielt.
Als einer der wichtigen Vorteile der JavaFX Bibliothek gilt die Möglichkeit der Nutzung von Styles (CSS), die uns aus dem WWW bekannt sind.
Dadurch verliert sich die Abhängigkeit zu der Bibliothek Look & Feel. Man kann jedoch selbst das Aussehen der Applikation bestimmen, was auch ziemlich flexibel und schön möglich ist. Die Gestaltung kann dynamisch erfolgen, eine Animation beinhalten oder auch 3D Grafik.
Durch die Benutzung der Style-Konzeption ist es nun möglich, die Applikationen mit so genannten „Skins“ zu erstellen, durch die das Aussehen der Applikation komplett von der Businesslogik losgelöst wird und mehr Individualtät erhält. Solche individuellen Skins können sogar separat von einem Designer erstellt werden.
Erstellen wir ein einfaches Beispiel eines Dialogfensters mit einem Button:
public class JavaFXDialog1 extends Application { @Override public void start(Stage stage) { final VBox vbox = new VBox(); final Button button = new Button("test"); vbox.getChildren().addAll(button); final Scene scene = new Scene(vbox, 150, 100); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); }
Styles kann man unterschiedlich anwenden:
1) Unmittelbar im Code, um z.B. die Schriftfarbe in dem Button zu ändern:
button.setStyle("-fx-text-fill: red");
2) Mit Hilfe einer CSS-Datei, auf die die Klasse Scene ausgerichtet werden soll:
Dafür wird eine Datei mit der Erweiterung .css erstellt und unter dem Projektverzeichnis abgelegt, z.B. /css/styles.css.
Inhalt der Datei:
.button { -fx-text-fill: blue; }
Dabei ist es sehr wichtig, die Entwicklungsumgebung so einzurichten, dass sie diese CSS-Dateien beim Bilden der Applikation auch mitkopiert.
Unter IntelliJ IDEA wird es beispielsweise folgendermaßen gemacht:
Nun ist alles fertig, um die Style-Datei einzubinden:
scene.getStylesheets().add((getClass().getResource("/css/styles.css")).toExternalForm());
Wir starten das Projekt und bekommen folgendes Dialogfenster:
Instruktion .button in der CSS-Datei sagt aus, dass nun alle Knöpfe eine blaue Schriftfarbe haben werden:
final Button button1 = new Button("button1"); final Button button2 = new Button("button2"); vbox.getChildren().addAll(button1, button2);
Und was, wenn das nicht das ist, was wir brauchen? Was, wenn wir einen konkreten Knopf definieren wollen?
3) Abhilfe schafft die userbezogene Definition des Knopf-Styles:
In styles.css schreiben wir:
.button1 { -fx-text-fill: green; }
Und im Code:
button1.getStyleClass().add("button1");
Das Dialogfenster sieht so aus:
Nun haben alle Knöpfe, die mit der Style-Klasse verbunden sind, grüne Schriftfarbe, wobei die Methode add() uns dabei Hinweise gibt, dass wir mehrere von solchen Styles hinzufügen können, wodurch verschiedene Elementeigenschaften erweitert, vordefiniert oder überladen werden.
4) User-Style kann man auch durch den so genannten ID definieren:
In der styles.css schreiben wir:
#button2 { -fx-text-fill: yellow; }
Und im Code:
button2.setId("button2");
Als Ergebnis bekommen wir folgendes Dialogfenster:
D.h. alle Elemente mit der gleichen ID sehen gleich aus.
Was kann man sonst noch Interessantes mit den Styles machen?
Styles können auch die so genannten Verhaltenstrigger bearbeiten, die aus der XAML Welt kommen.
Wenn wir bei dem Beispiel mit dem Knopf bleiben, gehören zu diesen Triggern solche Ereignisse der GUI wie Fokus, Selektion, Mousedown, Mouseover usw. also alles, was man nicht im Dialogfenster haben möchte. Es beinhaltet nur den Buisinesslogin bei den kundenseitigen Änderungswünschen und es wird nur die CSS-Datei geändert und nicht die Logik.
So kann man z.B. mit der folgenden CSS-Definition die Farbe des Knopfes ändern, wenn der User mit der Maus darüber fährt.
.button:hover { -fx-background-color: orange; }
So sieht das Dialogfenster bei Mouseover aus:
Wie bereits oben erwähnt, führt das zum gleichen Verhalten von allen Button-Klassen. Mit diesem Code:
.button1:hover { -fx-background-color: orange; }
werden die Trigger nur für die Elemente angewendet, die auf die Klasse «button1» verweisen.
Diese Vorgehensweise kann man bei vielen Trigger anwenden, bei Knöpfen gibt es z.B. auch noch foused, selected oder presset.
Leider kann das nicht direkt im Code genutzt werden:
button.setStyle(":hover -fx-text-fill: red");
Vielleicht wird diese Möglichkeit in der Zukunft durch JavaFX Developer realisiert.
Wozu brauchen wir das alles? Im Internet kann man noch eine Reihe an Beispielen finden, die deutlicher sind als dieses. Das Ziel des Beitrags war nicht, diese zu kopieren. Uns geht es darum, dass die Konzeption von Syles und Trigger auch für den eigenen Bedarf erweitert werden kann, das ist für uns von Interesse.
Als Beispiel können wir folgende Ausgangssituation betrachten:
wir wollen mit JavaFX eine visuelle Komponente umsetzen, die für die Auswahl der Größe einer im Text eingefügten Tabelle dienen soll. Das Aussehen der Komponente, das Farbschema, die Größe usw., sollen dabei kein Bestandteil der Businesslogik der Komponente sein, sondern über eine externe CSS-Datei konfigurierbar sein.
Das sollte ungefähr so aussehen:
Der User geht mit dem Mauszeiger über die Tabelle und sieht seine ausgewählten Elemente, in dem Fall eine Tabelle mit 7 x 8 Zellen. Beim Klick auf die Komponente sollte die Auswahl an das Programm übermittelt werden, um eine entsprechende Tabelle einzufügen.
Sicherlich kann man auf die Auswahl über den Code reagieren, aber was wenn der eine Kunde eine bestimmte Farbe bevorzugt, der andere aber ganz andere Farben bevorzugt? Möglich ist auch, dass das Farbschema durch ein «Skin» oder sonst wie vordefiniert wird – was dann?
Hier wird nur eine Komponente für die Zellenselektion benötigt, um deren Aussehen es an dieser Stelle aber nicht gehen soll.
Hier können wir auf das vorangegangene Beispiel mit dem Trigger «hover» für den Button anknüpfen. Der wirkt allerdings für jede Zelle, wenn man mit dem Mauszeiger über sie fährt. Die Zellen, die der Mauszeiger verlassen hat, entsprechen dem Trigger nicht mehr und fallen somit aus der Auswahl. Wie können wir den gesamten Bereich ausgewählt behalten?
Für die Lösung dieser Aufgabe wird ein eigener Trigger erstellt, der auf eine bestimmte Eigenschaft des Objektes reagiert, z.B. «bin im Diapason der Selektion» oder inRange, die wir folgendermaßen in der CSS-Datei definieren können:
.MyCell:inRange { -fx-border-width: 0.5; -fx-border-color: #ffffff; -fx-background-color: lightskyblue }
An der Stelle muss man aber sagen, dass dies ist keine einfache Aufgabe ist. Zudem ist die Lösung für die JavaFX Versionen 1.7 und 1.8 gänzlich unterschiedlich. Für die Lösung in der Version 1.7. sind wir auf die Nutzung einer Menge der deprecated-Methoden angewiesen.
Für den Anfang schauen wir uns die Komponente an:
public class JavaFXDialog2 extends Application { @Override public void start(Stage stage) { final VBox vbox = new VBox(); final GridPaneEx table = new GridPaneEx(); table.init(10, 10); final Label label = new Label(); label.setMaxWidth(Double.MAX_VALUE); label.setAlignment(Pos.CENTER); label.setTextAlignment(TextAlignment.CENTER); label.setStyle("-fx-padding: 3 0 5 0"); label.textProperty().bind(table.text); vbox.getChildren().addAll(label, table); final Scene scene = new Scene(vbox, 350, 300); scene.getStylesheets().add((getClass().getResource("/css/styles.css")).toExternalForm()); scene.setFill(null); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } private void fireCreateTable(final int cols, final int rows){ System.out.println("cols = " + cols + ", rows = " + rows); } protected class GridPaneEx extends GridPane { public final StringProperty text = new SimpleStringProperty("cancel"); private int cols; private int rows; public GridPaneEx(){ this.setOnMouseExited(new EventHandler() { @Override public void handle(MouseEvent mouseEvent) { text.setValue("cancel"); deselectAll(); } }); } public void init(final int cols, final int rows){ getChildren().clear(); this.cols = cols; this.rows = rows; for (int col = 0; col < cols; col++){ for (int row = 0; row < rows; row++){ final Button rect = new Button(); rect.setMinSize(30, 10); add(rect, col, row); final int selectedCol = col; final int selectedRow = row; rect.setOnMouseMoved(new EventHandler() { @Override public void handle(MouseEvent mouseEvent) { selectRange(selectedCol, selectedRow); text.setValue((selectedCol + 1) + " x " + (selectedRow + 1)); } }); rect.setOnAction(new EventHandler() { @Override public void handle(ActionEvent actionEvent) { fireCreateTable(selectedCol + 1, selectedRow + 1); deselectAll(); } }); } } deselectAll(); } private Node getNodeFromGridPane(int col, int row) { for (Node node : getChildren()) { if (GridPane.getColumnIndex(node) == col && GridPane.getRowIndex(node) == row) { return node; } } return null; } private void selectCell(int col, int row, final boolean select){ final Node node = getNodeFromGridPane(col, row); if (select){ node.setStyle("-fx-border-width: 0.5; -fx-border-color: #ffffff; -fx-background-color: lightskyblue"); } else { node.setStyle("-fx-border-width: 0.5; -fx-border-color: #000000; -fx-background-color: #ffffff"); } } public void deselectAll(){ for (int col = 0; col < cols; col++){ for (int row = 0; row < rows; row++){ selectCell(col, row, false); } } } private void selectRange(int selectedCol, int selectedRow){ deselectAll(); for (int col = 0; col <= selectedCol; col++){ for (int row = 0; row <= selectedRow; row++){ selectCell(col, row, true); } } } } }
Besonders interessant ist die Methode selectCell, in der die Zellenfärbung unmittelbar im Code realisiert wird:
Für normale Zellen:
node.setStyle("-fx-border-width: 0.5; -fx-border-color: #000000; -fx-background-color: #ffffff");
Für Zellen im ausgewählten Bereich:
node.setStyle("-fx-border-width: 0.5; -fx-border-color: #ffffff; -fx-background-color: lightskyblue");
Weil es in der Aufgabenbeschreibung steht, dass klare Farbenzuweisung unmöglich ist, versuchen wir die mit Hilfe eines eigenen Styles in styles.css zu definieren:
#MyCellNormal { -fx-border-width: 0.5; -fx-border-color: #000000; -fx-background-color: #ffffff; } #MyCellInRange { -fx-border-width: 0.5; -fx-border-color: #ffffff; -fx-background-color: lightskyblue }
Und in der Methode selectCell:
private void selectCell(int col, int row, final boolean select){ final Node node = getNodeFromGridPane(col, row); if (select){ node.setId("MyCellNormal"); } else { node.setId("MyCellInRange"); } }
Schon besser, nicht war? D.h. wenn der Kunde mit der Farbwahl unzufrieden sein sollte, kann man sie direkt in der Datei styles.css ändern. Die Logik der Komponente bleibt dabei unverändert.
Allerdings gibt es eine noch elegantere Lösung der Aufgabe: Platzieren wir in styles.css noch zusätzlich 2 Sytles:
.MyCell { -fx-border-width: 0.5; -fx-border-color: #000000; -fx-background-color: #ffffff; } .MyCell:inRange { -fx-border-width: 0.5; -fx-border-color: #ffffff; -fx-background-color: lightskyblue }
Das bedeutet, dass die Zellen sich auf den Style «MyCell» orientieren und wenn der Trigger «inRange» greift, analog zu «hover» oder «presset», ändert sich die Farbe entsprechend.
Aber wie bringen wir der Zelle bei, den Trigger zu starten?
Da wir in unserem Beispiel für die Zellen, Button-Elemente nutzen, ist es erforderlich ihr Verhalten in der so genannten Pseudo-Klasse neu zu definieren. In JavaFX 1.7 wird das so gemacht:
protected static class RangeButton extends Button { public RangeButton(){ getStyleClass().add("MyCell"); } private BooleanProperty inRange = new BooleanPropertyBase() { @Override protected void invalidated() { impl_pseudoClassStateChanged("inRange"); } @Override public Object getBean() { return RangeButton.this; } @Override public String getName() { return "inRange"; } }; public boolean isInRange() { return inRange.get(); } public void setInRange(boolean value) { inRange.set(value); } private static final long IN_RANGE_PSEUDOCLASS_STATE = StyleManager.getInstance().getPseudoclassMask("inRange"); @Override public long impl_getPseudoClassState() { long mask = super.impl_getPseudoClassState(); if (isInRange()) mask |= IN_RANGE_PSEUDOCLASS_STATE; return mask; } }
Wie wir sehen, sind alle Methoden «…PseudoClass…» – deprecated.
Jetzt nutzen wir statt Button unseren RangeButton. Und die Methode selectCell sieht nun so aus:
private void selectCell(int col, int row, final boolean select){ final Node node = getNodeFromGridPane(col, row); ((RangeButton)node).setInRange(select); }
D.h. die Änderung der Feldeigenschaft «InRange» führt dazu, dass der Style-Trigger greift und die Farbe der ausgewählten Zellen sich entsprechend ändert.
Das ist genau das, was wir brauchen!
In JavaFX 1.7 funktioniert das. In JavaFX 1.8 ist das leider verboten. Der Code ist nicht mehr kompilierbar sobald JVM 1.8 hinzugeschaltet wird.
Was bietet uns dann die neue Version an der Stelle?
Wie bereits erwartet, wurden die deprecated Methoden entfernt und die Architektur vereinfacht. Jetzt reicht es daher aus, wenn wir folgendes tun:
protected static class RangeButton extends Button { protected final PseudoClass pcInRange = PseudoClass.getPseudoClass("inRange"); public RangeButton(){ getStyleClass().add("MyCell"); } protected final BooleanProperty inRange = new BooleanPropertyBase() { @Override protected void invalidated() { pseudoClassStateChanged(pcInRange, getValue()); } @Override public Object getBean() { return RangeButton.this; } @Override public String getName() { return "inRange"; } }; public boolean isInRange() { return inRange.get(); } public void setInRange(boolean value) { inRange.set(value); } }
Alles funktioniert wie bisher.
Man kann den Code noch etwas vereinfachen…:
protected static class RangeButton extends Button { protected final BooleanProperty inRange; public RangeButton(){ getStyleClass().add("MyCell"); final PseudoClass pcInRange = PseudoClass.getPseudoClass("inRange"); inRange = new SimpleBooleanProperty(); inRange.addListener(new ChangeListener() { @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { pseudoClassStateChanged(pcInRange, newValue); } }); }
…und die Methode entsprechend ändern:
private void selectCell(int col, int row, final boolean select){ final Node node = getNodeFromGridPane(col, row); ((RangeButton)node).inRange.setValue(select); }
Zusammenfassung: Mit Hilfe der beschriebenen Lösung auf JavaFX ist es möglich, spezielle userbezogene Eigenschaften der Komponenten zu erstellen und diese mit den Styles zu verknüpfen,. Das ist besonders bequem, wenn die Anforderung besteht, die Businesslogik komplett von der GUI zu abstrahieren.
Im August 2009 haben wir in der Ausschreibung des Projektes „Pflege und Weiterentwicklung Textsystem_RP“ des Ministeriums der Justiz Rheinland-Pfalz teilgenommen. Im November 2009 haben wir Zuschlag erteilt bekommen.
Die Intechcore GmbH mit Sitz in München wurde 2008 gegründet. Seit der Gründung begleiten wir diverse Projekte der Justiz in verschiedenen Bundesländern.
Unsere Kompetenzen:
Intechcore ist auf die Optimierung, Wartung und Neuentwicklung von Software und Softwaresystemen, sowie auf Beratungen zur Qualitätssicherung von Softwaresystemen spezialisiert.
Auf dem 22. EDV-Gerichtstag 2013 präsentiert Intechcore folgende Projekte:
- Pflege und Weiterentwicklung von forumSTAR-Text. Hier handelt es sich um ein Textverarbeitungssystem für die Justiz
- Pflege und Weiterentwicklung von web.sta- Zwischenschicht. Es geht um einen Teil der Fachanwendung web.sta, die als DMS (Dokumentenmanagementsystem) funktioniert und zusammen mit forumSTAR-Text die Abarbeitung von XML-Vorlagen und XML-Dokumente ermöglicht
- Entwicklung und Pflege von XML-Vorlagen für die staatsanwaltschaftliche Textverarbeitung des Landes Rheinland-Pfalz
Hier geht es zur Übersicht:
http://www.edvgt.de/pages/22.-deutscher-edv-gerichtstag/begleitausstellung-und-unternehmenportraets.php
Achtung, seit Januar 2016 haben wir neue Adresse:
Intechcore GmbH
Keltenring 17
82041 Oberhaching bei München
Wir freuen uns, am 23. EDV-Gerichtstag im September 2014 in Saarbrücken teilzunehmen.
In einem unsere Projekte setzten wir Scala und JavaFX ein. Bei der Qualitätssicherung wird zusätzlich scalastyle verwendet.
Unsere Firma hat vom 12.09.2012 bis 14.09.2012 am EDV-Gerichtstag 2012 in Saarbrücken teilgenommen.
Sehr geehrte Damen und Herren,
in nächster Zeit suchen wir Sie, für das Entwerfen und Verfassen von Texten in der deutschen Sprache für drei Seiten unserer Homepage (www.intechcore.de : „Home“ (ca. 80 Wörter), „Über uns“ (ca. 120Wörter) und „Service“ (ca.550 Wörter)).
Für die Erhöhung des Bekanntheitsgrades und des Firmenimages haben wir uns im Rahmen der Entwicklungsstrategie entschieden, unsere vorhandenen deutschen Texte durch neue zu ersetzen. Die neuen Texte sollen dazu dienen, den Kunden und anderen Interessenten wichtige Informationen über das Unternehmen und unsere Leistungen zur Verfügung zu stellen. Das wird für den Imageaufbau der Firma ein wichtiger Beitrag sein. Die Texte müssen auch unter Einbehaltung von SEO-Methoden geschrieben werden, also mit Rücksicht auf die Wirkung der Texte in den Suchmaschinen. Das heißt, die Benutzung der zutreffenden Keywords muss den Suchmaschinen erleichtern, unsere Webseite besser zu aufzufinden. Die Texte müssen auch komplett selbst verfasst sein. Vor der Bezahlung werden die Texte daher auf ihre Authentizität hin geprüft.
Bei Interesse Ihrerseits senden Sie uns bitte in den nächsten Tagen ein unverbindliches Kostenangebot dieses Auftrages bzw. Ihre Schätzung der Kostenhöhe und Zahlungskonditionen, mögliches Anfangsdatum und ungefähre Dauer des Auftrages, damit wir eine Entscheidung treffen könnten. Uns würden auch Beispiele oder Hinweise auf Ihre bereits geschriebene Inhalte sehr interessieren.
Kontakt:
Andrej Petrenko
Intechcore GmbH
www.intechcore.de
a.petrenko@intechcore.de
info@intechcore.de
Softwareentwickler(w/m) – Java SE/EE Vollzeit für eine Festanstellung in München gesucht. Wir brauchen Verstärkung für unser kompetentes Teams bei der Erstellung von Konzeptionen, Softwareentwicklung und Support von Softwaresystemen im Rahmen komplexer Systemumgebungen.
Was wird erwartet:
- Weiterentwicklung
- Fehlerbehebung
- Erstellung von technischen Spezifikationen
Ihr Profil:
- Hochschulabschluss in Informatik, Mathematik oder vergleichbarer Abschluss
- Mindestens dreijährige Erfahrung in der Softwareentwicklung mit Java-Projekten
- Hervorragende Kenntnisse in Java/J2EE, Servlets, JSP und XML/XSL sowie Java-Frameworks für Webanwendungen und Web-Services. Insbesonders Swing Kenntnisse.
- Eigenverantwortliches, kunden- und lösungsorientiertes Denken
- Teamfähigkeit, Flexibilität und Organisationsstärke
- kommunikative Fähigkeiten
- Eigenmotivation
Was kommt von uns
- Eine interessante und abwechslungsreiche Tätigkeit
- Die Mitarbeit in interdisziplinären Teams
- Intensive Einarbeitung, inklusive interner Schulungen
Wir freuen uns auf Ihre Bewerbungen mit frühstem Einstiegstermin und Gehaltsvorstellung per E‑Mail an jobs@intechcore.de an Hr. Smeltsov oder auf Ihre ausführlichen Bewerbungsunterlagen an folgende Anschrift:
Intechcore GmbH
z.H. Hr. Smeltsov
Wamslerstr. 2
81829 München
TELEFON
Fax
Address
Keltenring 10
82041 Oberhaching near Munich
Germany
GESCHÄFTSZEITEN
Mo-Fr: 8:00–18:00
Sa, So: Geschlossen