Freitag, 13. September 2013

As Pedro Duque Vieira recently mentioned in his blogpost about his DateAxis, Diego Cirujano-Cuesta and me developed a DateAxis, which uses actual Date objects.
I don't want to hesitate to introduce it to you and spend some words on how it works.

As you can see, it allows you to define XYChart.Data with Date objects.

Basically that's all you need to do.
As with other axes, you can either define a lower and upper bound or you can chose to let the axis find out its bounds by itself. Just set autorange to true.

Furthermore you can also use your own tick label formatter or enable or disable animation.

Some words on the implementation

The first important part to implement was the getDisplayPosition(Date) method in order to get the display position for a given date. To do this, we get the percentage value, where the date is "located" between the lower and upper bound. Imagine the lower bound as 0 and the upper bound as 1 and we get a value between 0 and 1. Then we have to multiply this value with the length of the axis and we are basically done.

Here's my implementation:

@Override
public double getDisplayPosition(Date date) {
final double length = getSide().isHorizontal() ? getWidth() : getHeight();
// Get the difference between the max and min date.
double diff = currentUpperBound.get() - currentLowerBound.get();
// Get the actual range of the visible area.
// The minimal date should start at the zero position, that's why we subtract it.
double range = length - getZeroPosition();
// Then get the difference from the actual date to the min date and divide it by the total difference.
// We get a value between 0 and 1, if the date is within the min and max date.
double d = (date.getTime() - currentLowerBound.get()) / diff;
// Multiply this percent value with the range and add the zero offset.
if (getSide().isVertical()) {
return getHeight() - d * range + getZeroPosition();
} else {
return d * range + getZeroPosition();
}
}

The next important method to implement was calculateTickValues(double v, Object range).

Uhh, this was a tricky one ;-).

Depending on the range, we want to display different values. E.g. If we display a large range, ranging over several years, we want each tick label to represent one year, where as if the range ranges only over a few days, we want to display the days, and if it ranges over half a day only, we want to display hours.

So, I defined intervals, which define in which intervals tick labels will be shown, e.g. years, months, 3 months, weeks, days, ... and so on.
Then we find out, which interval suites best for the current range. We do this by keep adding one interval to the lower bound until we have reached the upper bound or until there are too many tick labels. Then we try it with the next smaller interval until we found good one.

As last step, we want to make sure, that years always start on January 1st and months always start on the first date of the month and so on.

As a end result we have a nice list of Dates, which represent the tick marks.
In fact, it is even a little bit trickier, but if you're really interested in the details, have a look at the code ;-).

Last important method is the getTickMarkLabel(Date date), which converts a date to a String representation.

It just guesses what might be the best representation depending on the range / interval, which was used. E.g. if only years are displayed the date values are formatted only to display the year, where as smaller ranges are displayed with days or hours.

If you are in need of a DateAxis, give it a try and I hope you like it!

Sonntag, 23. Juni 2013

I called my project "ExtFX", in the style of the JavaScript Framework "ExtJS".

I also added a permissive license (MIT license) to the code.

The repository also contains some cool interpolators, which are also included in many JavaScript web frameworks. I just translated them to JavaFX.

Besides the actual JAR file with some few controls from this blog, I also uploaded a little samples application and JavaDocs in the downloads section.
The date picker control underwent some changes since the last publication, including a cell factory for the date cells, (which allows for more customization), some bug fixes, cleaner and better documented code, min and max date, and some other minor improvements.
I'd be glad to get some feedback!
Have fun!

Dienstag, 21. Mai 2013

Hey all. It's been a long time, when I published my last "real" control (DatePicker). It's about time to present you a new one, right?

I started to develop a NumberSpinner control based on the official UI specification, because a) I needed such a control and b) I really love to develop controls :-).

I also looked into another one, but wasn't quite happy with it because I couldn't really get it to work stable and it was missing some basic requirements like "If users keep one of the buttons pressed, the value in the field increases or decreases automatically until users release the button" or different button layout.

I did not implement all the requested features of the specification (like Date and Time), but it comes already close.

(Sidenote: I am also currently getting to know Mercurial, so that I will push my work into a public repository sooner or later. For now, I will post my code here.)

So here's how it looks:

The Control class

Because the control is very similar to a JavaFX TextField it is quite obvious to derive the NumberSpinner control from TextField. You also want to have all the properties and methods which a TextField already has, especially:

selectAll()

editableProperty()

alignmentProperty()

prompTextProperty()

I expanded the control by the following methods and properties:

ObjectProperty<Number> value, which holds the current number

ObjectProperty<Number> maxValue, which defines the maximum value

ObjectProperty<Number> minValue, which defines the minimum value

ObjectProperty<Number> stepWidth, which defines the step by which the value is changed

ObjectProperty<NumberStringConverter> numberStringConverter, to convert the Number from and to String

ObjectProperty<HPos> hAlignment, which defines the horizontal position of the text field in relation to the buttons

The Skin class

(Note that I didn't make a behavior class nor did I derive from SkinBase, since both are not public API).

I don't want to go into great detail here, but just will post the code, which is hopefully self-explanatory.

One important thing to note, is that you should really unbind everything and remove all listeners from the control in the dispose() method. Otherwise there could be memory leaks, if for some reason the skin changes.

The ClickRepeater class

Now that's an interesting part.
If you want to implement the requirement "If users keep one of the buttons pressed, the value in the field increases or decreases automatically until users release the button" from the specification, you need a way to make the buttons fire periodically while they are pressed.

Buttons have a state called "armed", which is the state before the button is actually fired, that is while it is pressed.

So, while a button is armed, I set up an endless PauseTransition, which periodically fires the button's ActionEvent after an initial pause of 500ms. This mimics the behavior of the KeyEvent, which is also constantly fired, while a key is pressed.

The CSS file

Finally, to round things up, here's the CSS file for the control.
It does the correct button radius for each possible button location, removes the focus style from the skin's text field and sets the focus style on the whole control instead.

Montag, 13. Mai 2013

Have you ever came to the problem to limit the input in a text field to a maximal length?

Or to restrict the input to specific characters, like allowing only numbers?

Of course there are already some solutions around, but most of them suffer from the problem, that they still allow invalid user input by pasting invalid characters with Ctrl + V or via the context menu, because they only check key events or use some other technique like overriding replaceText.
Here's a simple class that I want to share with you, which let you restrict the input to a regular expression class and also let you set a maximal length, just like in HTML, no matter, if the user pasted or typed it into the text field.
Basically it just listens to text changes and if it is too long or does not match it is either truncated or the old text is set instead.
By the way: the property names (maxLength, restrict) are burrowed from HTML and Adobe Flex respectively.

In order to build the JavaFX application, we use ANT. We kind of have to, because this is currently the only official and supported way to build a JavaFX application (besides the command line tool).
Using only Maven is complicated (especially when it comes to web deployment) and needs some more setup.

Building the jar file

The first important part is to build the class-path for the manifest file. Here we use the class-path, which we created with Maven and write it to the manifest.

The rest of the configuration is mostly taken from the official JavaFX documentation (see there for more information).

The fx:jar task creates the jar file and adds some JavaFX properties to the manifest in order to be able to launch your application.
As fileset we reference our target/classes folder which was generated by Maven.

For just building the jar, this would already be sufficient.

Signing the jar files

If you want to also deploy it in the web you can sign the application using the signjar or the fx:signjar task.
(I had some problems signing a jar with fx:signjar, if that jar doesn't have a META-INF folder).