Tuesday, October 15, 2013

Custom ComboBox

The nice thing about JavaFX 2.2 is that Controls can hold any kind of objects rather than a String. The same goes for ChoiceBox, ListView, TableView.
Let's start creating a class called Member. In this class we have two member variables. One is a String and is the name of the member the other is his age. Here the code:

public class Member {
 String  name;
 int  age;
 
 public Person(String name) {
  this.name = name;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public int getAge() {
  return age;
 }
 public void setAge(int age) {
  this.age = age;
 }
}  

A typical JavaBean right? :). Now we declare our ComboBox using our own class.

 @FXML
 private ComboBox<Member> comboBox;

The next step is to create a ObservableList which will hold a list of Persons. I will talk more about the ObservableList's later, for now I just use it and add some members into it.

ObservableList<Member> list = FXCollections.observableArrayList();
  list.add(new Person("Cengiz"));
  list.add(new Person("Mueller"));
  list.add(new Person("Nick")); 

How do we assign that list to the combobox? Easy, we use the ComboBox.setItems(...) method to do so. After doing that I like the ComboBox show me the first item in the list as choosen one.

comboBox.setItems(list);
comboBox.getSelectionModel().selectFirst();  

Here the whole code. Don't forget to prepare the FXML file with a ComboBox in it :). So here how it looks like.
package javafx.tutorials;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.stage.Stage;

public class Tutorial3 extends Application implements Initializable{
 
 public class Member {
  String  name;
  int  age;
  
  public Member(String name) {
   this.name = name;
  }
  public String getName() {
   return name;
  }
  public void setName(String name) {
   this.name = name;
  }
  public int getAge() {
   return age;
  }
  public void setAge(int age) {
   this.age = age;
  }
 }
 
 @FXML
 private ComboBox<Member&gt comboBox;
 
 @Override
 public void start(Stage stage) throws Exception {
  FXMLLoader loader = new FXMLLoader(getClass().getResource("Tutorial3.fxml"));
  loader.load();
  Scene scene = new Scene((Parent) loader.getRoot());
  stage.setScene(scene);
  stage.show();
 }

 @Override
 public void initialize(URL location, ResourceBundle resources) {
  ObservableList<Member&gt list = FXCollections.observableArrayList();
  list.add(new Member("Cengiz"));
  list.add(new Member("Mueller"));
  list.add(new Member("Nick")); 
  
  
  comboBox.setItems(list);
  comboBox.getSelectionModel().selectFirst();
 
 }
 
 public static void main(String[] args) {
  Application.launch(args);

 }

}


Ok, now let's start the applicaton. And what do we seeeeee.....


Uuuuupsssss. What happend here? Of course, what we see in the combo box fields is text but how can the ComboBox object know what to use to represent the string over there? It just knows the "Member" object. Our solution is easy. The documentation of JavaFX tells us to use the ComboBox.setCellFactory(...) method. That helps us to define how we present the items in the ComboBox itself. OK, I thought, let's use that no problem. And here the additional part of the code ...

comboBox.setCellFactory(new Callback<ListView<Member>, ListCell<Member>>() {
 @Override
 public ListCell<Member> call(ListView<Member> param) {
  
  return new ListCell<Member>(){
   @Override
   public void updateItem(Member item, boolean empty){
    super.updateItem(item, empty);
    if(!empty) {
     setText(item.getName());
     setGraphic(null);
    }
   }
  };
 }
});

Now let's start the code again.
 Oh yeahh cooool it wooooorrkssssssssss .... eeeeee not? If I select one of those members I still have a wrong representation of my object. Now the elements in the list are correct but the label which shows the current selected one is still wrong? How do I change that?
I have to admit I was looking and looking and couldn't find anything in the World Wide Web. I checked the documentation ComboBox, which tells us something additionally important too. It's a warning about using nodes in the ComboBox itself. But that didn't solve my problem either. Nevertheless the solution at the end was so damn easy. I noticed that the String representation was using Java's Object's class names.
I just had to override the toString for Java objects and that's it.  .... I had it.
And actually for the "Member" example I didn't have to make my own cell factory anyway. It's completely enough to implement the toString method that's it.
Of course if I want show some icons or colors as items in the ComboBox or other controll elements than I have to implement that function. But that I'll do in the next section. Here the complete code (I let the setCellFactory implementation in just to see how to do it anyway:

package javafx.tutorials;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.stage.Stage;

public class Tutorial3 extends Application implements Initializable{
 
