Javafx 2 klikken en dubbelklikken

Ik zou graag willen weten of het mogelijk was om de dubbelklik in JavaFX 2 te detecteren? en hoe?

Ik wil een andere gebeurtenis maken tussen een klik en een dubbelklik.

Bedankt


Antwoord 1, autoriteit 100%

Ja, u kunt enkele, dubbele of zelfs meerdere klikken detecteren:

myNode.setOnMouseClicked(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent mouseEvent) {
        if(mouseEvent.getButton().equals(MouseButton.PRIMARY)){
            if(mouseEvent.getClickCount() == 2){
                System.out.println("Double clicked");
            }
        }
    }
});

MouseButton.PRIMARYwordt gebruikt om te bepalen of de linker (meestal) muisknop de gebeurtenis activeert. Lees de api van getClickCount()om te concluderen dat er misschien meerdere klikken zijn, behalve enkele of dubbele. Ik vind het echter moeilijk om onderscheid te maken tussen enkele en dubbele klikgebeurtenissen. Omdat de eerste kliktelling van de dubbelklik ook een enkele gebeurtenis zal verhogen.


Antwoord 2, autoriteit 7%

Hier is nog een stukje code dat kan worden gebruikt als u onderscheid moet maken tussen een enkele klik en een dubbele klik en in beide gevallen een specifieke actie moet ondernemen.

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class DoubleClickDetectionTest extends Application {
    boolean dragFlag = false;
    int clickCounter = 0;
    ScheduledThreadPoolExecutor executor;
    ScheduledFuture<?> scheduledFuture;
    public DoubleClickDetectionTest() {
        executor = new ScheduledThreadPoolExecutor(1);
        executor.setRemoveOnCancelPolicy(true);
    }
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage primaryStage) throws Exception {
        StackPane root = new StackPane();
        primaryStage.setScene(new Scene(root, 400, 400));
        primaryStage.show();
        root.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent e) {
                if (e.getButton().equals(MouseButton.PRIMARY)) {
                    dragFlag = true;
                }
            }
        });
        root.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent e) {
                if (e.getButton().equals(MouseButton.PRIMARY)) {
                    if (!dragFlag) {
                        System.out.println(++clickCounter + " " + e.getClickCount());
                        if (e.getClickCount() == 1) {
                            scheduledFuture = executor.schedule(() -> singleClickAction(), 500, TimeUnit.MILLISECONDS);
                        } else if (e.getClickCount() > 1) {
                            if (scheduledFuture != null && !scheduledFuture.isCancelled() && !scheduledFuture.isDone()) {
                                scheduledFuture.cancel(false);
                                doubleClickAction();
                            }
                        }
                    }
                    dragFlag = false;
                }
            }
        });
    }
    @Override
    public void stop() {
        executor.shutdown();
    }
    private void singleClickAction() {
        System.out.println("Single-click action executed.");
    }
    private void doubleClickAction() {
        System.out.println("Double-click action executed.");
    }
}

Antwoord 3, autoriteit 3%

De reactie van P. Pandey is de eenvoudigste benadering die daadwerkelijk onderscheid maakt tussen enkele en dubbele klik, maar het werkte niet voor mij. Ten eerste geeft de functie “currentTimeMillis” al milliseconden terug, dus delen door 1000 lijkt niet nodig. De onderstaande versie werkte voor mij op een meer consistente manier.

@Override
 public void handle(MouseEvent t) {
        long diff = 0;
        currentTime=System.currentTimeMillis();
        if(lastTime!=0 && currentTime!=0){
            diff=currentTime-lastTime;
            if( diff<=215)
                isdblClicked=true;
            else
                isdblClicked=false;
        }
        lastTime=currentTime;
        System.out.println("IsDblClicked()"+isdblClicked); 
       //use the isdblClicked flag...   
}

Antwoord 4, autoriteit 3%

Als u zich aan Java SE 8 lambda-expressies houdt, ziet dit er ongeveer zo uit:

node.setOnMouseClicked(event -> {
    if(event.getButton().equals(MouseButton.PRIMARY) && event.getClickCount() == 2) {
        handleSomeAction();
    }
});

Als je eenmaal gewend bent aan lambda-expressies, worden ze uiteindelijk begrijpelijker dan de originele klasse-instantiatie en overschrijvende (x) methode. -Naar mijn mening-


Antwoord 5, autoriteit 2%

Hier is hoe ik dubbelklik heb geïmplementeerd

