FX Experience Has Gone Read-Only

I've been maintaining FX Experience for a really long time now, and I love hearing from people who enjoy my weekly links roundup. One thing I've noticed recently is that maintaining two sites (FX Experience and JonathanGiles.net) takes more time than ideal, and splits the audience up. Therefore, FX Experience will become read-only for new blog posts, but weekly posts will continue to be published on JonathanGiles.net. If you follow @FXExperience on Twitter, I suggest you also follow @JonathanGiles. This is not the end - just a consolidation of my online presence to make my life a little easier!

tl;dr: Follow me on Twitter and check for the latest news on JonathanGiles.net.

Introducing the ControlsFX TableFilter

Introducing the ControlsFX TableFilter

This is a guest post by Thomas Nield, a contributor to ControlsFX. Enjoy ๐Ÿ™‚


Last year I spent a decent amount of time contributing a TableFilter control to the ControlsFX project. It was inspired by Eugene Ryzhikov’s SwingBits project which contained a table filtering control for Swing. After recently making some enhancements, it seemed like a good time to share the TableFilter and what it can do. I hope it enriches your UI much like the rest of the ControlsFX project has done for me.

The TableFilter behaves very similarly to Excel's spreadsheet filter. Right-clicking on a column header will make it visible, and a searchable checklist of distinct values for that column allows you to quickly filter for data.

Here is how it works:

Step 1: Have a TableView with a TableFilter applied

TableView<Person> tableView = ...
//add TableColumns
TableFilter filter = new TableFilter(myTableView);

Step 2: Right click the header for the column you want to filter on

Step 3: Either search for items you want to filter on or uncheck values you want to filter out

Step 4: Click APPLY to execute the filter

You can always remove the filter by right-clicking any column and clicking RESET to clear the filter for that particular column, or RESET ALL to remove all filters for all columns.

How it Works

This filter control is highly intuitive to business users. From my experience, many expect it especially if they have worked with Excel. But for something that is so intuitive to use, there are a surprising number of complicated moving parts the developer has to account for:

  • A distinct set of values must be maintained for each column
  • Values that are selected/unselected must be tracked
  • If data is added or removed, the distinct values must be rebuilt while persisting the selected/unselected values
  • Each column can have a separate filter applied, and only mutually inclusive items that qualify with all filters can be displayed
  • Users may want distinct values to be grayed out if they are no longer visible due to a recent filter in another column
  • There may be a need to customize search "strategies" for distinct values, such as wildcards, regular expressions, date and number comparisons, etc.

The TableFilter control does all the above, and after a lot of work I have used it in production for about six months. My clients have been extremely satisfied with it and it has worked reliably.

The TableFilter will swap out the ObservableList of items with a FilteredList. This FilteredList will only display items that qualify on all column's respective filters. This means you can execute filters on more than one column in combination.

For instance, we have already filtered to records where the last name is "Warren". We can add another filter where the age is 16. Note that when I right-click the "Age" column, values that are no longer visible due to the "Last Name" filter are grayed out. You can use the "NONE" button to unselect all values and pick the value(s) you are interested in.

STEP 1: Right-Click "Age" and apply a second filter

STEP 2: Hit "Apply" to execute the second filter

You can remove all filters from all columns by right-clicking any column and clicking "RESET ALL".

Custom Search Strategies

One simple but highly flexible configuration you can do with the TableFilter is change the search behavior on the search box. By default, the search box will filter distinct values based on the inputted String, and pass it to the contains() method of each distinct value's toString(). Effectively, whatever input you type in the search box will match distinct values that contain it.

You can change this behavior easily by passing a BiPredicate<String,String> lambda, where the first String parameter is the "input" from the search box, and the second String value is the "target" representing each distinct value. Using these two String inputs, you can do any matching logic that returns a boolean.

For example, you can implement the search box behavior to use regular expressions for the table filters.

TableFilter<Person> tableFilter = new TableFilter<>(table);

tableFilter.setSearchStrategy((input,target) -> {
    try {
        return target.matches(input);
    } catch (Exception e) {
        return false;
    }
});

Now I can use regular expressions to search through the distinct values.

Note I had to use the try-catch because event-driven text inputs will likely contain broken regular expressions, especially as they are being typed. When the typed input is currently not a valid regular expression, I just default the qualification to false.

Not that all values for all columns are searched by their toString() values. This makes sense since you are searching by typing in String inputs, so the distinct values should be compared to the input as String values as well. You can do other behaviors, like using startsWith() instead of contains() or even a wildcard pattern system.

Currently, the API is set up to use one search strategy for all column filters. I may explore column-specific search behaviors later but for now I feel it would complicate the API. But with a little effort, you can create more complex search strategies that recognize numbers, dates, and even simple math expressions. For example, you could evaluate if the two String inputs are numbers. If the numeric input starts with a "less than" symbol, as in "<120", you would then only qualify distinct values that are less than 120. This sounds involved but I have found it is not much work, and it just requires a few nested case statements. I have even developed implementations that can work with date strings of various formats.

Updating Data in the TableView

Because the TableFilter commandeers the TableView's backing ObservableList and replaces it with a FilteredList, you will get errors when trying to modify the backing ObservableList of items returned from getItems().

To modify the "backing list" of items for a TableView, call the getBackingList() method on the TableFilter and modify that instead to update the TableView.

TableFilter<Person> tableFilter = new TableFilter<>(table);
ObservableList<Person> items = tableFilter.getBackingList();

Conclusions

I hope you find the TableFilter useful. It is currently available in the ControlsFX project but an improved version (which includes the custom search strategy and fixes for nullability issues) is available in the coming release. Please let us know what you think and do not hesitate to get involved if you have ideas or improvements.