 public class Member {
  String  name;
  int  age;
  
  public Member(String name) {
   this.name = name;
  }
  public String getName() {
   return name;
  }
  public void setName(String name) {
   this.name = name;
  }
  public int getAge() {
   return age;
  }
  public void setAge(int age) {
   this.age = age;
  }
                @Override
  public String toString() {
   return name;
  }
  
 }
 
 @FXML
 private ComboBox<Member&gt comboBox;
 
 @Override
 public void start(Stage stage) throws Exception {
  FXMLLoader loader = new FXMLLoader(getClass().getResource("Tutorial3.fxml"));
  loader.load();
  Scene scene = new Scene((Parent) loader.getRoot());
  stage.setScene(scene);
  stage.show();
 }

 @Override
 public void initialize(URL location, ResourceBundle resources) {
  ObservableList<Member&gt list = FXCollections.observableArrayList();
  list.add(new Member("Cengiz"));
  list.add(new Member("Mueller"));
  list.add(new Member("Nick")); 
  
  
  comboBox.setItems(list);
  comboBox.getSelectionModel().selectFirst();
  comboBox.setCellFactory(new Callback<ListView<Member>, ListCell<Member>>() {
       @Override
       public ListCell<Member> call(ListView<Member> param) {
  
            return new ListCell<Member>(){
              @Override
                public void updateItem(Member item, boolean empty){
                  super.updateItem(item, empty);
                  if(!empty) {
                    setText(item.getName());
                    setGraphic(null);
                  } else {
                    setText(null);
                  }
                }
           };
      }
  });
 
 }
 
 public static void main(String[] args) {
  Application.launch(args);

 }

}


And here how it looks like this time :D.  Yeaaaa niceeee.
OK you think, strings are easy and we kind of hacked the value in the selected field. After a bit more investigation I figured out that what I so call selected field is the button field. We can add a custom cell too like for the items. We can do the same procedure like for the ComoBox.setSellFactory(). Now I'm going to do that for both with a more sophisticated example.

Selections

Until now the ComboBox is not doing anything. How do we know which Member the user selects? As we can see in the code above, we used the ComboBox.getSelectionModel().selectFirst() method to select the first Member in the list. The selection model handles everything in terms of selecting. We have to ways to get the selected item:

  1. Using the ComboBox.getSelectionModel().getSelectedItem() and ComboBox.getSelectionModel().getSelectedIndex()
  2. Using Listener to the selectedItems or selectedIndex.

Selection using Listener

We have to add a listener to that to track the users selections. Here the code:


comboBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Member>() {

 @Override
 public void changed(ObservableValue<? extends Member> observable,
   Member oldValue, Member newValue) {
  System.out.println("Member: " + newValue.getName());
  
 }
});

Now everytime the user selects something in the ComboBox the listener will be called and we have access to the previous selected item and the current selected item. The good thing is really that we get the objects itself so we can use it immediately.

Selectiong using direct method

That means we use the ComboBox.getSelectionModel().getSelectedItem() or ComboBox.getSelectionModel().getSelectedIndex().

Friday, October 11, 2013

JavaFX and FXML

Introduction

In the last post I was showing how to create a window and put some buttons on it. Now let's imagine that we want to put some more elements on the window like TextField"s and some ChoiceBox'es and TableView's and get closer to a RCA. We have to choices:

  1. Manually: We have to design and think before how and which container we have to use to design our UI. After doing that or during we hard code every element into our code. This method is of course for really small projects enough. For bigger projects I think everyone knows that it is a pane if we have to change or add something which makes everything inflexible and hard to support.
  2. "Automated" Using FXML files and SceneBuilder. Every aspect of UI is done with the SceneBuilder. We see immediately how it will look like. After saving it as an FXML file we can load the scene with the FXMLLoader class.

FXML and SceneBuilder

So I assume that we have installed SceneBuilder 1.1 and it is ready to use. We create the design of our UI using SceneBuilder 1.1 and saving it as the FXML file which can be loaded in a JavaFX application. With all that we can create UI's in seconds for our applications.
OK, let's start. Here the same application like in the first post but this time using a FXML file.

package javafx.tutorials;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class Tutorial2 extends Application {

 @Override
 public void start(Stage stage) throws Exception {
  FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Tutorial2.fxml"));
  fxmlLoader.load();
  AnchorPane root = fxmlLoader.getRoot();

  Scene scene = new Scene(root);
  stage.setScene(scene);
  stage.show();
  
 }

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

 @FXML
 public void onClickMe(ActionEvent event) {
  System.out.println("You clicked me.");
 }
 @FXML
 public void onClickMeToo(ActionEvent event) {
  System.out.println("You clicked me too.");
 }

}

