Jfoenix: JFXSnackbar fireEvent issue when closing snackbar to hide previous event and show next one

Created on 17 Apr 2019  路  11Comments  路  Source: sshahine/JFoenix

Hi there,

I do the following which causes an NPE in line JFXSnackbar:179 eventsSet.remove(currentEvent):

  1. snackbar.fireEvent(e1); //e1 should appear for 10 seconds for instance
  2. 5 seconds after showing e1 do:
    2.1. snackbar.close();// To hide e1
    2.2 snackbar.fireEvent(e2); // to show e2

This will produces an NPE right when e1 is going to finish at line 179, i.e., 10 seconds after firing e1.

A workaround for this issue is to check if currentEvent is not null right before removing it from eventsSet like this:

Replace eventsSet.remove(currentEvent); with :

if (currentEvent != null)
                        eventsSet.remove(currentEvent);

Note that there are more issues to resolve when trying to fire more events between the time e1 is issued to the time e1 is finished (10 seconds in this example). However, this workaround is very simple to apply for now.

Bug Enhancement pinned

All 11 comments

I think I ran into this same problem today.

My stack trace looks like this:

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at java.util.concurrent.ConcurrentHashMap.replaceNode(ConcurrentHashMap.java:1106)
    at java.util.concurrent.ConcurrentHashMap.remove(ConcurrentHashMap.java:1097)
    at java.util.concurrent.ConcurrentHashMap$KeySetView.remove(ConcurrentHashMap.java:4569)
    at com.jfoenix.controls.JFXSnackbar.lambda$null$5(JFXSnackbar.java:179)
    at com.jfoenix.controls.JFXSnackbar$$Lambda$11.handle(Unknown Source)
    at javafx.animation.Animation.impl_finished(Animation.java:1132)
    at javafx.animation.AnimationAccessorImpl.finished(AnimationAccessorImpl.java:49)
    at com.sun.scenario.animation.shared.SingleLoopClipEnvelope.timePulse(SingleLoopClipEnvelope.java:103)
    at javafx.animation.Animation.impl_timePulse(Animation.java:1102)
    at javafx.animation.Animation$1.lambda$timePulse$25(Animation.java:186)
    at java.security.AccessController.doPrivileged(Native Method)
    at javafx.animation.Animation$1.timePulse(Animation.java:185)
    at com.sun.scenario.animation.AbstractMasterTimer.timePulseImpl(AbstractMasterTimer.java:344)
    at com.sun.scenario.animation.AbstractMasterTimer$MainLoop.run(AbstractMasterTimer.java:267)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:514)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:498)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:491)
    at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$408(QuantumToolkit.java:319)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$152(WinApplication.java:177)
    at java.lang.Thread.run(Thread.java:748)

I'm trying this

// If there is already an snackbar event showing, close it.
if (snackbar.getCurrentEvent() != null) {
    snackbar.close();
}

    snackbar.enqueue(event);

but trying to close the snackbar seems to be causing the above exception.

@aminabs, you're referring to this line 179 (it's now line 249), right?

@jfoenixadmin, what is the recommended way for SnackBars to interrupt each other?

(If it's helpful, here's what Web SnackBars interrupting each other looks like.)

Yes. I am refering to that line.

@jfoenixadmin, would changing line 249 to check for null, fix this simply?

if (currentEvent != null) {
     eventsSet.remove(currentEvent);
}

Yes. The npe would go away if checking for null as I already mentioned.
However this is not a perfect solution.
There some more issues when you work with the new snackbar then.

@aminabs. thanks for chiming in. Do you mind describing those issues?

In short, there would be problems with appearance of new snacks and/or disappearance of the old one if the new one is fired before the old one retired normally

@TurekBot yeah the np check will fix the exception. However as @aminabs said, there could be issues with entrance/exit of the snack bar.

I'm also experience the same issue described. Is there a workaround not involving changing the SnackBar source code until a fix is made? The way it is now, it seems we cannot use snackbar.close() if there are chances of triggering events before the timeout - am I seeing this right? BTW, thanks a lot for the work on the lib =)

I'm also experience the same issue described. Is there a workaround not involving changing the SnackBar source code until a fix is made? The way it is now, it seems we cannot use snackbar.close() if there are chances of triggering events before the timeout - am I seeing this right? BTW, thanks a lot for the work on the lib =)

I think you can just perform a close animation rather calling close(). The snackbar will be closed after the timeout, so you don't need to worry about it.

Remember that close() will also invoke close animation, which means in this dirty solution, close animation will be played 2 times, this can be expensive.

In first animation (defined by you) you can set the opacity to 0/ set invisible at the end, so that the animation invoke by close() will not affect the GUI.

To play animation to let the user think that the snackbar is closed:

JFXSnackbar noti = new JFXSnackbar();
noti.registerSnackbarContainer(this.root);
noti.getStylesheets().add(getClass().getResource("snackbar.css").toExternalForm());
noti.toFront();
noti.enqueue(new JFXSnackbar.SnackbarEvent(
    new JFXSnackbarLayout(message,
            "CLOSE", action -> {
        Timeline closeAnimation = new Timeline(
                new KeyFrame(
                        Duration.ZERO,
                        e -> noti.toFront(),
                        new KeyValue(noti.opacityProperty(), 1, Interpolator.EASE_IN),
                        new KeyValue(noti.translateYProperty(), 0, Interpolator.EASE_OUT)
                ),
                new KeyFrame(
                        Duration.millis(290),
                        new KeyValue(noti.visibleProperty(), true, Interpolator.EASE_BOTH)
                ),
                new KeyFrame(Duration.millis(300),
                        e -> noti.toBack(),
                        new KeyValue(noti.visibleProperty(), false, Interpolator.EASE_BOTH),
                        new KeyValue(noti.translateYProperty(),
                                noti.getLayoutBounds().getHeight(),
                                Interpolator.EASE_IN),
                        new KeyValue(noti.opacityProperty(), 0, Interpolator.EASE_OUT)
                )
        );
        closeAnimation.setCycleCount(1);
        closeAnimation.play();
    }),
    Duration.seconds(3), null));

will close this issue as it's related to #1101 which I pushed the fix for

Was this page helpful?
0 / 5 - 0 ratings

Related issues

caoyanfeng picture caoyanfeng  路  3Comments

alittwin picture alittwin  路  4Comments

DmitryZagr picture DmitryZagr  路  3Comments

rfenters95 picture rfenters95  路  4Comments

riftshadow picture riftshadow  路  4Comments