if (e.getEventType().equals(MouseEvent.MOUSE_CLICKED) && !drag_Flag) {
                long diff = 0;
            if(time1==0)
             time1=System.currentTimeMillis();
            else
            time2=System.currentTimeMillis();
            if(time1!=0 && time2!=0)
            diff=time2-time1;
            if((diff/1000)<=215 && diff>0)
            {
                isdblClicked=true;
            }
            else
            {
                isdblClicked=false;
            }
            System.out.println("IsDblClicked()"+isdblClicked); 

}


Antwoord 6, autoriteit 2%

Omdat het standaard niet mogelijk is om een ​​onderscheid te maken tussen een enkele klik en een dubbele klik, gebruiken we de volgende aanpak:

Bij één klik verpakken we de bewerking met één klik in een aborteerbaar uitvoerbaar bestand. Dit uitvoerbare bestand wacht een bepaalde tijd (d.w.z. SINGLE_CLICK_DELAY) voordat het wordt uitgevoerd.

In de tussentijd, als een tweede klik, d.w.z. een dubbelklik, plaatsvindt, wordt de enkele klik afgebroken en wordt alleen de dubbelklik uitgevoerd.

Op deze manier wordt ofwel de enkelklik ofde dubbelklikbewerking uitgevoerd, maar nooit beide.


Hier volgt de volledige code. Om het te gebruiken, hoeven alleen de drie TODOregels te worden vervangen door de gewenste handlers.

