“Bind” components’ size to a MediaView’s size in JavaFX

The MediaView is a control allowing you to add videos and sounds to your JavaFX application. If you are playing a video, maybe you would like another control to have the same size. For example, you can have a video played next to an image that you would like to have the same height. The easy thing would like to do some property binding like this:

final MediaView mediaView = new MediaView(aPlayerInstance);
mediaView.setFitWidth(200);

final ImageView imageView = new ImageView(anImageInstance);
imageView.fitHeightProperty().bind(mediaView.heightProperty());

But this code is wrong because the MediaView doesn’t have a height property. So in order to succeed in our goal, the MediaView has a boundsInLocalProperty which can be used to change the size of the ImageView in our case. Look at this code example:

final MediaView mediaView = new MediaView(aPlayerInstance);
mediaView.setFitWidth(200);

final ImageView imageView = new ImageView(anImageInstance);

mediaView.boundsInLocalProperty().addListener(new ChangeListener<Bounds>() {
  @Override
  public void changed(ObservableValue<? extends Bounds> observableValue, Bounds bounds, Bounds bounds2) {
    imageView.setFitHeight(bounds2.getHeight());
  }
});

Of course you have to do some nullity checks to make your code more robust.

Working with NetBeans 7.3 and JavaFX 8

Maybe you want to work with the latest version of NetBeans, currently 7.3. But also with the early access of JDK 8 in order to play with the latest features of JavaFX. The thing is that NetBeans will not launch your JavaFX application, even if you are using JDK 7. Indeed you will get an exception dealing with Nashorn.

java.lang.VerifyError: Code generation bug in "runScript": likely stack misaligned: java.lang.ArrayIndexOutOfBoundsException: 0 <eval>
	at jdk.nashorn.internal.codegen.CodeGenerator.leave(CodeGenerator.java:1003)
	at jdk.nashorn.internal.ir.FunctionNode.accept(FunctionNode.java:339)
	at jdk.nashorn.internal.codegen.CompilationPhase$7.transform(CompilationPhase.java:239)
	at jdk.nashorn.internal.codegen.CompilationPhase.apply(CompilationPhase.java:372)
	at jdk.nashorn.internal.codegen.Compiler.compile(Compiler.java:263)
	at jdk.nashorn.internal.runtime.Context.compile(Context.java:758)
	at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:720)
	at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:358)
	at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:463)
	at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:451)
	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:379)
	at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:134)
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:487)
	at org.apache.tools.ant.util.ReflectUtil.invoke(ReflectUtil.java:108)
	at org.apache.tools.ant.util.ReflectWrapper.invoke(ReflectWrapper.java:81)
	at org.apache.tools.ant.util.optional.JavaxScriptRunner.evaluateScript(JavaxScriptRunner.java:103)
	at org.apache.tools.ant.util.optional.JavaxScriptRunner.executeScript(JavaxScriptRunner.java:67)
	at org.apache.tools.ant.taskdefs.optional.Script.execute(Script.java:52)
	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
	at sun.reflect.GeneratedMethodAccessor74.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:487)
	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
	at org.apache.tools.ant.Task.perform(Task.java:348)
	at org.apache.tools.ant.taskdefs.Sequential.execute(Sequential.java:68)
	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
	at sun.reflect.GeneratedMethodAccessor74.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:487)
	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
	at org.apache.tools.ant.Task.perform(Task.java:348)
	at org.apache.tools.ant.taskdefs.MacroInstance.execute(MacroInstance.java:398)
	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
	at sun.reflect.GeneratedMethodAccessor74.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:487)
	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
	at org.apache.tools.ant.Task.perform(Task.java:348)
	at org.apache.tools.ant.Target.execute(Target.java:392)
	at org.apache.tools.ant.Target.performTasks(Target.java:413)
	at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1399)
	at org.apache.tools.ant.Project.executeTarget(Project.java:1368)
	at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
	at org.apache.tools.ant.Project.executeTargets(Project.java:1251)
	at org.apache.tools.ant.module.bridge.impl.BridgeImpl.run(BridgeImpl.java:283)
	at org.apache.tools.ant.module.run.TargetExecutor.run(TargetExecutor.java:541)
	at org.netbeans.core.execution.RunClassThread.run(RunClassThread.java:153)
