This time I stumbled upon a control that doesn't really look that fancy and to make it short here is a picture of what I am talking about...
This control is used over at orangetheory fitness where you do your workout in a group and the trainer can see the individual data on a monitor like on the image. As you can see the control itself is not really fancy but I liked the colors and it has again a little thing which was not that easy to implement.
I just had this image and I have no idea how the control works in reality so the first thing I did was creating a prototype in Adobe Fireworks which looks like follows...
With this vector drawing in place it was easy to create the control in JavaFX but there was one little thing that I was missing. There are these little colored trapezoids on the lower left side of the control and if you take a look at the upper image you will see that in whatever zone I'm at the moment the little trapezoid with the same color as the current background is bigger than the others. And exactly this little feature was the main reason to build this control.
This little trapezoids have no special meaning but they give the control a special touch and so I started thinking about how could they work.
The first idea that came to my mind was they should be animated. Means as soon as the heart rate zone changes the trapezoid with the same color should increase it's size using an animation.
Nice idea but it comes with a problem...
Because the trapezoids are all the same just with different colors I thought it should be easy to just create a path for the small and for the big version and morph them when needed. Unfortunately JavaFX doesn't offer a morphing transition by default BUT Tom Schindl (create of E(fx)clipse and on twitter under @tomsontom) has created an excellent PathMorphTransition which takes a source and a target path and animates the points from the source path to the target path. (you can find the PathMorphAnimation class in the Eclipse source code and also in the sources of this friday fun component).
So I've put the 5 trapezoids in an HBox container and I was ready to go...well not really.
The first problem comes with resizing...because the Path is a Shape it has no setPrefSize() method. Well the workaround is to use setScaleX() and setScaleY() on the Path. But this comes with another problem...placement. The problem is that when you scale a node in JavaFX you have to take care about the placement in a different way to make sure that the scaled node stays at the correct position.
Well even this was not a big deal but there was another problem...
When morphing a trapezoid from the small to the big state the layout bounds of the trapezoid will change because we increase the width and height.
And because I've put the trapezoids in an HBox with a spacing of 0 it leads to the following effect...
When the gray trapezoid will be transformed the increase in width will lead to a bigger gap between the gray and the blue trapezoid...BAM
And because I can't adjust the spacing for each child of the HBox this approach did not work. :(
Well there are ways to realize this but that felt like a workaround which I did not like.
So I was looking for another approach and decided that the easiest way would be to create a separate control for the trapezoid that encapsulates the Path object in a Region.
To make this work you have to make sure that the Region always has the size of the big trapezoid. So I can put the Regions in a HBox and because the Region won't change it's layout bounds when the trapezoids morphs from one to the other state the distance between the trapezoids stays the same.
To make sure that the trapezoids are not too far away from each other I've use a negative spacing (keep in mind that when resizing the control you also have to resize the spacing of the HBox).
Long story short...here is the result of my JavaFX version...
To make the control work you have to initialize it with your age. I need that info to calculate the maximum heart rate for the control. The calculation of the maximum heart rate is based on the following equation which seems to be good enough...
maxBpm = (207 - 0.7 * (age));
There are other equations but it seems this is more accurate (at least that's what I've found on the web).
If you initialize the control with an empty constructor it takes 30 as the default age.
Now you simply have to call the setBpm() method of the control to set the current heart rate and the control will react on it.
Because your heart can beat faster than the recommended max heart rate there is an indication that you are over 100% by blinking the text.
Means if your heart rate is higher than 100% it will still show 100% but the numbers will start to blink as long as your heart rate is higher than 100%.
Well maybe that was not a really fancy control but again it has a little feature that was interesting to implement.
Here is also a little video that shows the control in action...
If you are interested in the code you will find it as always on github.
That's it for today and even if I don't know if I will ever use this control I hope someone of you have learned something...enjoy the upcoming weekend...and keep coding... :)