JavaFX button with ripple effect

I watched material design principle from google and I wanted to try implement some of it in JavaFX.
At first I tried to create ripple effect. I picked button class, learn internal code from JavaFX and this is result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
public class MaterialDesignButton extends Button {
private Circle circleRipple;
private Rectangle rippleClip = new Rectangle();
private Duration rippleDuration = Duration.millis(250);
private double lastRippleHeight = 0;
private double lastRippleWidth = 0;
private Color rippleColor = new Color(0, 0, 0, 0.11);
public MaterialDesignButton(String text) {
super(text);
getStyleClass().addAll("md-button");
createRippleEffect();
}
@Override
protected Skin<?> createDefaultSkin() {
final ButtonSkin buttonSkin = new ButtonSkin(this);
// Adding circleRipple as fist node of button nodes to be on the bottom
this.getChildren().add(0, circleRipple);
return buttonSkin;
}
private void createRippleEffect() {
circleRipple = new Circle(0.1, rippleColor);
circleRipple.setOpacity(0.0);
// Optional box blur on ripple - smoother ripple effect
//circleRipple.setEffect(new BoxBlur(3, 3, 2));
// Fade effect bit longer to show edges on the end of animation
final FadeTransition fadeTransition = new FadeTransition(rippleDuration, circleRipple);
fadeTransition.setInterpolator(Interpolator.EASE_OUT);
fadeTransition.setFromValue(1.0);
fadeTransition.setToValue(0.0);
final Timeline scaleRippleTimeline = new Timeline();
final SequentialTransition parallelTransition = new SequentialTransition();
parallelTransition.getChildren().addAll(
scaleRippleTimeline,
fadeTransition
);
// When ripple transition is finished then reset circleRipple to starting point
parallelTransition.setOnFinished(event -> {
circleRipple.setOpacity(0.0);
circleRipple.setRadius(0.1);
});
this.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
parallelTransition.stop();
// Manually fire finish event
parallelTransition.getOnFinished().handle(null);
circleRipple.setCenterX(event.getX());
circleRipple.setCenterY(event.getY());
// Recalculate ripple size if size of button from last time was changed
if (getWidth() != lastRippleWidth || getHeight() != lastRippleHeight)
{
lastRippleWidth = getWidth();
lastRippleHeight = getHeight();
rippleClip.setWidth(lastRippleWidth);
rippleClip.setHeight(lastRippleHeight);
// try block because of possible null of Background, fills ...
try {
rippleClip.setArcHeight(this.getBackground().getFills().get(0).getRadii().getTopLeftHorizontalRadius());
rippleClip.setArcWidth(this.getBackground().getFills().get(0).getRadii().getTopLeftHorizontalRadius());
circleRipple.setClip(rippleClip);
} catch (Exception e) {
}
// Getting 45% of longest button's length, because we want edge of ripple effect always visible
double circleRippleRadius = Math.max(getHeight(), getWidth()) * 0.45;
final KeyValue keyValue = new KeyValue(circleRipple.radiusProperty(), circleRippleRadius, Interpolator.EASE_OUT);
final KeyFrame keyFrame = new KeyFrame(rippleDuration, keyValue);
scaleRippleTimeline.getKeyFrames().clear();
scaleRippleTimeline.getKeyFrames().add(keyFrame);
}
parallelTransition.playFromStart();
});
}
public void setRippleColor(Color color) {
circleRipple.setFill(color);
}
}

I don’t describe the code because of comments that you can find in more important fragments.
This is only test case class, not production ready.

You can also watch how it look in practise:

I inserted sample application from video on github -> https://github.com/nonameplum/md-button-fx-sample

Have fun.

JavaFX eventbus using Akka

Last days I started to look more at reactive programming, especially that I am familiar with Play Framework.
One of the successor on this area is akka framework - Scala/Java implementation of actor-based runtime for managing concurrency.

I started to think how it can be useful for JavaFX application. One of the think what I came up with is EventBus.
Creating EventBus using akka I’m getting reactive/concurrent message passing for free.
The events/messages are sent using the internal threading management.
What I do is that when actor receives message, then I perfrom Platform.runLater to pass this event to JavaFX thread.

This is my starting point of learning akka, so take a look at my code and share your thoughts.

You can find my implementation of akka-eventbus for JavaFX here:
https://github.com/nonameplum/akka-eventbus-javafx

Also please take a look at usage example:
https://github.com/nonameplum/akka-eventbus-javafx-example

My fork of jidefx-oss

I spend some time on looking deeper into validation in JavaFx. I found some useful API in ControlsFX, Jide-oss and FXValidation.

I found that on my point of view the best API is in Jide-oss. But Jide-oss library was last updated in February 2014 and was not compatible with jdk1.8.0_20.

Fortunately only a few corrections had to do to fix the problem.
Next I started to look into missing features that I think can be useful.

Here you can find my fork: https://github.com/nonameplum/jidefx-oss

First of all I wanted to add posibility to easy customization for decoration of validation. Now you can do this by passing your custom lambda that will create decorator or use one of prepared by me decoration creators defined in ValidationDecorators class (for now graphic and FontAwesome). Also you can easly customize look and feel of validated controls and validators by css.

The next thing that I missed was the ability to easily check status of few validators. It can be useful for define disabled property e.g. button. For that purpose you can use ValidationGroup class.

Use case example:

1
2
ValidationGroup validationGroup = new ValidationGroup(emailField, passwordField);
signUpButton.disableProperty().bind(validationGroup.invalidProperty());

I added also some utils methods e.g. to:

  • get decorators from control
  • show tooltip manualy on validation decorator
  • install required decrator

A brief description of the changes:

  • validation support for datepicker
  • better ability to css customization
  • more freedom of decoration creation (support for lambda expression)
  • ability to create decoration using FontAwesome font.
  • support for observable status of validation group
  • some helper methods