Caused by: java.lang.ArrayIndexOutOfBoundsException: 0
	at jdk.internal.org.objectweb.asm.Frame.merge(Frame.java:1408)
	at jdk.internal.org.objectweb.asm.Frame.merge(Frame.java:1364)
	at jdk.internal.org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1382)
	at jdk.nashorn.internal.codegen.MethodEmitter.end(MethodEmitter.java:198)
	at jdk.nashorn.internal.codegen.CodeGenerator.leave(CodeGenerator.java:1000)
	... 50 more

The problem is that NetBeans uses the most recent JDK installed on your machine to run itself. And if you look in the About NetBeans menu entry, you will see that it uses the JDK 8 if it is installed, even if your JAVA_HOME points to your JDK 7 installation.

NB_JDK7

But there is a little workaround to get things working again: you can specify, in a configuration file of NetBeans, which JDK it should use. On OSX, show the package content of NetBeans 7.3.app and go to Content > Resources > NetBeans &gt etc and open the file netbeans.cnf. In this file, locate the line containing netbeans_jdkhome and uncomment it and specify the JDK home you want NetBeans to use. For example:

netbeans_jdkhome="/Library/Java/JavaVirtualMachines/Current/Contents/Home"

Then just restart NetBeans and go the About NetBeans menu entry. You should see that your JDK 7 installation instead of JDK 8.

NB_JDK8

Now if you try to launch your JavaFX application, everything is working again.

Set custom font in JavaFX 2

Maybe you would like to set custom fonts to your application for various reasons: make it touchy, be compliant with your corporation with corporate font, and so on. In JavaFX it’s pretty easy to change the font of a component. You can use the Labeled#setFont(Font f) method in your code, or define the -fx-font-family in your CSS. But what about custom fonts?

Load the font

The first thing you have to do is load the font. This is done like this:

Font.loadFont(SomeApp.class.getResource("/com/twasyl/someapp/font/MyFONT.TTF").toExternalForm(), 12);

This loads your font, which has a family, let’s say Thierry Font. This method makes the font available in your graphics environment and you just have to use it whenever you want. I like to place this code in my Application class, especially in the init method, so the I’m sure the font is loaded before any other treatment (especially before any labeled element is rendered). Let’s write this code:

public class SomeApp extends Application {

  @Override
  public void start(Stage stage) throws Exception {
    // Do some stuff
  }

  @Override
  public void init() throws Exception {
    super.init();

    Font.loadFont(SomeApp.class.getResource("/com/twasyl/someapp/font/MyFONT.TTF").toExternalForm(), 12);
  }

  public static void main(String[] args) {
      SomeApp.launch(args);
  }
}

Use your custom font

Punctual use

Either in your FXML or CSS code, you can now define the -fx-font-family with the value Thierry Font.

<Label style="-fx-font-family: 'Thierry Font'" />

This label will use the font, but what about the others elements? Do I really have to do this on ALL elements? Of course not …

General use

The interesting part is you can define your font for the whole application by default. Indeed in your CSS you just have to do this:

.root {
  -fx-font-family: 'Thierry Font';
  -fx-font-size: 15pt;
}

By default every labeled element will use your custom font. And of course you can change the font for a particular label again, using the style attribute for example.

DrawFX – the text tool

It hasn’t be an easy task to add a text tool, but I finally did it. The tool is actually a simple text area (with some customization for hiding the scroll bars) placed into a panel with some buttons (valid and cancel). The tool is not draggable and has the following options:
- Bold
- Italic
- Underline (a bit tricky to make that one work. A solution is proposed here but I used something else)
- Size for the text
- Change the font by using fonts on your system

