【问题标题】:How can the mouse listener activate after a certain time when the mouse is already over the button?当鼠标已经在按钮上时,鼠标侦听器如何在一段时间后激活?
【发布时间】:2017-11-05 10:00:15
【问题描述】:

我试图在计时器结束后在反应按钮上出现一个边框。除非我将鼠标从触发按钮上移开然后再移回它,否则我似乎不会发生这种情况。有没有办法在不移动鼠标的情况下激活鼠标侦听器,然后在计时器之后再回到触发按钮上?请不要说:计时器结束时设置边框,因为那不是我想要的。 另外,请随时指出我的代码中的其他错误或坏习惯。我是 Java 编码新手。

import java.util.Timer;
import java.util.TimerTask;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class ButtonTester{

  public static final Border PANEL_BORDER = new LineBorder(Color.red, 12);
  public static JPanel panel;
  public static JButton trigger;
  public static JButton react;
  public static JLabel msg;

  public static void main(String [] args){
    JFrame frame = new JFrame();
    frame.setSize(new Dimension(500,200)); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    panel = new JPanel();
    panel.setBorder(PANEL_BORDER);
    frame.getContentPane().add(panel);

    JButton trigger = new JButton("Trigger");
    JButton react = new JButton("React");
    JLabel msg = new JLabel();
    react.setPreferredSize(new Dimension(200, 60));
    trigger.setPreferredSize(new Dimension(200, 60));

    panel.add(trigger); 
    panel.add(react);
    panel.add(msg);
    panel.setVisible(true);
    frame.setVisible(true);

    MouseListener mL = new MouseAdapter(){
      @Override public void mouseEntered(MouseEvent evt) {
        react.setBorder(PANEL_BORDER);
      }
      @Override public void mouseExited(MouseEvent evt) {
        react.setBorder(javax.swing.BorderFactory.createEmptyBorder());
      }
    };
    countDown(msg, trigger, mL);
  }

  public static void countDown(JLabel msg, JButton trigger, MouseListener mL){
    Timer timer = new Timer();
    TimerTask task = new TimerTask(){
      short seconds = 4; 
      public void run(){
        if(seconds == 0){
          timer.cancel(); 
          trigger.addMouseListener(mL); 
          return;
        }
        seconds--;
        msg.setText("Attempting to add listener in : "+seconds);
      }
    };
    timer.scheduleAtFixedRate(task, 1000, 1000);
  }

}

【问题讨论】:

  • “另外,请随时指出我的代码中的其他错误或坏习惯。我是 Java 编码新手” - 不要使用 java.util.Timer当您想与 UI 交互时,请使用 SwingWorker 或 javax.swing.Timer。我也避免使用setPreferredSize,它没有考虑getPreferredSize 用来计算其价值的许多条件。相反,考虑在JButton 上使用setMargin 或不同布局管理器中可用的不同布局约束
  • “请不要说:在计时器结束时设置边框,因为这不是我想要的” - 好的,但这听起来和你的完全一样重新询问 - “我正试图让反应按钮在计时器结束后出现带边框” ...所以我很困惑
  • 所以,你“似乎”要说的是,如果在你设置了MouseAdapter 之后,鼠标悬停在trigger 按钮上,那么你希望听众是触发?相反,您可以先应用侦听器,然后可以有两个标志,一个确定鼠标是否进入或退出按钮,一个确定计时器是否已完成,这样,您可以结合二。可能需要额外的方法或观察者将这两种状态结合在一起,但基本思想应该可行

标签: java swing mouseevent border mouselistener


【解决方案1】:

好的,这个例子设置了两个状态变量,一个用来判断鼠标是进入还是退出按钮,另一个用来判断定时器是否完成。

如果这两个条件为true,则设置边框。

这意味着当计时器用完时,如果鼠标不在trigger按钮上,react按钮的边框将不会改变,但如果用户移回按钮,它将被改变.它也会在鼠标悬停在trigger 按钮上并且计时器用完时进行更改

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class TestPane extends JPanel {

        public static final Border PANEL_BORDER = new LineBorder(Color.red, 12);

        private boolean mouseInTheHouse = false;
        private boolean timedOut = false;

        private JButton react;
        private JButton trigger;

        public TestPane() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.ipadx = 200;
            gbc.ipady = 60;
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            react = new JButton("React");
            trigger = new JButton("Trigger");

            add(react, gbc);
            add(trigger, gbc);

            trigger.addMouseListener(new MouseAdapter() {

                @Override
                public void mouseEntered(MouseEvent e) {
                    mouseInTheHouse = true;
                    stateChanged();
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    mouseInTheHouse = false;
                }

            });
            Timer timer = new Timer(4000, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    timedOut = true;
                    System.out.println("!!");
                    stateChanged();
                }
            });
            timer.start();
        }

        protected void stateChanged() {
            if (mouseInTheHouse && timedOut) {
                react.setBorder(PANEL_BORDER);
            }
        }

    }

}