So what do we do in the code above? First we have to call the Application's static method "launch" in the main() function.
In the start(Stage stage) function we use the FXMLLoader class to parse the FXML file.  We get the root object using the FXMLLoader::getRoot() method. This root is the root of our scene. 

Open SceneBuilder, create a new AnchorPane FXML project. If you just select "New" in the "File" menu it will do that automatically. If you wanna use another root container select "New with Root Container" and you'll see 3 choices. Select the AnchorPane one hahahahahaaa ... :D
Select the AnchorPane on the left side in the "Hierarchy" section. On the right side in the "Code" section you'll see the "Controller class" field, write "javafx.tutorials.Tutorial2" which is the name of our main JavaFX class.
Now put a VBox into the AnchorPane. In the "Hierarchy"section select the VBox node. Right click your mouse and select "Fit to Parent" or go to the "Modify" menu and select "Fit to Parent". This fill stretch out the VBox to the whole AnchorPane area. Now put 2 Buttons into the VBox field. Rename the buttons on the right side in the "Properties" section in the "Text" field.


On the right side of SceneBuilder in the Inspector column, select the Code Section. Click the first button and you see a field called "On Action". In that field we write the function name we will use in our class. As you can see in the code above I used onClickMe


Now click the second button and type in the "On Action" field "onClickMeToo"


Now save it under Tutorial2.fxml. Actually that's it. Easy right :D. Now if we run our JavaFX code we will see the window and if we click the buttons we have the same behavior like in the first tutorial code what we did in the first post. That's it. If you want to learn more about SceneBuilder check out the Internet, there are enough descriptions to understand.


Wednesday, October 9, 2013

JavaFX and Main

Introduction

This is how the basic template for a JavaFX applications look like:

package javafx.tutorials;
import javafx.application.Application;
import javafx.stage.Stage;


public class Tutorial1 extends Application {

 @Override
 public void start(Stage stage) throws Exception {
  

 }

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

}
We have to derive our main class from the javafx.application.Application class. Doing this will force us to override the start(Stage stage) function. That member function is the main entrance to start JavaFX applications.
  • The Application() class has a static member method launch(). We have to call that in the main() method to initialize JavaFX. I have to admit I didn't read the documentation so I don't no exactly what the method is doing with the arguments of the main() but at least I know that calling it will call the start(Stage stage) method.
  • The Stage() class is the main, let's say window. Everything we will see is managed by that class. Scene Graph is used to manage elements in the scene. The Scene Graph is called Scene() and the elements are called Node(). So we can put some nodes into the scene like Buttons, TextFields, Tables etc.
  • A Scene can have only one root node. Every other elements we are going to use will go into that node.

The first raw window

Here is a short example how to show a window with a title. Later we will see we don't have to specify the width and height of the window in the stage. We'll do that in the Scene itselft.

package javafx.tutorials;
import javafx.application.Application;
import javafx.stage.Stage;


public class Tutorial1 extends Application {

@Override
public void start(Stage stage) throws Exception {
  
 // Set the title of the main windows.
 stage.setTitle("Tutorial 1");
  
 // Set the width of the window.
 stage.setWidth(320);
  
 // Set the height of the window.
 stage.setHeight(200);
  
 // Show the window.
 stage.show();
}

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

}


Nice and quite easy without any problems. Now if we want to put some elements onto the window like a button we have to do more. The Stage() is just the window now we need the Scene() which will holds the elements we are going to put on the Stage window. In the next section we go one step further in the development process. :D

The first more "advanced" example.

There is a Node() container called VBox() and I'm going to use that as root. Every added Node() within that container will be arranged vertically. Easy right :)?
I call the main window Tutorial 1 and will add 2 buttons into the Scene.

package javafx.tutorials;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;


public class Tutorial1 extends Application {

 @Override
 public void start(Stage stage) throws Exception {
  
  // Set the title of the main windows.
  stage.setTitle("Tutorial 1");
  
  // Here we create our root node.
  VBox root = new VBox();  
  
  // And here we create our first scene with the width 200 and height 100.
  Scene scene = new Scene(root, 200, 100);
  
  // Let's add some stuff to the root node.
  Button button = new Button("Click Me!");
  Button button2 = new Button("Click Me Too!");
  
  // Now we add the button to the root node.
  root.getChildren().add(button);
  root.getChildren().add(button2); 
  
  stage.setScene(scene);
  stage.show();

 }

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

}

If we start now we will see two buttons. Super easy. Clicking onto the buttons will not do anything so we need some kind of trigger.

Buttons events

We can set the setOnAction() ActionEvent handler for the buttons and get some interaction. See here:

package javafx.tutorials;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;


public class Tutorial1 extends Application {

 @Override
 public void start(Stage stage) throws Exception {
  
  // Set the title of the main windows.
  stage.setTitle("Tutorial 1");
  
  // Here we create our root node.
  VBox root = new VBox();  
  
  // And here we create our first scene with the width 200 and height 100.
  Scene scene = new Scene(root, 200, 100);
  
  // Let's add some stuff to the root node.
  Button button = new Button("Click Me!");
  button.setOnAction(new EventHandler() {
   
   @Override
   public void handle(ActionEvent event) {
    System.out.println("You clicked me.");
   }
  });
  Button button2 = new Button("Click Me Too!");
  button2.setOnAction(new EventHandler() {
   
   @Override
   public void handle(ActionEvent event) {
    System.out.println("You clicked me too.");
   }
  });
  
  // Now we add the button to the root node.
  root.getChildren().add(button);
  root.getChildren().add(button2); 
  
  stage.setScene(scene);
  stage.show();

 }

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

}

If we click the buttons we can see some text on the console. If we don't have a console we don't see anything hahahahaha :D. Just a joke sorry. Ok than let's add same text. We can use the Label() class to put some labels on the Screen. I will put the label object after the buttons. Here the final code.

package javafx.tutorials;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;


public class Tutorial1 extends Application {

@Override
public void start(Stage stage) throws Exception {
 
 // Set the title of the main windows.
 stage.setTitle("Tutorial 1");
 
 // Here we create our root node.
 VBox root = new VBox();  
 
 // And here we create our first scene with the width 200 and height 100.
 Scene scene = new Scene(root, 200, 100);
  
 final Label label1 = new Label();
 final Label label2 = new Label();
  
 // Let's add some stuff to the root node.
 Button button = new Button("Click Me!");
 button.setOnAction(new EventHandler() {
   
  @Override
  public void handle(ActionEvent event) {
   System.out.println("You clicked me.");
   label1.setText("You clicked me.");
  }
 });
 Button button2 = new Button("Click Me Too!");
 button2.setOnAction(new EventHandler() {
  
  @Override
  public void handle(ActionEvent event) {
   System.out.println("You clicked me too.");
   label2.setText("You clicked me too.");
  }
 });
 
 // Now we add the button to the root node.
 root.getChildren().add(button);
 root.getChildren().add(button2); 
 root.getChildren().add(label1); 
 root.getChildren().add(label2); 
 
 scene.setRoot(root);
 stage.setScene(scene);
 stage.show();
}

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

}

And the picture above is how it looks like. Quite fun here. We see here where we are going with all that hopefully. That's how easy it is to write RCA with JavaFX. See you in the next tutorial.

Sunday, October 6, 2013

Dive into ...

Before I start giving my humble opinion about JavaFX I like to say that I am the type jumping into things and try before I start trying to understand the fundamental things.That's what I did here too. So don't get confused if the things I am saying maybe will vary from post to post. I do it this way because I can find out if the framework is user friendly.
I worked with Java AWT,  Swing and SWT. By far the most powerful was SWT compared to the others which is unfortunately not a part of Java.
  • AWT is a part of Java so the motto, program once run everywhere. It is simple but not powerful enough for big projects.
  • Swing is cool is a part of Java too but the same goes for Swing too. It has limits. The look and feel is not up to date anymore.
  • SWT is the most powerful and adapts to the look and feel of native OS apps. Unfortunately it is not a part of Java. It's developed by the Eclipse Team. Deploying those apps on different OS's can be really big hassle.
  • JavaFX is not yet a part of Java but soon. At the moment JavaFX2.2 runs on Linux, Mac OSX and Windows. If you use Eclipse you can install e(fx)clipse plugin or you can use Netbeans. Using SceneBuilder 1.1 makes it easy create UI's in minutes.
I tested my written programs on all three platforms and it was running without problems.  I am really positive impressed. The standard design of the UI looks already professional and the support of CSS scripting to change the look makes everything super comfortable and easy.
So I don't want to talk too much about the stuff JavaFX provides. I decided to give it a chance. :-)