http://www.slideshare.net/fullscreen/javafxpert/scratching-thesurfacewithjavafx-23349729/2
IntelliJ IDEA 13
Example for JavaFX CubicCurve with animation
import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.application.Application; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.scene.shape.CubicCurve; import javafx.scene.shape.Line; import javafx.stage.Stage; import javafx.util.Duration; public class CubicCurveDemo extends Application { @Override public void start( Stage stage ) { double startX = 200, startY = 200; DoubleProperty controlX1 = new SimpleDoubleProperty( 20 ); DoubleProperty controlY1 = new SimpleDoubleProperty( 20 ); DoubleProperty controlX2 = new SimpleDoubleProperty( 400 ); DoubleProperty controlY2 = new SimpleDoubleProperty( 20 ); double endX = 300, endY = 200; // Linie von [controlX1, controlY1] nach [startX, startY] Line line1 = new Line( 0, 0, startX, startY ); line1.startXProperty().bind( controlX1 ); line1.startYProperty().bind( controlY1 ); line1.setStrokeWidth( 2 ); // Linie von [controlX2, controlY2] nach [endX, endY] Line line2 = new Line( 0, 0, endX, endY ); line2.startXProperty().bind( controlX2 ); line2.startYProperty().bind( controlY2 ); line2.setStrokeWidth( 2 ); // Animierte Kontrollpunkte Timeline timeline = new Timeline( new KeyFrame( Duration.millis( 1000 ), new KeyValue( controlX1, 300 ), new KeyValue( controlY2, 300 ) ) ); timeline.setCycleCount( Timeline.INDEFINITE ); timeline.setAutoReverse( true ); timeline.play(); CubicCurve curve = new CubicCurve( startX, startY, 0, 0, 0, 0, endX, endY ); curve.controlX1Property().bind( controlX1 ); curve.controlY1Property().bind( controlY1 ); curve.controlX2Property().bind( controlX2 ); curve.controlY2Property().bind( controlY2 ); curve.setFill( null ); curve.setStroke( Color.BLUEVIOLET ); curve.setStrokeWidth( 3 ); stage.setScene( new Scene( new Group( line1, line2, curve ), 450, 300 ) ); stage.show(); } public static void main( String[] args ) { launch( args ); } }
Update von FXForm2
Siehe http://dooapp.github.io/FXForm2/, Änderungen der Updates bei http://blog.dooapp.com/2013/11/fxform-two-releases-and-lot-of-new.html.
Inselraus: Swing/AWT-Bilder im Speicher erzeugen
Nicht immer kommen die Bilder vom Datensystem oder aus dem Internet. Mit der Java-Bibliothek lassen sich einfach auch eigene (Buffered)Image-Objekte anlegen. Dazu bieten sich – wieder historisch bedingt – verschiedene Varianten an:
- Jede AWT-Komponente, wie Frame oder Panel, bietet die Methode createImage(…). Die Anweisung Image image = panel.createImage(800, 600); erzeugt ein Image-Objekt mit 800 Pixeln in der Breite und 600 in der Höhe, das mit getGraphics() Zugriff auf den Grafikkontext bietet. Wenn die AWT-Komponente noch nicht angezeigt wurde, liefert createImage(…) die Rückgabe null, sodass hier leicht eine NullPointerException entstehen kann. Auch unterstützen die Bilder keine Transparenz.
- Java 1.2 führte die Klasse BufferedImage ein, die eine Erweiterung der Image-Klasse ist. Image ist eine abstrakte Klasse und BufferedImage eine konkrete nicht abstrakte Unterklasse. Ein zentraler Unterschied ist, dass der Zugriff auf die Pixel von BufferedImage-Objekten einfach ist, weil sie auf der Java-Seite in Byte-Arrays gespeichert sind, dass aber der Zugriff auf die Pixel von Image-Objekten schwierig ist, da Image-Objekte vom Betriebssystem kommen. Bei BufferedImage ist die Manipulation der Pixel einfach. Die Klasse bietet drei Konstruktoren. Beim Erzeugen ist immer ein Bildtyp anzugeben, der über die physikalische Speicherung bestimmt.[1]
- createCompatibleImage(…) über GraphicsConfiguration erzeugt ein BufferedImage und benötigt keinen Bildtyp.
BufferedImage erzeugen lassen
Ein Bild über createCompatibleImage(…) zu erzeugen, hat den großen Vorteil, dass das Daten- und Farbmodell optimal gewählt ist. Der einzige Nachteil dieser Methode ist die große Menge an benötigten Hilfsobjekten – was zusätzliche Schreibarbeit bedeutet:
GraphicsConfiguration gfxConf = GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().getDefaultConfiguration();
int width = 600, height = 400;
BufferedImage image = gfxConf.createCompatibleImage( width, height );
Neben createCompatibleImage(int, int) gibt es auch eine Variante, die die Angabe einer Transparenz ermöglicht.
abstract class java.awt.GraphicsConfiguration
- abstract BufferedImage createCompatibleImage(int width, int height)
Erzeugt ein BufferedImage. - BufferedImage createCompatibleImage(int width, int height, int transparency)
Erzeugt ein BufferedImage mit optionaler Transparenz. Das Argument für transparency kann sein: Transparency.OPAQUE (keine Transparenz, der Alpha-Wert ist 1,0), Transparency.BITMASK (Bilddaten sind komplett sichtbar, also opak mit Alpha-Wert 1, oder transparent, also Alpha-Wert 0), Transparency.TRANSLUCENT (Grafik erlaubt das Durchscheinen mit Alpha-Werten von 0,0 bis 1,0).
Das Bild bemalen
Image-Objekte (BufferedImage ist eine Unterklasse) geben über getGraphics() das Graphics-Objekt zurück, mit dem sich das Bild bemalen lässt. Im Fall eines speziellen BufferedImage-Objekts ist es jedoch üblich, die Methode createGraphics() einzusetzen, da sie ein Graphics2D-Objekt – eine Unterklasse von Graphics – liefert, mit dem weitere Zeichenoperationen möglich sind. Außerdem ruft getGraphics() sowieso createGraphics() auf …
Beispiel: Initialisiere ein Bild img mit weiß.
Graphics2D g = img.createGraphics();
g.setColor( Color.WHITE );
g.fillRect( 0, 0, b – 1, h – 1 );
Alternativ kann zum Löschen des Hintergrunds auch g.setBackground(Color.WHITE); g.clearRect(…); verwendet werden.
BufferedImage von Hand erzeugen
Der Konstruktor der Klasse BufferedImage wird mit den Maßen parametrisiert und zusätzlich mit einem Speichermodell für die Bildinformationen. Das ermöglicht die Verwendung von beliebigen Farb- und Speichermodellen:
int h = 400,
b = 600;
BufferedImage img = new BufferedImage( b, h, BufferedImage.TYPE_INT_RGB );
Das notwendige dritte Argument kennzeichnet den Speichertyp; hier sind die Farben durch je 8 Bit Rot, Grün und Blau abgebildet. Um weitere 2 der über 10 Bildtypen zu nennen: TYPE_USHORT_GRAY (Graubilder) oder TYPE_INT_ARGB (RGB mit jeweils 8 Bit sowie Alpha).
class java.awt.image.BufferedImage
extends Image
implements RenderedImage, Transparency, WritableRenderedImage
- BufferedImage(int width, int height, int imageType)
Liefert ein neues Hintergrundbild mit den gegebenen Maßen.
[1] Details finden Sie unter http://weblogs.java.net/blog/chet/archive/2004/08/toolkitbuffered.html.
Inselraus: Neue TrueType-Fonts in AWT/Swing nutzen
Die auf allen Systemen vordefinierten Standardzeichensätze sind etwas dürftig, obwohl die Font-Klasse selbst jeden installierten Zeichensatz einlesen kann. Da ein Java-Programm aber nicht von der Existenz eines bestimmten Zeichensatzes ausgehen kann, ist es praktisch, einen Zeichensatz mit der Installation auszuliefern und dann diesen zu laden; das kann die Font-Klasse mit der statischen Methode createFont(…) sein. Aus einem Eingabestrom liest die Methode den TrueType-Zeichensatz und erstellt das entsprechende Font-Objekt, Bsp.:
Font font = Font.createFont( Font.TRUETYPE_FONT,
getClass().getResourceAsStream( „/NASALIZA.TTF“) );
Das erste Argument ist immer Font.TRUETYPE_FONT. Das zweite Argument bestimmt den Eingabestrom zur Binärdatei mit den Zeichensatzinformationen. Die Daten werden ausgelesen und zu einem Font-Objekt verarbeitet.
Waren die Beschreibungsinformationen in der Datei ungültig, so erzeugt die Font-Klasse eine FontFormatException(„Unable to create font – bad font data“). Dateifehler fallen nicht darunter und werden extra über eine IOException angezeigt. Der Datenstrom wird anschließend nicht wieder geschlossen.
An dieser Stelle verwundert es vielleicht, dass die Arbeitsweise der statischen Methode createFont(…) der des Konstruktors ähnlich sein müsste, aber der Parameterliste die Attribute fehlen. Das liegt daran, dass die Methode automatisch einen Zeichensatz der Größe 1 im Stil Font.PLAIN erzeugt. Um einen größeren Zeichensatz zu erzeugen, müssen wir ein zweites Font-Objekt anlegen, was am einfachsten mit der Methode deriveFont(…) geschieht.
class java.awt.Font
implements Serializable
- static Font createFont(int fontFormat, InputStream fontStream)
throws FontFormatException, IOException
Liefert ein neues Zeichensatzobjekt in der Größe von einem Punkt und mit keinem besonderen Stil.
Soll nicht direkt der Font verwendet werden, sondern soll der Zeichensatz unter seinem Namen in den Namensraum gelegt werden, sodass er später auch über den Font-Konstruktor gefunden werden kann, lässt er sich mit registerFont(Font) anmelden. Das sieht etwa so aus:
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont( font );
Node.js bei PayPal, Java geht
toString(), toGenericString(), getCanonicalName() in Class
Die Klasse Class überschreibt die Methode toString() und greift dabei auf getName() zurück. toString() fügt zusätzlich Informationen über die Art des repräsentierten Typs (normale Klasse, Schnittstelle oder primitiver Datentyp) ein. Neu in Java 8 ist toGenericString(), was auch noch in spitzen Klammern die Typvariablen anzeigt. (Natürlich nicht den Typparameter, da der zur Laufzeit wegen der Typlöschung nicht bekannt ist.) Um die Typvariable zu erfragen wird getTypeName() verwendet; die Methode ist eine Implementierung aus der Schnittstelle Type, die Class implementiert.
Beispiel: Teste für ein Class-Objekt die drei String-Repräsentationen:
Class<?> c = HashMap.class;
System.out.println( c.getName() ); // java.util.HashMap
System.out.println( c.toString() ); // class java.util.HashMap
System.out.println( c.toGenericString() ); // public class java.util.HashMap<K,V>
Bei inneren Typen trennt ein $ bei der String-Repräsentation den äußeren und inneren Typ. Anders verhält sich getCanonicalName(), wie das Beispiel zeigt:
Anweisung |
Rückgabe |
Map.Entry.class.getName() |
java.util.Map$Entry |
Map.Entry.class.getCanonicalName() |
java.util.Map.Entry |
Map.Entry.class.toString() |
interface java.util.Map$Entry |
Map.Entry.class.toGenericString() |
public abstract static interface java.util.Map$Entry<K,V> |
String-Repräsentation bei inneren Typen
Inselraus: Punkt in einer Form, Schnitt von Linien, Abstand Punkt/Linie
Die unterschiedlichen Klassen für die geometrischen Formen aus dem Java 2D-Paket besitzen Methoden, um zum Beispiel festzustellen, ob ein Punkt in einer Form liegt.
interface java.awt.Shape
- boolean contains(int x, int y )
- boolean contains(Point2D p)
Liefert true, wenn der Punkt in der Form liegt. - boolean contains(int x, int y, int w, int h)
- boolean contains(Rectangle2D r)
Liefert true, wenn das beschriebene Rechteck komplett in der Form liegt.
Besonders praktisch ist die Methode contains(…) für Polygone.[1] Sie arbeitet aber nur korrekt für Punkte innerhalb der eingeschlossenen Fläche. Bei Abfrage von Punkten, die den Eckpunkten entsprechen, kommen immer sehr willkürliche Werte heraus – und genauso bei der Abfrage, ob die Punkte auf der Linie zum Innenraum gehören oder nicht.
Die Klasse Point2D berechnet den Abstand zweier Punkte mit den Methoden:
- double distance(double PX, double PY)
- static double distance(double X1, double Y1, double X2, double Y2)
- double distance(Point2D pt)
- double distanceSq(double PX, double PY)
- static double distanceSq(double X1, double Y1, double X2, double Y2)
- double distanceSq(Point2D pt)
Verwandte Methoden zur Berechnung des Abstands eines Punktes zur Line bietet auch Line2D:
- double ptLineDist(double PX, double PY)
- static double ptLineDist(double X1, double Y1, double X2, double Y2, double PX, double PY)
- double ptLineDist(Point2D pt)
- double ptLineDistSq(double PX, double PY)
- static double ptLineDistSq(double X1, double Y1, double X2, double Y2, double PX, double PY)
- double ptLineDistSq(Point2D pt)
- double ptSegDist(double PX, double PY)
- static double ptSegDist(double X1, double Y1, double X2, double Y2, double PX, double PY)
- double ptSegDist(Point2D pt)
- double ptSegDistSq(double PX, double PY)
- static double ptSegDistSq(double X1, double Y1, double X2, double Y2, double PX, double PY)
- double ptSegDistSq(Point2D pt)
Die relativeCCW(…)-Methoden von Line2D können herausfinden, ob der Punkt rechts oder links einer Linie liegt. Ob sich zwei Linien schneiden, ermitteln zwei überladene Line2D-Methoden intersectsLine(…). Neben der Objektmethode testet die mit acht Parametern gesegnete statische Methode linesIntersect(…), ob zwei Liniensegmente sich schneiden. Zwei allgemeine intersects(…)-Methoden deklariert die Schnittstelle Shape, doch bei diesen Methoden aus Line2D geht es darum, ob eine Form ein Rechteck schneidet. intersectsLine(…) bietet auch Rectangle2D und meldet damit, ob ein Rechteck eine Linie schneidet.
Genau das Gegenteil vom Schnitt ist die Vereinigung. So legt die Methode union(Rectangle2D src1, Rectangle2D src2, Rectangle2D dest) von Rectangle2D zwei Rechtecke zusammen, wobei ein neues Rechteck entsteht, das die äußersten Koordinaten der beiden Ursprungsrechtecke besitzt. Die Methode outcode(double, double) ist ebenfalls interessant, da sie über eine Bit-Maske in der Rückgabe angibt, wo ein außerhalb des Rechtecks befindlicher Punkt steht, also etwa OUT_BOTTOM, OUT_LEFT, OUT_RIGHT, OUT_TOP.
[1] Ob ein Punkt im Polygon ist, entscheidet der Gerade/Ungerade-Test (http://en.wikipedia.org/wiki/Point_in_polygon).
Most Popular Code Libraries Java Developers Use Today
Der Array-Index beginnt bei 0. Schon immer so?
Eine schöne Geschichte dazu bereitet ein Blog-Post http://exple.tive.org/blarg/2013/10/22/citation-needed/ auf.
Alle Neuerungen von Java 8 über die Javadoc finden
Im folgenden Beispiel wollen wir ein kleines Doclet schreiben, das Klassen, Methoden und Konstruktoren ausgibt, die das Tag @since 1.8 (bzw. @since 8, was aber eigentlich falsch ist) tragen. So lässt sich leicht ermitteln, was in der Version Java 8 alles hinzugekommen ist. Doclets werden normalerweise von der Kommandozeile aufgerufen und dem javadoc-Tool übergeben. Unser Programm vereinfacht das, indem es direkt das Tool über Java mit dem passenden Parameter aufruft. tools.jar muss dafür im Klassenpfad sein und die Dokumentation ausgepackt am angegeben Ort.
package com.tutego.tools.javadoc; import java.util.*; import java.util.function.Predicate; import com.sun.javadoc.*; import com.sun.tools.javadoc.Main; public class SinceJava8FinderDoclet { public static boolean start( RootDoc root ) { for ( ClassDoc clazz : root.classes() ) processClass( clazz ); return true; } private static void processClass( ClassDoc clazz ) { Predicate<Tag> isJava18 = tag -> tag.text().equals( "8" ) || tag.text().equals( "1.8" ); if ( Arrays.stream( clazz.tags( "since" ) ).anyMatch( isJava18 ) ) System.out.printf( "Neuer Typ %s%n", clazz ); for ( MethodDoc method : clazz.methods() ) if ( Arrays.stream( method.tags( "since" ) ).anyMatch( isJava18 ) ) System.out.printf( "Neue Methode %s%n", method ); for ( ConstructorDoc constructor : clazz.constructors() ) if ( Arrays.stream( constructor.tags( "since" ) ).anyMatch( isJava18 ) ) System.out.printf( "Neuer Konstruktor %s%n", constructor ); for ( FieldDoc field : clazz.fields() ) if ( Arrays.stream( field.tags( "since" ) ).anyMatch( isJava18 ) ) System.out.printf( "Neues Attribut %s%n", field ); } public static void main( String[] args ) { String[] params = { "-quiet", "-XDignore.symbol.file", "-doclet", SinceJava8FinderDoclet.class.getName(), "-sourcepath", "C:/Program Files/Java/jdk1.8.0/src/", // "java.lang", // Nur java.lang "-subpackages", "java:javax" // Alles rekursiv unter java.* und javax.* }; Main.execute( params ); } }
Unsere main(String[])-Methode ruft das JDK-Doclet-Programm über Main.execute(String[]) auf und übergibt die eigene Doclet-Klasse per Parameter – die Argumente von execute(String[]) erinnern an die Kommandozeilenparameter. Das Doclet-Hauptprogramm wiederum ruft unsere start(RootDoc root)-Methode auf – das Gleiche würde auch passieren, wenn das Doclet von außen über javadoc aufgerufen würde. Unser start(RootDoc) läuft über alle ermittelten Typen und übergibt zum Abarbeiten der Innereien die Verantwortung an processClass(ClassDoc). Die Metadaten kommen dabei über diverse XXXDoc-Typen. Ein Precidate zieht den Tag-Test heraus, ein weiterer Einsatz der neuen Java 8 Streams würde das Programm nicht übersichtlicher machen.
Das Programm nutzt ein paar Tricks, um die Ausgabe auf das Wesentliche zu konzentrieren. Der Schalter –quit schaltet den üblichen Ladestatus, der zu Ausgaben wie
Loading source files for package java.lang…
Loading source files for package java.applet…
Loading source files for package java.awt…
führt ab.
Der Schalter -XDignore.symbol.file wiederum unterdrückt Meldungen wie diese hier:
C:\…\src\java\lang\Class.java:57: warning: Unsafe is internal proprietary API and may be removed in a future release
import sun.misc.Unsafe;
^
Die Meldungen landen auf dem System.err-Kanal, sodass sie sich auch mit System.setErr(…) in einem ausgabeverwerfenden Strom geschickt werden können um sie zu unterdrücken.
Offtopic: Unterstützt Ärzte ohne Grenzen …
GWT 2.6.0 (RC1)
Siehe http://www.gwtproject.org/release-notes.html#Release_Notes_2_6_0_RC1:
Freuen tue ich mich insbesondere über die Unterstützung von Java 7.
JavaFX vertical text scroller in effectively 4 lines
import javafx.animation.*; import javafx.application.Application; import javafx.scene.*; import javafx.scene.text.Text; import javafx.stage.Stage; import javafx.util.Duration; public class FxScroller extends Application { @Override public void start( Stage stage ) { Text text = new Text( 600, 20, "It is a period of civil war. Rebel spaceships, striking from a hidden base, have won their first victory against the evil Galactic Empire." ); new Timeline( new KeyFrame( Duration.seconds( 20 ), new KeyValue( text.xProperty(), -text.getBoundsInLocal().getWidth() - 1) ) ) .play(); stage.setScene( new Scene( new Group( text ), 600, 30 ) ); stage.show(); } public static void main( String[] args ) { launch( args ); } }
Thema der Woche: JSON-Verarbeitung
JSON ist ein beliebtes Format für Web-Services geworden, ließ die Einführung unter http://www.json.org/. Was ergeben die Diskussionen unter http://stackoverflow.com/questions/3536893/what-are-the-pros-and-cons-of-xml-and-json für Vor-/Nachteile?
Für Java gibt es diverse Bibliotheken und seit Java EE 7 ist ein Reader/Writer auch Teil vom Standard. In der Regel werden Autoren aber zu externen Libs greifen, etwa http://www.json.org/java/index.html, https://github.com/FasterXML/jackson oder http://jettison.codehaus.org/.
Beziehe das Jar für json.org, verstehe das Beispiel http://www.concretepage.com/java/example_json_java und lasse es laufen.
Mache dich kurz mit http://www.geojson.org/ vertraut. Was sagt die http://www.geojson.org/geojson-spec.html zum Unterschied von Polygon und MultiPolygon?
http://www.mapfish.org/svn/mapfish/contribs/java-geojson/ ist eine Bibliothek für das GeoJSON-Format; intern baut es auf json.org auf. Binde die Bibliothek ein und beziehe über vom Webserver http://openstreetmap.us/~migurski/vector-datasource/ eine JSON-Datei und gib die erkannten Geo-Objekte auf der Konsole aus. Für http://tile.openstreetmap.us/vectiles-water-areas/12/656/1582.json soll zum Beispiel so etwas erscheinen:
MultiPolygon mit Koordinaten [-122.30168,37.85751],[-122.30029,37.85071],[-122.30105,37.84776],[-122.30078,37.84630],[-122.30241,37.84564],[-122.29997,37.84620], … Polygon mit Koordinaten [-122.34366,37.85751],[-122.34366,37.78808],[-122.34375,37.78808], …
Kommerzielle Unterstützung für GlassFish läuft aus
Quelle:
Bemerkungen dazu unter
- http://www.heise.de/newsticker/meldung/Oracle-stellt-kommerziellen-GlassFish-Anwendungsserver-ein-2039763.html
- http://blog.c2b2.co.uk/2013/11/oracle-dropping-commercial-support-of.html
- http://www.heise.de/developer/meldung/GlassFish-Ein-Nekrolog-fuer-Oracles-Open-Source-Anwendungsserver-2039780.html
Oracle handelt in meinen Augen (wieder einmal) aus rein ökonomischen Interessen; es geht nicht gut, zwei Produkte in einem Marktsegment zu positionieren, die Geschichte hatten wir schon mit Oracle Database und MySQL.
Möglicherweise wird GlassFish (und die assoziierten Java EE-Teile, wie Web-Services, OR-Mapper) eine Spielwiese für Technologien, die dann in WebLogic übertragen werden, wenn sie funktionieren, und dort dann exklusiv verbessert und optimiert.
Ob ich Kunden nun zu GlassFish raten würde? Eher nicht. Bis gestern war ich absoluter GF-Fan und habe den Server immer empfohlen, auch gegen den starken JBoss, doch wenn kein kommerzieller Support vorhanden ist, werden große Unternehmen ihn schlicht nicht einsetzen wollen.
Bye Bye GF
Besonderheit bei vererbten Konstruktoren mit Ausnahmen
Implementiert eine Unterklasse einen eigenen Konstruktor, und ruft dieser super(…) für einen Konstruktor auf, der eine Ausnahme auslöst, so muss auch der Konstruktor der Unterklasse diese Ausnahme melden, denn der neue Konstruktor kann die Ausnahme nicht auffangen. In unsere Beispiel wäre also
public SubRandomAccessFile( File file, String mode ) {
try {
super( file, mode );
} catch ( Exception e ) { }
}
illegal. Der Grund ist ganz einfach: Wenn der Konstruktor der Oberklasse eine Ausnahme auslöst, ist das Objekt nicht vollständig initialisiert. Und wenn der Konstruktor der Unterklasse dann die Ausnahme abfängt, würde ja die Unterklasse vielleicht nicht vollständig initialisierte Eigenschaften der Oberklasse erben, also ein halbgares Objekt. Das ist unerwünscht.