private static final int SINGLE_CLICK_DELAY = 250;
private ClickRunner latestClickRunner = null;
private class ClickRunner implements Runnable {
    private final Runnable onSingleClick;
    private boolean aborted = false;
    public ClickRunner(Runnable onSingleClick) {
        this.onSingleClick = onSingleClick;
    }
    public void abort() {
        this.aborted = true;
    }
    @Override
    public void run() {
        try {
            Thread.sleep(SINGLE_CLICK_DELAY);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (!aborted) {
            System.out.println("Execute Single Click");
            Platform.runLater(() -> onSingleClick.run());
        }
    }
}
private void init() {
    container.setOnMouseClicked(me -> {
        switch (me.getButton()) {
            case PRIMARY:
                if (me.getClickCount() == 1) {
                    System.out.println("Single Click");
                    latestClickRunner = new ClickRunner(() -> {
                      // TODO: Single-left-click operation
                    });
                    CompletableFuture.runAsync(latestClickRunner);
                }
                if (me.getClickCount() == 2) {
                    System.out.println("Double Click");
                    if (latestClickRunner != null) {
                        System.out.println("-> Abort Single Click");
                        latestClickRunner.abort();
                    }
                    // TODO: Double-left-click operation
                }
                break;
            case SECONDARY:
                // TODO: Right-click operation
                break;
            default:
                break;
        }
    });
}

Antwoord 7, autoriteit 2%

Ik weet niet zeker of iemand deze OP nog steeds volgt of doorverwijst, maar hieronder is mijn versie van het onderscheiden van enkele klik tot dubbelklik. Hoewel de meeste antwoorden redelijk acceptabel zijn, zou het erg handig zijn als het op een goed herbruikbare manier kan worden gedaan.

Een van de uitdagingen die ik tegenkwam, is de noodzaak om differentiatie met één dubbele klik op meerdere knooppunten op meerdere plaatsen te hebben. Ik kan niet dezelfde repetitieve omslachtige logica op elk knooppunt doen. Het moet op een generieke manier worden gedaan.

Dus ik heb ervoor gekozen om een ​​aangepaste EventDispatcher te implementeren en deze coördinator op knooppuntniveau te gebruiken of ik kan het rechtstreeks toepassen op Scene om het toepasbaar te maken voor alle onderliggende knooppunten.

Hiervoor heb ik een nieuw MouseEvent gemaakt, namelijk ‘MOUSE_DOUBLE_CLICKED’, zodat ik me nog steeds aan de standaard JavaFX-praktijken houd. Nu kan ik de dubbelgeklikte gebeurtenisfilters/-handlers opnemen, net als andere typen muisgebeurtenissen.

node.addEventFilter(CustomMouseEvent.MOUSE_DOUBLE_CLICKED, e->{..<code to handle double_click>..});
node.addEventHandler(CustomMouseEvent.MOUSE_DOUBLE_CLICKED, e->{..<code to handle double_click>..});

Hieronder vindt u de implementatie en volledig werkende demo van deze aangepaste gebeurtenisverzender.

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class DoubleClickEventDispatcherDemo extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        Rectangle box1 = new Rectangle(150, 150);
        box1.setStyle("-fx-fill:red;-fx-stroke-width:2px;-fx-stroke:black;");
        addEventHandlers(box1, "Red Box");
        Rectangle box2 = new Rectangle(150, 150);
        box2.setStyle("-fx-fill:yellow;-fx-stroke-width:2px;-fx-stroke:black;");
        addEventHandlers(box2, "Yellow Box");
        HBox pane = new HBox(box1, box2);
        pane.setSpacing(10);
        pane.setAlignment(Pos.CENTER);
        addEventHandlers(pane, "HBox");
        Scene scene = new Scene(new StackPane(pane), 450, 300);
        stage.setScene(scene);
        stage.show();
        // SETTING CUSTOM EVENT DISPATCHER TO SCENE
        scene.setEventDispatcher(new DoubleClickEventDispatcher(scene.getEventDispatcher()));
    }
    private void addEventHandlers(Node node, String nodeId) {
        node.addEventFilter(MouseEvent.MOUSE_CLICKED, e -> System.out.println("" + nodeId + " mouse clicked filter"));
        node.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> System.out.println("" + nodeId + " mouse clicked handler"));
        node.addEventFilter(CustomMouseEvent.MOUSE_DOUBLE_CLICKED, e -> System.out.println("" + nodeId + " mouse double clicked filter"));
        node.addEventHandler(CustomMouseEvent.MOUSE_DOUBLE_CLICKED, e -> System.out.println(nodeId + " mouse double clicked handler"));
    }
    /**
     * Custom MouseEvent
     */
    interface CustomMouseEvent {
        EventType<MouseEvent> MOUSE_DOUBLE_CLICKED = new EventType<>(MouseEvent.ANY, "MOUSE_DBL_CLICKED");
    }
    /**
     * Custom EventDispatcher to differentiate from single click with double click.
     */
    class DoubleClickEventDispatcher implements EventDispatcher {
        /**
         * Default delay to fire a double click event in milliseconds.
         */
        private static final long DEFAULT_DOUBLE_CLICK_DELAY = 215;
        /**
         * Default event dispatcher of a node.
         */
        private final EventDispatcher defaultEventDispatcher;
        /**
         * Timeline for dispatching mouse clicked event.
         */
        private Timeline clickedTimeline;
        /**
         * Constructor.
         *
         * @param initial Default event dispatcher of a node
         */
        public DoubleClickEventDispatcher(final EventDispatcher initial) {
            defaultEventDispatcher = initial;
        }
        @Override
        public Event dispatchEvent(final Event event, final EventDispatchChain tail) {
            final EventType<? extends Event> type = event.getEventType();
            if (type == MouseEvent.MOUSE_CLICKED) {
                final MouseEvent mouseEvent = (MouseEvent) event;
                final EventTarget eventTarget = event.getTarget();
                if (mouseEvent.getClickCount() > 1) {
                    if (clickedTimeline != null) {
                        clickedTimeline.stop();
                        clickedTimeline = null;
                        final MouseEvent dblClickedEvent = copy(mouseEvent, CustomMouseEvent.MOUSE_DOUBLE_CLICKED);
                        Event.fireEvent(eventTarget, dblClickedEvent);
                    }
                    return mouseEvent;
                }
                if (clickedTimeline == null) {
                    final MouseEvent clickedEvent = copy(mouseEvent, mouseEvent.getEventType());
                    clickedTimeline = new Timeline(new KeyFrame(Duration.millis(DEFAULT_DOUBLE_CLICK_DELAY), e -> {
                        Event.fireEvent(eventTarget, clickedEvent);
                        clickedTimeline = null;
                    }));
                    clickedTimeline.play();
                    return mouseEvent;
                }
            }
            return defaultEventDispatcher.dispatchEvent(event, tail);
        }
        /**
         * Creates a copy of the provided mouse event type with the mouse event.
         *
         * @param e         MouseEvent
         * @param eventType Event type that need to be created
         * @return New mouse event instance
         */
        private MouseEvent copy(final MouseEvent e, final EventType<? extends MouseEvent> eventType) {
            return new MouseEvent(eventType, e.getSceneX(), e.getSceneY(), e.getScreenX(), e.getScreenY(),
                    e.getButton(), e.getClickCount(), e.isShiftDown(), e.isControlDown(), e.isAltDown(),
                    e.isMetaDown(), e.isPrimaryButtonDown(), e.isMiddleButtonDown(),
                    e.isSecondaryButtonDown(), e.isSynthesized(), e.isPopupTrigger(),
                    e.isStillSincePress(), e.getPickResult());
        }
    }
}