It is also possible to resize the text area by simply drag the mouse anywhere. Below is a little demo.

DrawFX – this is JaaavaaaFX

DrawFX has made some good progress these days. A lot of new features/improvements have been implemented like:

  • Priority for tasks: tasks now have a priority which can be LOW, NORMAL and HIGH. The colour of each sticky accords the task’s priority: green for LOW, yellow for NORMAL and orange for HIGH ;
  • It is possible to have multiple layers
  • A “Pen” tool has been added. It draws a line (composed of squares) while the mouse is dragged. The thickness can be modified using a slider ;
  • A “Rectangle” tool has been added in order to draw rectangles. The stroke’s color is the primary color, the fill’s color the secondary one. The width of the stroke can be changed using a slider ;
  • An “Ellipse” tool has been added in order to draw ellipses. The stroke’s color is the primary color, the fill’s color the secondary one. The width of the stroke can be changed using a slider ;
  • A “Shape” tool has been added in order to draw polygons. One click with the primary mouse’s button in the drawing area draws a line between the current click’s location and the previous one, while a double click closes the polygon. The stroke’s color is the primary color, the fill’s color the secondary one. The width of the stroke can be changed using a slider ;
  • A “Move” tool has been added and allows to move a layer ;
  • The “Eraser” tool now have a width and height properties ;
  • The “Bucket” tool is now able to only fill an area delimited by a bound color. For example you can change the color of a polygon by using this tool ;
  • The “Brush” tool has been improved. It now draws shapes defined by a SVG path. By default, the SVG path is a square, but can be changed to any valid path. In my screencast I use the path of a Java logo. It is also to possible to specify the scale X and Y to resize the shape represented by the path.

I still have some nice features left in my mind.

DrawFX – What you draw is what you attach

Some have guessed what I’m developing: a kind of agile board in JavaFX. Well the thing is when I have a team meeting, where we discuss about functionalities, improvements, etc, sometimes I just want to draw something to explain what I understand, what could be done and so on. Sometimes a picture is better than thousand words… So I like to draw… For DrawFX I had this idea: why not give the possibility to the user to draw his ideas and then create a task and attach the content of the drawing board to the task? And that what I did.

I still have a lot of ideas, and here it’s just a first and basic implementation, but more is to come! Here are some screenshots and a screencast.
DrawFX_01

DrawFX02

DrawFX – early task board screencast

A screencast for an early version of a task board … So for now we have a basic task board and a basic drawing board … All done in JavaFX, and it’s not going to change … More to come …

DrawFX – early stage screencast

Here is a little screencast of the really early stage of DrawFX … Teasing …

I JUG-FX, feedback on my JavaFX presentation at the ElsassJUG

It’s not very common I’m writing a post that is not about code. But today is an exception to give you a feedback on my first JavaFX presentation I gave at the ElsassJUG, the JUG located in Strasbourg, Alsace, France.

Back on stage

JavaFX_ElsassJUG_03
First of all, it feels good to be back on stage. As a former Java teacher, I really enjoy sharing my knowledges and experiences with others, having a discussion about a specific topic (essentially Java). Having others’ point of view, confront them make you grow. Having the opportunity to share them again was really great, especially when it is about a topic you really believe in: JavaFX. I really missed those feelings when you’re preparing your talk: you train a lot, ask yourself what you’re going to say and do, you have to be concise but not too much, “should I code something?”, and so on. So many feelings and questions that cheer you up. And then just before to start, in front of your attendance, you’re just a bit nervous, and now time for show! If you like challenges, it’s a good one. Back when I was a teacher, I remember almost every single moment. That was great. And yesterday at the JUG, it was great too!

Show time