You can check example project witch I modified to show new API features (code changes mostly in ValidationDemo class)

My changes was also merged by Jide-oss into orignal repo - pull request: https://github.com/jidesoft/jidefx-oss/pull/21

JavaFX tooltip event propagation quick tip

Using tooltips in different environments than JavaFx, like VLC Delphi or C# WPF, I’m used to behaviour that when user click outside of tooltip or tooltip owner then tooltip is hidden and also event is transmitted to original event target.
First part (hiding) working as expected but event propagation not. But JavaFX developers not forget about this and created opportunity to push event further.
All you have to do is to change property using method setConsumeAutoHidingEvents to false:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Specifies whether the event, which caused the Popup to hide, should be
* consumed. Having the event consumed prevents it from triggering some
* additional UI response in the Popup's owner window.
* @defaultValue true
* @since JavaFX 2.2
*/
private BooleanProperty consumeAutoHidingEvents =
new SimpleBooleanProperty(this, "consumeAutoHidingEvents",
true);
public final void setConsumeAutoHidingEvents(boolean value) {
consumeAutoHidingEvents.set(value);
}

As you can see consumeAutoHidingEvents property default value is true, so event is not propagated. This code is from PopupWindow class and Tooltip class inherits this property.
In so far as it may be appropriate for popupWindow, it is not good solution for tooltip, where do you not expect to click two times, first one only to hide tooltip and second time to make action on some another control.

JavaFX tooltip styleable parent bug

Few days ago I found problem with tooltip styling.
I wanted to show tooltip on demand (when TextField get focus) and I also wanted to style this tooltip using custom css like this:

1
2
3
4
5
6
7
8
9
.label-with-tooltip .tooltip {
-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 10, 0, 0, 0);
-fx-font-weight: bold;
-fx-padding: 5;
-fx-border-width:1;
-fx-background-color: #FBEFEF;
-fx-text-fill: #cc0033;
-fx-border-color:#cc0033;
}

I got result that when I hovered mouse on TextField then my style was applied correctly but when I show tooltip on focus event using tooltip.show(...) then tooltip didn’t get my style.

This is due to getStyleableParent method of Tooltip class with is responsible for style “propagation”. In Tooltip class getStyleableParent looks like this:

1
2
3
public Styleable getStyleableParent() {
return BEHAVIOR.hoveredNode;
}

BEHAVIOR is static class responsible for properly showing and hiding tooltip when mouse is over node. hoveredNode property is set in mouse move event handler on the node that have installed tooltip supported by BEHAVIOR class. Therefore, when you use show method to manually show tooltip then BEHAVIOR.hoveredNode property is not set and also you don’t have chance to change it, because BEHAVIOR is private class.

I found solution by overriding getStyleableParent and returning ownerNode which is set when you show tooltip using method of Tooltip class:

1
2
3
public void show(Node ownerNode, double anchorX, double anchorY) {
...
}

Here is example application where you see how it works:

JavaFX tooltip fix example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public class Main extends Application {
public static class TooltipFix extends Tooltip {
public TooltipFix(String text) {
super(text);
}
@Override
public Styleable getStyleableParent() {
Styleable styleableParent = super.getStyleableParent();
// if default behavior is not returning styleable parent
// then return owner node
if (styleableParent != null) {
return styleableParent;
} else {
return getOwnerNode();
}
}
}
@Override
public void start(Stage primaryStage) throws Exception{
GridPane gridPane = new GridPane();
Parent root = gridPane;
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
primaryStage.getScene().getStylesheets().clear();
primaryStage.getScene().getStylesheets().add(getClass().getResource("style.css").toExternalForm());
Label lblTooltipBug = new Label("Tooltip bugged");
lblTooltipBug.getStyleClass().add("label-with-tooltip");
// original tooltip
Tooltip tooltipBug = new Tooltip("I'm broken");
lblTooltipBug.setTooltip(tooltipBug);
gridPane.add(lblTooltipBug, 0, 0);
Label lblTooltipFix = new Label("Tooltip fixed");
lblTooltipFix.getStyleClass().add("label-with-tooltip");
gridPane.add(lblTooltipFix, 0, 1);
// fixed tooltip
TooltipFix tooltipFix = new TooltipFix("I'm repaired");
lblTooltipFix.setTooltip(tooltipFix);
Button btnShowTooltip = new Button("Show tooltips");
btnShowTooltip.setOnAction(event -> {
showTooltip(lblTooltipBug);
showTooltip(lblTooltipFix);
});
gridPane.add(btnShowTooltip, 0, 2);
gridPane.setVgap(20);
gridPane.setAlignment(Pos.CENTER);
}
public static void showTooltip(Label label) {
if (label != null && label.getTooltip() != null) {
// offset
Point2D point = label.localToScene(100, 0);
label.getTooltip().setAutoHide(true);
label.getTooltip().show(label, point.getX()
+ label.getScene().getX() + label.getScene().getWindow().getX(), point.getY()
+ label.getScene().getY() + label.getScene().getWindow().getY());
}
}
public static void main(String[] args) {
launch(args);
}
}

Welcome on my blog

Hi, my name is Lukas Sliwinski, I’m developer for about 10 years and I live in Poland.

On a daily basis I work in Delphi language creating Windows desktop applications (yes, it can be still powerful :)) in company that is a specialized manufacturer and supplier of comprehensive systems mainly for large and medium-sized enterprises.
In my work I also sometimes create software using other languages, mostly Java, C#, Javascript and Objective-C.

I like learn new technologies and languages. Mostly Scala language, Play! framework, node.js and JavaFX.

Within these mentioned areas you will be able to hopefully learn something new here.

Best regards,
Lukas.