请注意,我没有设置鼠标离开trigger 按钮时应该发生的情况的条件,但我假设您会重置边框。

我明白了。我还有一个问题。如果我有 10 个触发按钮(面板顶部)和 10 个反应按钮(面板底部)怎么办?条件是:如果我将鼠标悬停在其中一个触发按钮上,则相同位置的相应反应按钮加上该反应按钮右侧的反应按钮将有边框。如何在不循环通过我的按钮列表和检测 mouseInHouse 的情况下检测到这一点?

基本上,将想法提炼到最常见的水平。您有两个按钮,一个 Timer、MouseListener 和两个状态变量。将它们包装成一个公共类,然后您可以重用它。

public class ButtonStateManager {

    private boolean mouseInTheHouse = false;
    private boolean timedOut = false;

    private JButton trigger;
    private JButton react;

    private Timer timer;

    public ButtonStateManager(JButton trigger, JButton react, int timeOut) {
        this.trigger = trigger;
        this.react = react;

        trigger.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseEntered(MouseEvent e) {
                mouseInTheHouse = true;
                stateChanged();
            }

            @Override
            public void mouseExited(MouseEvent e) {
                mouseInTheHouse = false;
            }

        });

        Timer timer = new Timer(timeOut, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                timedOut = true;
                stateChanged();
            }
        });
    }

    protected void stateChanged() {
        if (mouseInTheHouse && timedOut) {
            react.setBorder(TestPane.PANEL_BORDER);
        }
    }

}

现在,这假设两个按钮之间存在关系。

【讨论】:

  • 我明白了。我还有一个问题。如果我有 10 个触发按钮(面板顶部)和 10 个反应按钮(面板底部)怎么办?条件是:如果我将鼠标悬停在其中一个触发按钮上,则相同位置的相应反应按钮加上该反应按钮右侧的反应按钮将有边框。如何在不循环通过我的按钮列表和检测 mouseInHouse 的情况下检测到这一点?
  • 其实,没关系。我使用 MouseInfo 来获取位置,使用组件方法 findComponentAt() 并检查它的实例,然后调用 mouseEnter。不过谢谢!
  • 基本思想是将 MouseAdpter 和 ActionListener 包装到一个可重用的类中。然后可以使用它来管理状态
【解决方案2】:

如果你的鼠标在trigger按钮上,然后MouseListener被添加到按钮上,它就无法捕获之前发生的进入trigger按钮区域的事件。

如果你在鼠标停留在trigger按钮上时坚持显示边框,没有下车再上去,你应该在添加mouseListener之后手动调用mouseEnter方法:

(在此之前您应该将final JFrame frame 传递给您的countDown 方法)

if(seconds == 0){
      timer.cancel(); 
      trigger.addMouseListener(mL);
      Point mousePosition = MouseInfo.getPointerInfo().getLocation();
      Rectangle triggerRect = trigger.getBounds();
      Rectangle frameRect = frame.getBounds();
      Rectangle newRect = new Rectangle(triggerRect.x + frameRect.x, triggerRect.y + frameRect.y, triggerRect.width, triggerRect.height);
      if(newRect.contains(mousePosition)) {
          mL.mouseEntered(new MouseEvent(trigger, 0, System.currentTimeMillis(), 1, 0, 0, 0, false));
      }
      return;
}

但是正如 MadProgrammer 提到的那样,您的问题并不清楚 “我正在尝试让反应按钮在计时器结束后显示带有边框”。

希望这会有所帮助!

【讨论】:

  • 如果鼠标实际上不在按钮上会发生什么?
  • @MadProgrammer:非常值得考虑!我应该检查鼠标是否真的在按钮区域,然后调用mouseEntered 方法。谢谢!
  • 我认为是两个,但我认为有一个更简单的方法;)
  • 其实@STaefi,这是一个非常好的解决方案。我正在尝试使用 10 个触发按钮和 10 个反应按钮而不循环将其应用于我的情况,因此对于我想要使用它的方式来说效率不高。我宁愿跳过循环。
  • @PhongVo 是的事件或动作侦听器是一些方法,在某些接口中最有可能设计为public 方法,可以与其他方法类似地调用。实际上,在某些情况下或由于操作而调用其网络的侦听器的注册机制是某些方法调用。
猜你喜欢
  • 1970-01-01
  • 2012-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-14
  • 2013-07-27
相关资源
最近更新 更多