Sunday, November 17, 2013

How to implement Filter/Search for TableView in JavaFX

Filter or Search fields for tables are nothing new. Most of us would have used a custom filter field for JTable in Swing. In this post I'm going to show you how to implement a basic filter for TableView in JavaFX. You can download the source of the example from here.


This filter field will look for each cell in a row and if it finds a match in any of the row, then it retains the row in the TableView else removes it. I have used a TextField as the Filter component (actually, I have used TextFields.createSearchField() from the ControlsFX project. But a simple TextField will work). The filtering logic goes inside the invalidation listener, that is added to the text property of the TextField. I think this is a more cleaner way of implementing the logic, as opposed to implementing the logic in the Key listener (as we do in Swing). Below is the snippet on the filtering logic.

 
  1. private void initFilter() {
  2. txtField = TextFields.createSearchField();
  3. txtField.setPromptText("Filter");
  4. txtField.textProperty().addListener(new InvalidationListener() {
  5. @Override
  6. public void invalidated(Observable o) {
  7. if(txtField.textProperty().get().isEmpty()) {
  8. table.setItems(data);
  9. return;
  10. }
  11. ObservableList<Employee> tableItems = FXCollections.observableArrayList();
  12. ObservableList<TableColumn<Employee, ?>> cols = table.getColumns();
  13. for(int i=0; i<data.size(); i++) {
  14. for(int j=0; j<cols.size(); j++) {
  15. TableColumn col = cols.get(j);
  16. String cellValue = col.getCellData(data.get(i)).toString();
  17. cellValue = cellValue.toLowerCase();
  18. if(cellValue.contains(txtField.textProperty().get().toLowerCase())) {
  19. tableItems.add(data.get(i));
  20. break;
  21. }
  22. }
  23. }
  24. table.setItems(tableItems);
  25. }
  26. });
  27. }
Every time the text property of the TextField is invalidated, the listener will check for matching items in the table content and set the table with items that only match the filter criteria. In the above snippet, the variable data holds the original list of items for the table. In Swing context you can think of it like a original table model. So when you filter, a new list is created to hold only the matching rows and this list is set as items to the TableView. 

The whole example is available as a maven project. Download to explore more. This is a very basic implementation. Some of the limitations of this filter is that it calls toString() to compare the contents. That would need some clean up. More features like filtering based on column index, highlighting the matching text in the table, showing some animation in case of really big list of data etc.. can be added to make this a perfect JavaFX control. 

Happy coding!!  

Table Filter Demo Example : https://drive.google.com/file/d/0B96EBDPI6nZZX2VpdThBa3RnOGc/view?usp=sharing

Wednesday, July 24, 2013

Animated Wizard using JavaFX

I recently got to work on a Wizard Type application for one of my clients. Also I am currently playing with the JavaFX Animations. Animations were never straight forward in Swing and even if we were to develop a custom animation, performance was a big factor to consider. This made JavaFX animations a lot of fun.

In this post I'm going to share a simple wizard like application, that has two forms. Not a real world use case, but could give you some basics on how animations can be used in a wizard.

The code for this application can be downloaded as a Netbeans project from here.

I use a single Parent View and two Child Views in this demo. The Parent view has Stack Pane in place, which acts as a host for the child views. The child view is swapped when "Next" button is clicked.



The nodes are animated in the initialize method of the controller class for each view. For this purpose I have a AnimationManager, which will animate the nodes for me. The layout of each child view (the actual forms), is simple. I have got a VBox and number of HBox s' inside it, for each field.


If you are a WPF developer trying to understand JavaFX, VBox is similar to a WPF StackPane with vertical orientation and HBox is similar to a WPF StackPane with horizontal orientation. 

The AnimationManager helps me animate all the 1st level child of the VBox (which are HBoxs). Below is the method from AnimationManager that animates the nodes.

  1. private static ObservableList<Timeline> getTimelines(Parent node) {
  2. double deltaSeconds = 0;
  3. ObservableList<Timeline> timeLines = FXCollections.observableArrayList();
  4. ObservableList<Node> children = node.getChildrenUnmodifiable();
  5. for(Node child: children) {
  6. deltaSeconds += 0.5;
  7. Rotate rotate = new Rotate(-90, 0, 0, 0, Rotate.Y_AXIS);
  8. child.getTransforms().add(rotate);
  9. Timeline anim = new Timeline(
  10. new KeyFrame(Duration.seconds(deltaSeconds),
  11. new KeyValue(child.opacityProperty(), 0),
  12. new KeyValue(rotate.angleProperty(), -90)),
  13. new KeyFrame(Duration.seconds(deltaSeconds + 1.0),
  14. new KeyValue(child.opacityProperty(), 100),
  15. new KeyValue(rotate.angleProperty(), 0))
  16. );
  17. timeLines.add(anim);
  18. }
  19. return timeLines;
  20. }

This method returns a List of Timeline objects to the caller. The AnimationManager is called inside the initialize method of the controller. And hence when the views are initialized the animation is played. Download the source to experiment further.