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!

ListView Multiple Selection

One of the things that would be really nice to have in the virtualised ListView and TreeView JavaFX controls, not to mention future controls like TableView, is multiple selection. Certain kinds of apps just can not exist without multiple selection in fact.

So, unsurprisingly, today I got an email from a user of JavaFX, who claims to be a fan of FX Experience (hi Keith!), who was needing multiple selection for his work. I didn’t actually think it could be done very easily (and one of my main jobs is working on these controls, so I should know), but I spent a bit of time looking into it, and it turns out that it’s actually quite possible, with a number of warnings and rough edges, and also the use of a little bit of unpublished API. As long as you’re promising to not tell anyone, I thought I’d share this code….but just with you, so shhh 🙂

What I present to you today therefore is to be considered proof of concept only. Using this in your day job is considered risky, as some of the code may not work in future releases. Being a proof of concept, the code below is not fully implemented (which I’ll detail more shortly), and performance may degrade somewhat as the number of selected items increases. No attempt at performance optimisation has been made. Proceed at your own risk 🙂 I hope that I’ve sufficiently scared you.

I wanted to briefly show the code, and then just provide you with a jar file to download and use in your own projects. Firstly though, here’s a screenshot I sent to Keith to detail what he should expect to see when he runs the new control:

Do not adjust your TV sets (for those of you, ah, accessing the net via your TV?). The ListView on the right looks odd because the label is rotated inside the cell, to make it apparent that you can get multiple selection even when using a custom cell factory. To get the multiple rows selected I just held down ctrl or shift as I left-clicked, as is usually the case in list controls in other UI toolkits. Here’s the code I wrote to create the app in the screenshot above:

Pretty much the kind of code you’d expect to see when creating a ListView, except the control is called MultiSelectListView, and the ListCell used in the second list is called MultiSelectListCell.

The code for MultiSelectListView is shown below. There is nothing special in here, it simply extends ListView, and adds a bit more API for selectedIndexes and selectedItems, as well as a default cell factory if one isn’t specified by the user.

Moving on, we come to MultiSelectListCell, which is where the real warning comes – this class has unpublished API being used, and we offer no guarantee that it’ll stay this way. Use it at your own risk, and seriously, don’t build your business around this API being available. You’ve been warned.

This class creates a custom MultiSelectListCellSkin, and binds to the selectedIndexes sequence to determine if it is selected or not. When this property changes, it calls some impl_ code to re-evaluate its state, to allow for the background selection colour to be turned on or off as necessary. The rest of the code is pretty straightforward (i.e. don’t question it) 😉

MultiSelectListCellSkin is hidden inside MultiSelectListCell (for no particular reason), and its job is simply to just extend ListCellSkin, apply a small bug workaround, and apply a custom behavior, which is where the actual multiple selection magic happens.

postinit {
// This fixes an issue where the mouseReleased function is called twice.
overlay.onMouseReleased = null;
}
}
[/jfx]

The final class is a slight extension of the ListCellBehavior class, not surprisingly called MultiSelectListCellBehavior. This class handles the mouse click event, including determining if ctrl or shift is held down, and acting appropriately.

That’s all there is to it. Note that whilst I wrote this, because it is not a ‘production-quality’ control, I haven’t tested it at all, past a few user tests. I’m sure it’ll have issues. If you report them to me I’ll revise this post to ensure the best control we can have. You should also note that this in no way reflects how we’ll do multiple selection in the future, when it is supported ‘natively’.

Now, on to the warnings I warned you were coming. In general, don’t use the select() function any more – just directly manipulate the selectedIndexes sequence. Also, don’t use the selectedItem / selectedIndex properties any more – just use the selectedItems and selectedIndexes sequences instead. If you think you’re going to forget this, it should be possible to just keep overriding more functions / properties to have everything work as expected, but unless I feel sufficiently nagged, I’ll probably just leave this as a user exercise. Similarly, there is no support for keyboard navigation / multiple selection in this version.

5 Comments

Dear Jonathan,
First of all THANK YOU for this implementation, I was looking for this. Thank you again 🙂

Now I also need to to have keys-functionality for multi-selection in ListView, so during 3 days I’m trying to make smth you’ve done with ListCell Behavior, but I’m working with ListViewBehavior and currently I have a problem.
When I’m setting a new behavior to ListViewSkin, in order to override some logic for keys, a scroll-functionality is going away. I’ve already tried to set method from skin to behavior but still no luck. So, I can see that the selection is changing but the scroll in the ListView doesn’t move, so I can’t see my current selection.