Quick Book Review: Mastering JavaFX 8 Controls

Quick Book Review: Mastering JavaFX 8 Controls

51oS4qJWf8L[1]Hendrik Ebbers was generous enough to arrange for a hard copy of his new book, Mastering JavaFX 8 Controls, to be sent to my place, so the least I can do is post a mini review of the book. I have now read the book cover to cover and I think that it is a very good book for people wanting to learn more about the controls that ship in JavaFX 8, and also for people wanting to learn more about how to build custom controls specific to their use cases.

Reviewing a book like this is difficult for me as I have lived and breathed JavaFX UI controls for over five years now, so it is hard for me to gauge whether the book is detailed enough for people newer to the subject. My gut feeling is that the book could do with more text to describe concepts, but in general I think most readers should be able to follow along without a problem. In reading the book I made a few notes that I have also passed on to Hendrik, to help improve future editions of the book (which I hope there are as JavaFX API evolves quite rapidly).

The early chapters of the book give a good introduction to the basics of JavaFX. The middle section gives a good overview of the existing JavaFX UI controls, as well as interesting topics such as Swing and SWT integration, and styling UI controls. Unfortunately, whilst the first two sections feel like they go at a good pace, the final section of the book seems to be over too quickly – there is only one chapter on creating custom controls, which is unfortunate given the subtitle of the book is “Create Custom JavaFX Controls for Cross-Platform Applications”. It would be nice to see the final section of the book expanded to fill multiple chapters in future editions – this way it could feel less cramped and the book could easily become the go-to reference for how to create custom controls.

One nice aspect of the book is the interviews with members of the community (including myself). I enjoyed reading the interviews, but I wished for more and for them to be longer! ๐Ÿ™‚ There are a lot of interesting members of the community who can provide a bunch of detailed insight and explanations, so I hope future editions expand on the interviews.

Overall I think that this is a great book for people interested in working with JavaFX UI controls, and shows great promise for future editions if some of the kinks above are worked out. Despite my negative points, I recommend this book to people who are serious about wanting to get to know JavaFX UI controls in greater depth.

 

DukePad

DukePad

At the JavaOne technical keynote just now Richard and Jasper introduced the DukePad – a custom built tablet device powered by a Raspberry Pi and featuring a touch screen, camera, HDMI output, GPIO pins, and more. It is powered with Java and has a custom-built JavaFX user interface.

DukePad-Front-Home-Screen-W

The DukePad is a Do-It-Yourself, make-at-home tablet computer based on theย Raspberry PI and JavaSE Embedded 8. The plans and instructions for building the DukePad are available here, and we’re working with suppliers to make available pre-made kits that can be more easily assembled. The software on the DukePad uses Raspbianย Linux as the operating system, and an OSGi-based JavaFX environment. Within this DukePad environment, apps are simple JavaFX OSGi Modules.

dukepad

Click for bigger image

The DukePad is not a product, it is an open source, freely available set of plans and software for assembling your own tablet using off the shelf components. As such, the quality of the DukePad software environment is demo-quality (although we did strive to write as much real functionality as we could, the realities of demo presentations requires sacrificing time on parts of the applications that are not going to be shown, in favor of smoothing out those parts that will be shown). The code is hosted in the OpenJFX repositories under apps/experiments/DukePad. We hope to see forks of this code (GitHub, BitBucket, whatever you like best) and lots of experimentation and improvement that can be shared.

Returning a TableView back to an unsorted state in JavaFX 8.0

One of the big features I’ve known people have wanted for a long time (hey, I’ve wanted it too!) is support for returning a TableView back to its original, unsorted state after being sorted by the end user. In general the user interaction goes something like this:

  1. Click on a TableView column header once. Everything sorts in ascending order. Great!
  2. Click on the same column header again. Everything sorts in descending order. We’re on a roll here!
  3. Click on the same column again. The sort arrow disappears, and…….nothing ๐Ÿ™

Of course, what should happen here is that the order of the items in the table should be reset back to their original order, from before the user ever clicked on anything. If you step behind the curtains with me for the briefest of moments, you’ll realise that the only way we can really do this is to of course keep a copy of the list in its original state (or a list of all the changes to the original list, such that we can unwind the changes later on). I never really wanted to do this, as you’re just setting yourself up for failure / pain / bugs / etc. What I always wanted to do was follow the wonderful GlazedLists approach from the Swing days, where the collections themselves became smarter, and the TableView remained mostly* inconsiderate of the type of collection given to it.
(more…)

JavaFX 2013 Survey

It’s been almost 2 years since we released JavaFX 2.0 on Windows, followed by Mac OS X and Linux support, and plenty of new features. It has been a blast for us, and we’re pretty happy with what we’ve accomplished so far. JavaFX 8 (JDK 8) is looking in great shape, and we’re pretty much done open sourcing all of JavaFX through OpenJFX. However, nothing matches hearing from you and getting a pulse on the developer community.

So, it’s time for another survey on FX Experience! You may recall our last survey was about tablets and mobile support, and we received an absolutely huge number of submissions. That information was fed directly to the relevant people, and they’ve asked us again to put out the survey below. Your input is hugely appreciated and it is a great way for you to continue to influence the future of JavaFX! Get your friends to participate! ๐Ÿ™‚
(more…)

JavaFX for Tablets & Mobile

We’re running a little survey here at FX Experience to get input from JavaFX developers (and everybody else!) as to the ways they would use a port of JavaFX to smartphones and tablets (think: iOS, Android, and WinRT). This is your chance to really influence the future of JavaFX! Get your friends to participate!

(more…)