JavaFX_ElsassJUG_02 My presentation was about JavaFX 2. If you’re following this blog, you won’t be surprised. Why JavaFX? Because since I discovered JavaFX 2, I’m very excited about it, and really enthusiast. I knew JavaFX 1 and I have to be honest, I was disappointed from it. I know Swing for many years and I think it suits no more the way we do UI today, in 2012. I know JSF (1 & 2) and some aspects are really great. I’ve always been someone who believes in desktop apps, despite of the great progress done in web, and all the great stuff you can do, I still believe in desktop apps. And JavaFX 2 is for me an absolutely great platform for developing desk apps in Java. Up to date in terms of development process (in comparison to Swing), easier, more powerful, UI & UX oriented, it takes nice concepts from the entire Java ecosystem. And I really wanted (still want) to share my enthusiast about it. So I did. My presentation was about an introduction to JavaFX 2, but I decided to give an overview of really important concepts like properties and bindings, as well as FXML, which for me are some really key features of JavaFX. And with which you do crazy things, believe me! And during the talk we switched from the slides to the code some times, in order to create a simple browser without pretensions, just to explore our key features. I think it’s important to explain important concepts but also to give a “real life” example. I could tell you many things about the properties during 30 minutes, but you won’t remember anything. So I kept it simple with a demo. And it’s fun to see Google and LinkedIn in our little browser. At the end we had a Q&A session which was really interesting because I felt some enthusiasm in the attendance about JavaFX and that was great! I really enjoyed the evening.

Community

As a Java developer I feel lucky. Lucky because the Java ecosystem has a great community. When you go to Devoxx, you have such a spirit that when you come back you just want to say “I wanna go back to Devoxx”. You meet people like you and I, people that love coding, love to try new things, enjoy to talk with you about the latest thing they’ve tried. Lucky because I have a JUG. A JUG is a place when you meet people that know what problems you’re facing when you’re trying to connect using a REST webservice, trying to understand why your pom.xml doesn’t work, how you could overcome a JPA problem and so on. A place where you discover so much topics from people with passion. Lucky because I know people all around the world that are as must passionate as I am, working in various companies (even at Oracle), on various projects and to who I can ask “Hey I don’t know how to do this, you have an idea?” and get an answer. That is what community is all about. The community spirit, the Java spirit!

And now?

I have another presentation about JavaFX planned in March 2013 at the MarsJUG (the JUG in Marseille, France) just before Devoxx France. Of course if you would like a JavaFX presentation, feel free to contact me. I think about submitting a paper at Devoxx France 2013, I just need to continue to think about a nice subject (I already have one in mind). After this first talk I am more than ever enthusiast about JavaFX, and spread the word about it. I wish I could *-FX instead of “just” JUG-FX. I’m working on that.

And also I’ll try get involved in the OpenJFX project because it’s a nice place for JavaFX lovers (and I’m definitively one of them!) and to learn a lot of things.

In conclusion, keep coding, keep loving that and do it with passion! (And do some JavaFX, it rocks)
See you around.

PS: I really would like to thank some guys at Oracle for the great support and also the team at the ElsassJUG for letting me present JavaFX. And thanks to the folks who came at my presentation!

Build JavaFX runnable JAR with IntelliJ IDEA (and Ant)

For years now I’ve been using NetBeans and I’m satisfied about it. But I wanted to give IntelliJ IDEA a try so I decided to test it by creating some JavaFX apps. And if you’re following the news, JetBeains has released IntelliJ IDEA 12. The thing is IntelliJ doesn’t have specific JavaFX features so I really wanted to see how to deal with it. Since JavaFX 2 is a part of the JDK 7 now, you won’t face any problem according like the JavaFX classes. The “problem” is I really have my habits on NetBeans and it is kind of difficult to throw them away. And one of my first problem was: how do I create a runnable JAR for my JavaFX app? Well I know the stuff about Ant tasks, the JavaFX packager in theory. But I didn’t had to really know them in details because NetBeans really does everything for you.
To be very clear: I won’t judge neither NetBeans, neither IntelliJ. Both have their pros and cons and both are great products. The subject here is really to focus on how to create a runnable JavaFX JAR in IntelliJ using Ant. And because I am really a newbie to IntelliJ, I hope this post could help others.