Antwoord 8

Een oplossing met PauseTransition:

PauseTransition singlePressPause = new PauseTransition(Duration.millis(500));
singlePressPause.setOnFinished(e -> {
    // single press
});
node.setOnMousePressed(e -> {
    if (e.isPrimaryButtonDown() && e.getClickCount() == 1) {
        singlePressPause.play();
    }
    if (e.isPrimaryButtonDown() && e.getClickCount() == 2) {
        singlePressPause.stop();
        // double press
    }
});

Antwoord 9

Een alternatief voor een enkele klik versus een dubbele klik die ik gebruik, is een enkele klik versus ingedrukt houden (ongeveer een kwart tot een halve seconde of zo), en laat dan de knop los. De techniek kan een afbreekbare timer met schroefdraad gebruiken, zoals in sommige van de bovenstaande codefragmenten om onderscheid te maken tussen de twee. Ervan uitgaande dat de feitelijke gebeurtenisafhandeling plaatsvindt bij het loslaten van de knop, heeft dit alternatief het voordeel dat een enkele klik normaal werkt (dwz zonder enige vertraging), en bij ingedrukt houden kunt u de gebruiker wat visuele feedback geven wanneer de knop is ingedrukt. lang genoeg vastgehouden om te worden vrijgegeven (dus er is nooit onduidelijkheid over welke actie werd uitgevoerd).


Antwoord 10

Als u test hoeveel muisknoppen (==2) er worden ingedrukt, codeer deze dan niet in de submethode! De volgende werkt:

listView.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                if( mouseEvent.getButton().equals(MouseButton.SECONDARY)) {
                    System.out.println("isSecondaryButtonDown");
                    mouseEvent.consume();
                    // ....
                }
                else
               if(mouseEvent.getButton().equals(MouseButton.PRIMARY)){
                    if(mouseEvent.getClickCount() == 2){
                        System.out.println("Double clicked");
                       // mousePressedInListViewDC(mouseEvent);
                    }
                    else
                    if(mouseEvent.getClickCount() == 1){
                        System.out.println("1 clicked");
                        mousePressedInListView1C(mouseEvent);
                    }
               }
            }
        })

;


Antwoord 11

Ik liep tegen hetzelfde probleem aan, en wat me opviel, is dat enkele en dubbele klik WORDEN onderscheiden met basic :

   Button btn = new Button("Double click me too");
    btn.setOnMousePressed(mouseEvent -> {
        // CLICK catches
        if (mouseEvent.getClickCount() == 1) {
            System.out.println("Button clicked");
        } else if (mouseEvent.getClickCount() == 2)
            System.out.println("Button double clicked");
    });

Maar een ‘enkele’ klik wordt opgevangen als onderdeel van de dubbele klik. Dus je ziet op de console:

Voornamelijk gebruik makend van het antwoord van @markus-weninger, heb ik een Class-extension Buttonopgebouwd om 2 nieuwe EventHandlers bloot te leggen:

  • setOnMouseSingleClicked(EventHandler<MouseEvent> eventHandler)
  • setOnMouseDoubleClicked(EventHandler<MouseEvent> eventHandler)

Dus met de volledige voorbeeldcode hieronder, als we dubbelklikken op de laatste knop, krijgen we:

Houd in gedachten:

  1. Het voor de hand liggende nadeel is dat zelfs een enkele klik die wordt opgevangen met setOnMouseSingleClickedwordt vertraagd met de singleClickDelayMillis(blootgestelde variabele die moet worden ingesteld overeenkomstig het besturingssysteem, zoals vermeld door Kleopatra).
  2. Een ander opvallend feit is dat ik Buttonheb uitgebreid, en niet Nodewaar het zou moeten zijn: de klasse waar de onMouseClicked(…) is geïmplementeerd.
  3. Als laatste opmerking heb ik besloten om een ​​nieuwe EventHandler toe te voegen in plaats van de bestaande setOnMousePressed, setOnMouseReleasedof setOnMouseClickedte gebruiken, zodat de ontwikkelaar kan deze handige EventHandlers nog steeds volledig implementeren. Bijvoorbeeld om direct antwoord te krijgen van een klik op de knop zonder te wachten op de singleClickDelayMillis. Maar dit betekent dat als u beide implementeert, de setOnMouseClickedzelfs bij een dubbelklik wordt geactiveerd… pas op.

Hier komt de code:

