Question

I am trying to configure certain actions in response to number of mouse clicks. It seems to me that the single click and triple click get detected and applied. but the double click does not really work. I tried to do something like:

if (doubleClick)
else if (tripleClick)
else if (singleClick).

The order of checking did not help either, the action for the double click never gets triggered because that of the single click get triggered first. Any ideas on how to do this?

Was it helpful?

Solution 2

If you want to detect each of the different click counts, you could use a switch statement.

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

/**
 *
 * @author jeffreyguenther
 */
public class ListTest extends Application{

    @Override
    public void start(Stage stage) throws Exception {
        Circle c = new Circle();
        c.setRadius(100);
        c.setCenterX(100);
        c.setCenterY(100);
        c.setFill(Color.AQUA);
        c.setStroke(Color.BLACK);
        c.setStrokeWidth(3);

        c.setOnMousePressed((e) ->{

            switch(e.getClickCount()){
                case 1:
                    System.out.println("One click");
                    break;
                case 2:
                    System.out.println("Two clicks");
                    break;
                case 3:
                    System.out.println("Three clicks");
                    break;
            }

        });

        stage.setScene(new Scene(new Group(c)));
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Then as you single, double, and triple click this output is given:

One click
Two clicks
Three clicks

You can use the case statements to determine which counts you want to handle. For example, if you only want to handle the double and triple clicks, you can remove the first case.

OTHER TIPS

Assuming you are doing something like

if (mouseEvent.getClickCount()==1) 

etc, then it's probably not doing what you think. MouseEvent.getClickCount() just returns the number of clicks that have occurred in a "small" region and in a "small" amount of time. "Small" is (deliberately) not defined.

So a double click is just two clicks. The first returns 1 for getClickCount(), then second returns 2. Similarly, a triple click is three clicks: the first returns 1, the next 2, the third 3. You can test this with a very simple piece of code:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class ClickCountTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        Pane root = new Pane();
        root.setOnMouseClicked(event -> System.out.println(event.getClickCount()));
        primaryStage.setScene( new Scene(root, 250, 150) );
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

If you click, you'll see the output "1". If you double click, you'll see a "1" followed by a "2".

There's no built-in way to ignore the first click if it's part of a double (or triple) click. The issue, obviously, is that at the time of the first click, there's no way of knowing if another click is going to come without some kind of timing, which gets pretty complicated and would force a delay on responding to any mouse click.

There was some talk a while back about implementing an onClickSequenceFinished type of event, so that instead of listening for mouse click events, you could listen for the click sequence finished event and then query that event to find the number of clicks. In the end, it was decided not to support this as the use case was not considered good UI programming practice.

The reason for that is that it's a pretty bad idea for, say, a double click to exclude the action of a single click. If the user is just too slow with their double click, then they will inadvertently invoke the single click action (twice). So if you are supporting both double click and single click actions, then the actions should be chosen so that it makes sense for the single-click action to be invoked any time the double-click action is invoked. The typical example is a ListView, where double-clicking a list element opens a "details" editor, and single-clicking selects the item. It makes sense for the item being edited to also be selected, so the double-click action implies the single-click action.

Put another way, it's considered a bad design if a double click action is designed to exclude a single click action, and that idiom is not directly supported. You should consider using modifier keys instead of click counts for this kind of distinction.

Update: If you really want to distinguish events by click count like this (and I really don't recommend it), then you can use something like a PauseTransition to implement the timer. Something like:

import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class ClickCountTest extends Application {

    @Override
    public void start(Stage primaryStage) {

        // This value may need tuning:
        Duration maxTimeBetweenSequentialClicks = Duration.millis(500);

        PauseTransition clickTimer = new PauseTransition(maxTimeBetweenSequentialClicks);
        final IntegerProperty sequentialClickCount = new SimpleIntegerProperty(0);
        clickTimer.setOnFinished(event -> {
            int count = sequentialClickCount.get();
            if (count == 1) System.out.println("Single click");
            if (count == 2) System.out.println("Double click");
            if (count == 3) System.out.println("Triple click");
            if (count > 3) System.out.println("Multiple click: "+count);
            sequentialClickCount.set(0);
        });

        Pane root = new Pane();
        root.setOnMouseClicked(event -> {
            sequentialClickCount.set(sequentialClickCount.get()+1);
            clickTimer.playFromStart();
        });
        primaryStage.setScene( new Scene(root, 250, 150) );
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

This is also probably a good use case for Tomas Mikula's ReactFX framework, (also see his blog post).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top