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.

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.

Originally, SortedList and FilteredList were targeted to JavaFX 2.0, but due to lack of time / engineers / etc, it had to be unfortunately scuttled from that release. Ever since then I’ve been waiting (and gently pestering) the Prague JavaFX team to reintroduce the functionality. Fortunately they relented and graciously spent the time to develop these collection classes for JavaFX 8.0. Whilst these collection classes were being defined, one of the major use cases was for their use in TableView, so it’s no surprise that the collections and TableView work really nicely together. However, until today I hadn’t actually taken the time to test that everything was working as expected, so when I stumbled upon an issue in our JavaFX Jira issue tracker, it gave me a chance to actually test it out to make sure everything works as expected. The tl;dr answer is “yes, it works great….now (and I’ll push the fixes to the repo soon)”.

So, what is the current approach to sorting in TableView (which is still applicable to those of you that don’t want to support returning to the unsorted state)? It looks a little something like this:

  1. Create a TableView
  2. Define the TableColumns
  3. Set the TableView items list to the applicable ObservableList instance
  4. Go and program something else

In other words, your items list will be directly sorted as the user interacts with the table columns. That is, the items list is both the model and the view.

With the introduction of SortedList, you have to do a little bit more work. Here are the steps you’ll want to take:

  1. Create a TableView
  2. Define the TableColumns
  3. Set the TableView items list to the applicable SortedList instance
  4. Bind the SortedList comparator property to the TableView comparator property
  5. Go and program something else

To those without eagle-like eye sight, it’s the third and fourth steps above that are a little different. What you’re doing is telling the SortedList to use the Comparator provided by the TableView, and this Comparator will either be a special comparator based on the currently selected table column(s), or it will be null (if no columns are in the sort order). If the comparator is null, the SortedList is designed to return to its unsorted state. Pretty neat, huh? 🙂

If following a five step process is too complex, here is some sample code you can refer to:

// create a SortedList based on the provided ObservableList
SortedList sortedList = new SortedList(FXCollections.observableArrayList(2, 1, 3));

// create a TableView with the sorted list set as the items it will show
final TableView<Integer> tableView = new TableView<>(sortedList);

// bind the sortedList comparator to the TableView comparator
sortedList.comparatorProperty().bind(tableView.comparatorProperty());

// Don't forget to define columns!

That’s it!

If you put a SortedList into a TableView, but forget to bind the comparator, you’ll find that nothing happens when you go to sort the TableView. This is because the TableView is updating its comparator but the SortedList isn’t listening.

I hope this has been informative! 🙂 Now it’s time for me to get back to squashing bugs 😎

* I say that TableView is mostly inconsiderate of the type of collection, but in reality it makes everyones day a little easier if I have the TableView smooth out some of the bumps. I know this will upset the purists, but I’m yet to find a way to make an omelet without breaking a few eggs – and my kitchen has the mess to prove it.