Configuration and tools

The configuration and tools that is used:

  • OS X Lion or Mountain Lion
  • Java 7u9 (with JavaFX 2.2.3)
  • IntelliJ IDEA 12 CE
  • Ant (shipped with the IDE)

The project I’m using is a simple project that you can grab here.

The Ant build script

First thing we have to do is to create the Ant build script. Go to Build > Generate Ant Build …
IntelliJ_Generate_Ant_build_script_01
Then create the script by giving it a name. I personally decided to make it as simple as possible.
IntelliJ_Generate_Ant_build_script_02
After that, I replaced the whole generated content of the build script by the following one:

<?xml version="1.0" encoding="UTF-8"?>
<project name="testerfx" default="default" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant">

  <property name="java.home" value="/Library/Java/JavaVirtualMachines/jdk1.7.0_09.jdk/Contents/Home"/>
  <property name="source.dir" value="src"/>
  <property name="out.dir" value="build"/>
  <property name="out.dir.classes" value="${out.dir}/classes" />
  <property name="out.dir.dist" value="${out.dir}/dist"/>
  <property name="app.jar.name" value="TesterFX.jar"/>

  <path id="classpath">
    <fileset dir="${java.home}/lib" includes="**/*.jar"/>
  </path>

  <target name="default">
    <taskdef resource="com/sun/javafx/tools/ant/antlib.xml"
                 uri="javafx:com.sun.javafx.tools.ant"
                 classpath=".:${java.home}/lib/ant-javafx.jar"/>
  </target>

  <target name="clean">
    <delete dir="${out.dir}"/>
  </target>

  <target name="init" depends="clean">
    <mkdir dir="${out.dir}"/>
    <mkdir dir="${out.dir.classes}"/>
    <mkdir dir="${out.dir.dist}"/>
  </target>

  <target name="compile" depends="default, init">
    <javac srcdir="${source.dir}" destdir="${out.dir}/classes" classpathref="classpath"
               source="1.7"
               target="1.7" />
    <copy todir="${out.dir}/classes">
      <fileset dir="${source.dir}">
        <include name="**/*.png" />
        <include name="**/*.fxml" />
        <include name="**/*.css" />
      </fileset>
    </copy>
  </target>

  <target name="build" depends="compile">
    <fx:application id="TesterFX" mainClass="com.twasyl.testerfx.app.TesterFX"/>
    <fx:jar destfile="${out.dir.dist}/${app.jar.name}">
      <fx:application refid="TesterFX"/>

      <manifest>
        <attribute name="Implementation-Vendor"
                           value="Thierry"/>
        <attribute name="Implementation-Title"
                           value="TesterFX"/>
        <attribute name="Implementation-Version" value="1.0"/>
      </manifest>

      <fileset dir="${out.dir.classes}"/>
    </fx:jar>
  </target>
</project>

If you’re familiar with Ant, this is pretty easy: I define some basic targets for cleaning and compiling. The most interesting part is the target build. It is there that the final jar for our application is done using some new tags like:

  • fx:application that represents our JavaFX app, where you define the application class, the fallback one and so on ;
  • fx:jar that will create the final JAR by assembling the classes, convert CSS files into binary format if wanted and so son.

Of course there are other tags like fx:deploy, fx:signJar, etc. But here we only wanted to build a simple JAR without any particular configuration.

Try it

Once your build script is ready you just have to execute the build target to get your runnable JAR. To do so, at the right of IntelliJ click on Ant Build and then on the + in order to add your build script.IntelliJ_Generate_Ant_build_script_03
Choose your file:
IntelliJ_Generate_Ant_build_script_04
And then you will be able to execute targets you want.
IntelliJ_Generate_Ant_build_script_05

That’s it. I hope this could help you.

Useful resources

Follow

Get every new post delivered to your Inbox.