import java.util.concurrent.CompletableFuture;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.beans.property.ObjectProperty;
import javafx.event.EventHandler;
import javafx.beans.property.SimpleObjectProperty;
public class DblClickCatchedWithoutSingleClick extends Application {
public class ButtonWithDblClick extends Button {
    private long        singleClickDelayMillis  = 250;
    private ClickRunner latestClickRunner       = null;
    private ObjectProperty<EventHandler<MouseEvent>>    onMouseSingleClickedProperty    = new SimpleObjectProperty<>();
    private ObjectProperty<EventHandler<MouseEvent>>    onMouseDoubleClickedProperty    = new SimpleObjectProperty<>();
    // CONSTRUCTORS
    public ButtonWithDblClick() {
        super();
        addClickedEventHandler();
    }
    public ButtonWithDblClick(String text) {
        super(text);
        addClickedEventHandler();
    }
    public ButtonWithDblClick(String text, Node graphic) {
        super(text, graphic);
        addClickedEventHandler();
    }
    private class ClickRunner implements Runnable {
        private final Runnable  onClick;
        private boolean         aborted = false;
        public ClickRunner(Runnable onClick) {
            this.onClick = onClick;
        }
        public void abort() {
            this.aborted = true;
        }
        @Override
        public void run() {
            try {
                Thread.sleep(singleClickDelayMillis);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (!aborted) {
                Platform.runLater(onClick::run);
            }
        }
    }
    private void addClickedEventHandler() {
        //Handling the mouse clicked event (not using 'onMouseClicked' so it can still be used by developer).
        EventHandler<MouseEvent> eventHandler = me -> {
            switch (me.getButton()) {
                case PRIMARY:
                    if (me.getClickCount() == 1) {
                        latestClickRunner = new ClickRunner(() -> {
                            System.out.println("ButtonWithDblClick : SINGLE Click fired");
                            onMouseSingleClickedProperty.get().handle(me);
                        });
                        CompletableFuture.runAsync(latestClickRunner);
                    }
                    if (me.getClickCount() == 2) {
                        if (latestClickRunner != null) {
                            latestClickRunner.abort();
                        }
                        System.out.println("ButtonWithDblClick : DOUBLE Click fired");
                        onMouseDoubleClickedProperty.get().handle(me);
                    }
                    break;
                case SECONDARY:
                    // Right-click operation. Not implemented since usually no double RIGHT click needs to be caught.
                    break;
                default:
                    break;
            }
        };
        //Adding the event handler
        addEventHandler(MouseEvent.MOUSE_CLICKED, eventHandler);
    }
    public void setOnMouseSingleClicked(EventHandler<MouseEvent> eventHandler) {
        this.onMouseSingleClickedProperty.set(eventHandler);
    }
    public void setOnMouseDoubleClicked(EventHandler<MouseEvent> eventHandler) {
        this.onMouseDoubleClickedProperty.set(eventHandler);
    }
    public long getSingleClickDelayMillis() {
        return singleClickDelayMillis;
    }
    public void setSingleClickDelayMillis(long singleClickDelayMillis) {
        this.singleClickDelayMillis = singleClickDelayMillis;
    }
}
public void start(Stage stage) {
    VBox root = new VBox();
    Label lbl = new Label("Double click me");
    lbl.setOnMouseClicked(mouseEvent -> {
        // CLICK catches
        if (mouseEvent.getClickCount() == 2) {
            System.out.println("Label double clicked");
        } else if (mouseEvent.getClickCount() == 1)
            System.out.println("Label clicked");
    });
    Button btn = new Button("Double click me too");
    btn.setOnMousePressed(mouseEvent -> {
        // CLICK catches
        if (mouseEvent.getClickCount() == 1) {
            System.out.println("Button clicked");
        } else if (mouseEvent.getClickCount() == 2)
            System.out.println("Button double clicked");
    });
    ButtonWithDblClick btn2 = new ButtonWithDblClick("Double click me three ;-)");
    btn2.setOnMouseSingleClicked(me -> {
        System.out.println("BUTTON_2 : Fire SINGLE Click");
    });
    btn2.setOnMouseDoubleClicked(me -> {
        System.out.println("BUTTON_2 : Fire DOUBLE Click");
    });
    root.getChildren().add(lbl);
    root.getChildren().add(btn);
    root.getChildren().add(btn2);
    Scene scene = new Scene(root);
    stage.setScene(scene);
    stage.show();
}
public static void main(String[] args) {
    launch();
}

}

Other episodes