简体   繁体   English

Java:忽略单击双击?

[英]Java : ignore single click on double click?

can anyone think of a good way to ignore the single click that comes with a double-click in Java?谁能想出一个好方法来忽略 Java 中双击所带来的单击?

I'm looking to have different behaviors for each such that:我希望每个行为都有不同的行为:

  • single-click paints crosshairs on the click point单击在单击点上绘制十字准线
  • double-click selects an object on the screen, but should not paint crosshairs on the click point双击在屏幕上选择一个 object,但不应在单击点上绘制十字准线

... can anyone think of a way to do this? ...任何人都可以想办法做到这一点吗? Some sort of timer set-up maybe?也许是某种计时器设置? An ideas appreciated:-)一个想法赞赏:-)

<disclaimer>...and yes, I know I'm committing a most heinous usability / UI faux pas. <免责声明>...是的,我知道我正在犯下最令人发指的可用性/用户界面失礼。 </disclaimer> </免责声明>

EDIT #2:编辑#2:

Even though this works the delay due to the timer is maddening - I'm abandoning this solution, and using middle-click for selection instead of double-click...即使这有效,但由于计时器引起的延迟令人抓狂 - 我放弃了这个解决方案,并使用中键单击而不是双击进行选择......

EDIT:编辑:

Thanks cgull - this is what I was able to come up with given your confirmation that there's no easy way to do this (note that if I set the timer < 200 odd racing is seen between the click & the timer, but as long as I set this to a value > 200 things work just peachy):谢谢cgull - 这就是我能够想出的,因为你确认没有简单的方法来做到这一点(请注意,如果我设置计时器 < 200 奇怪的赛车在点击和计时器之间看到,但只要我将其设置为 > 200 的值就可以了):

public void mouseClicked(MouseEvent e) {
    System.out.println( "Click at (" + e.getX() + ":" + e.getY() + ")" );
    if (e.getClickCount() == 2) {  
        System.out.println( "  and it's a double click!");
        wasDoubleClick = true;
    }else{
        Integer timerinterval = (Integer) 
          Toolkit.getDefaultToolkit().getDesktopProperty(
                      "awt.multiClickInterval");
        timer = new Timer(timerinterval.intValue(), new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                if (wasDoubleClick) {
                    wasDoubleClick = false; // reset flag
                } else {
                    System.out.println( "  and it's a simple click!");
                }
            }    
        });
        timer.setRepeats(false);
        timer.start();
    }
}

Indeed you'll need to set up a Timer in your overridden mouseClicked() method of your MouseAdapter to detect the time interval between the two clicks. 实际上,您需要在MouseAdapter的重写mouseClicked()方法中设置一个Timer,以检测两次点击之间的时间间隔。 The default interval in ms can be found by querying Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval") . 可以通过查询Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval")找到以ms为单位的默认时间间隔。 If another mouse click is detected before the timer expires, then you have a double-click, else once the timer expires, you can process the single-click. 如果在计时器到期之前检测到另一次鼠标单击,则双击,否则计时器到期后,您可以单击一次。

Actually I think there is a simpler solution (use InputEvent's getWhen() method): 实际上我认为有一个更简单的解决方案(使用InputEvent的getWhen()方法):

class DCListener extends MouseAdapter{

    private long maxTimeBetweenClicks; // in millis
    private long firstClickTime=0;
    private Runnable eventHandler;

    public DCListener(long maxTimeBetweenClicks,Runnable eventHandler){
        this.maxTimeBetweenClicks=maxTimeBetweenClicks;
        this.eventHandler=eventHandler;
    }

    public void mouseClicked(MouseEvent e){

        if((e.getWhen()-firstClickTime)<=maxTimeBetweenClicks){
            firstClickTime=0; // 3 clicks are not 2 double clicks
            eventHandler.run();
        }else{
            firstClickTime=e.getWhen();
        }

    }
}

An alternative solution: 替代解决方案:

I figured out this before I found the solution in this question. 在我在这个问题中找到解决方案之前,我想出了这一点。 The idea is the same, use a timer, although more complicated :). 这个想法是一样的,使用计时器,虽然更复杂:)。

Use SwingWorker : 使用SwingWorker

class BackgroundClickHandler extends SwingWorker<Integer, Integer> {
  @Override
  protected Integer doInBackground() throws Exception {
    Thread.sleep(200);
    // Do what you want with single click
    return 0;
  }

}

In mouseClicked() method you can do something like this: mouseClicked()方法中,您可以执行以下操作:

 if (event.getClickCount() == 1) {
  // We create a new instance everytime since
  // the execute() method is designed to be executed once
  clickHandler = new BackgroundClickHandler();

  try {
    clickHandler.execute();
  } catch(Exception e) {
    writer.println("Here!");
  }
}

if (event.getClickCount() == 2) {
  clickHandler.cancel(true);

  //Do what you want with double click
}

I hope it be useful. 我希望它有用。

Have you tried implementing the MouseListener interface already? 您是否尝试过实现MouseListener接口?

I think MouseEvent has a click count method ( or property ) to know that. 我认为MouseEvent有一个点击计数方法(或属性)来了解它。

I bet you have gone through that already, so what is the problem you're facing there? 我敢打赌你已经经历过了,所以你面临的问题是什么?

Probably what you can do is to code the time interval elapsed between a single and a double click with a thread or something. 你可以做的就是用线程或其他东西对单次和双击之间经过的时间间隔进行编码。

So a single click will only be valid if another click is not issued in let's say 300 ms. 因此,如果没有发出另一次点击(例如300毫秒),则单击即可生效。 ( something configurable ) (可配置的东西)

The idea is: 这个想法是:

public void listen for the single click() 
    if (  in x time there has not been another click  ) 
    then 
        we have a single click
        proceed with your single click handling
    else 
        we have a double click
       proceed with your double click handling

Or something like that. 或类似的东西。

Thanks to this thread I made this simillar solution, that is the way I would imagine it.感谢这个线程,我做了这个类似的解决方案,这就是我想象的方式。 It keeps a Timer thread (where single click events are scheduled with a 200ms delay) alive all the time - this could upset someone, but afterall starting a thread is the expensive stuff, isn't it?它使Timer线程(其中单击事件被安排有 200 毫秒的延迟)始终保持活动状态 - 这可能会让某些人不高兴,但毕竟启动线程是昂贵的东西,不是吗?

Track of the last TimerTask is kept and it's cancel() method is called if a double click arrives.保留最后一个TimerTask的轨迹,如果双击到达,则调用它的cancel()方法。 A double click cancels its single click if it arrives within 200ms, or has no effect if late.如果双击在 200 毫秒内到达,则双击将取消其单击,如果迟到则无效。

I use JavaFX, but that should not matter.我使用 JavaFX,但这应该无关紧要。 The core stuff is the Timer and TimerTask .核心的东西是TimerTimerTask

import java.util.Timer;
import java.util.TimerTask;
import javafx.fxml.FXML;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.MouseButton;

class SomeController {
    private static final long MAX_MULTICLICK_INTERVAL = 200; //the milliseconds delay
    private final Timer singleLeftClickTimer = new Timer(true); //isDeamon = true, will be terminated with the rest of the app
    private TimerTask lastSingleClickTask;

    private void onMouseClickedFiltered(MouseEvent event) {
        //TODO process clicks
        //For the primary button you get only single clicks that were not followed by double clicks within 200ms
        //Double clicks and all clicks of other buttons go through unchanged
    }

    @FXML
    private void onMouseClicked(MouseEvent event) {
        if (event.getButton() != MouseButton.PRIMARY) {
            onMouseClickedFiltered(event);
        } else if (event.getClickCount() == 2) {
            lastSingleClickTask.cancel(); //if 200ms passed from associated single click, this will have no effect
            onMouseClickedFiltered(event);
        } else if (event.getClickCount() == 1) {
            lastSingleClickTask = new TimerTask() {
                @Override
                public void run() {
                    onMouseClickedFiltered(event);
                }
            };
            singleLeftClickTimer.schedule(lastSingleClickTask, MAX_MULTICLICK_INTERVAL);
        } // else: might swallow primary button clicks with clickCount outside 1 and 2 which is ok for me
    }
}

Looking up the answer to this question I was thinking about a different solution.查找此问题的答案时,我正在考虑一个不同的解决方案。 Couldn't find it so I share my thoughts:找不到所以我分享我的想法:

The delay the other solutions introduce in the single click processing are a little painful because you get this delayed response.其他解决方案在单击处理中引入的延迟有点痛苦,因为您会得到这种延迟响应。 However those solutions are the simplest to implement and don't bother the rest of the code.然而,这些解决方案是最简单的实现方式,不会打扰代码的 rest。

The other option is:另一个选择是:

All events that a single click can produce are Undoable and are undone once an asociated double click event arrives .单击可以产生的所有事件都是可撤消的,并且一旦关联的双击事件到达就会撤消

Implementing Undo is an interesting topic, but for simplicity I will give a very basic example instead:实现撤消是一个有趣的话题,但为了简单起见,我将给出一个非常基本的示例:

import javafx.fxml.FXML;
import javafx.scene.input.MouseEvent;

class SomeController {

    private static final long MAX_MULTICLICK_INTERVAL = 200; //the milliseconds delay
    private long lastSingleClickTime = 0;

    private int singleClickCounter = 0, doubleClickCounter = 0;

    private void singleClick(MouseEvent event) {
        singleClickCounter++;
    }

    private void undoSingleClick(MouseEvent event) {
        singleClickCounter--;
    }

    private void doubleClick(MouseEvent event) {
        doubleClickCounter++;
    }

    @FXML
    private void onMouseClicked(MouseEvent event) {
        if (event.getClickCount() == 2) {
            if (System.currentTimeMillis() <= lastSingleClickTime + MAX_MULTICLICK_INTERVAL) {
                undoSingleClick(event);
                lastSingleClickTime = 0; //if another double-click arrived we don't want to undo anything
            }
            doubleClick(event);
        } else if (event.getClickCount() == 1) {
            lastSingleClickTime = System.currentTimeMillis(); //in AWT use the event.getWhen() method instead
            singleClick(event);
        } // else: might swallow button clicks with clickCount outside 1 and 2 which is ok for me
        System.out.printf("Single clicks: %d, double clicks: %d\n", singleClickCounter, doubleClickCounter);
    }
}

Note that primary, secondary and middle button events are mixed for simplicity here.请注意,为简单起见,这里混合了主要、次要和中间按钮事件。 You should track a lastSingleClickTime for each of the buttons or limit the processing to the primary button only, since that is usually the only button using double-clicks anyway.您应该为每个按钮跟踪一个lastSingleClickTime或将处理限制为仅对主按钮进行处理,因为无论如何这通常是唯一使用双击的按钮。

With a data model implementing Undo one would remember the Action the single click fired (let that be a class having a run() and undo() method doing opposite things) and call .undo() on that Action instance when the double click event arrived.使用数据 model 实现撤消,人们会记住单击触发的Action (假设是 class 具有run()undo()方法做相反的事情)并在双击事件时调用该Action实例上的.undo()到达的。 There are pitfalls of course.当然有陷阱。 But that would be a long chapter.但这将是一个很长的章节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM