简体   繁体   English

Java Swing:在鼠标悬停时更改背景颜色

[英]Java Swing: change background color on mouse over

I've implemented a simple mouse listener where the background color changes whenever the mouse enters the component (a JPanel), and it reverts back whenever the mouse leaves. 我已经实现了一个简单的鼠标监听器,只要鼠标进入组件(JPanel),背景颜色就会改变,只要鼠标离开,它就会恢复。 This has some problems: 这有一些问题:

  • Sometimes the mouse moves so quick that the mouseExit event is not fired 有时鼠标移动得太快,以至于不会触发mouseExit事件
  • If my component has childs, when the mouse moves to the childs it triggers the mouseExit 如果我的组件有子节点,当鼠标移动到子节点时它会触发mouseExit
  • If I move the mouse over to the childs quickly, the mouseEnter event is not fired 如果我将鼠标快速移动到子节点,则不会触发mouseEnter事件

I'm guessing this is an easy one for Swing veterans. 我猜这对于Swing老兵来说很简单。 Any suggestions on how to fix this? 对于如何解决这个问题,有任何的建议吗? I'd love not to use timers and such... 我不想使用计时器等......

If I move the mouse over to the childs quickly, the mouseEnter event is not fired 如果我将鼠标快速移动到子节点,则不会触发mouseEnter事件

I've never seen this to happen, but if it is an issue then you can handle mouseMoved instead to reset the background. 我从来没有见过这种情况,但如果它是一个问题,那么你可以处理mouseMoved而不是重置背景。

If my component has childs, when the mouse moves to the childs it triggers the mouseExit 如果我的组件有子节点,当鼠标移动到子节点时它会触发mouseExit

Use the following test and the code will only be executed when you leave the components bounds: 使用以下测试,只有在离开组件边界时才会执行代码:

public void mouseExited(MouseEvent e) 
{
    if (! getVisibleRect().contains(e.getPoint()) )
    {
        setBackground(...);
    }
}

There are a number of solutions: 有很多解决方案:

  • Add mouse listeners to the child components. 将鼠标侦听器添加到子组件。 Also container listeners, to add and remove listeners as components are added and removed. 还有容器侦听器,用于在添加和删除组件时添加和删除侦听器。 Unfortunately adding mouse listeners upset bubbling up of mouse events (hideous design). 不幸的是,添加鼠标监听器会扰乱鼠标事件(丑陋的设计)。
  • Add a glass pane over the top. 在顶部添加玻璃窗格。 This is mighty ugly, and forwarding of events always causes problems. 这是非常丑陋的,事件的转发总是会引起问题。
  • Add an AWTEventListener to the default Toolkit and filter through for the events you are interested in. This unfortunately requires a security permission. AWTEventListener添加到默认Toolkit并过滤您感兴趣的事件。遗憾的是,这需要安全权限。
  • Push a custom EventQueue and filter events. 推送自定义EventQueue并过滤事件。 This requires a security permission, put applets and WebStart/JNLP applications get that permission anyway. 这需要安全权限,applet和WebStart / JNLP应用程序无论如何都要获得该权限。

After trying various approaches on a container, without success, I ended up using a Timer . 在对容器尝试各种方法后,没有成功,我最终使用了Timer It didn't help that my container contained elements that already needed mouse listeners on them. 我的容器包含已经需要鼠标监听器的元素并没有帮助。

The timer approach also meant that I could delay the change for a short time. 计时器方法也意味着我可以在短时间内延迟更改。 (In my case, I show additional buttons in a tree node (a container), as well as changing the background.) (在我的例子中,我在树节点(容器)中显示其他按钮,以及更改背景。)

On a mouseEntered() on the container, a Timer is created (if not there already) which repeats every 260 milliseconds. 在容器上的mouseEntered()上,创建一个Timer (如果不存在),每260毫秒重复一次。 On each call of the Timer, it determines whether the mouse is inside the container. 在每次调用Timer时,它确定鼠标是否在容器内。 If so, on the first time it signals mouse-over. 如果是这样,它第一次发出鼠标悬停信号。 If not, it signals non-mouse-over and stops the timer. 如果没有,它会发出非鼠标悬停信号并停止计时器。

In Scala, this is as follows, where the method call to entryExit() encodes whether the mouse is over or not (where multiple calls with the same value have no affect): 在Scala中,这是如下所示,其中方法调用entryExit()编码鼠标是否结束(具有相同值的多个调用没有影响):

abstract class MouseInterpreter(component: JComponent) extends MouseAdapter {
  ...
  private var mouseOverAction: () => Unit   = () => {}
  private var mouseOverTimer: Option[Timer] = None
  ...
  def entryExit(entered: Boolean) // this is an abstract method

  override def mouseEntered(e: MouseEvent) {
    if (mouseOverTimer.isEmpty) {
      val aTimer = new Timer(260, new ActionListener {
        def actionPerformed(e: ActionEvent) {
          mouseOverAction()
        }
      })
      mouseOverTimer = Some(aTimer)
      mouseOverAction = () => {
        mouseOverAction = () => {
          val point = MouseInfo.getPointerInfo.getLocation
          SwingUtilities.convertPointFromScreen(point, component)
          if (component.getVisibleRect.contains(point))
            entryExit(entered = true)
          else {
            entryExit(entered = false)
            aTimer.stop()
            mouseOverTimer = None
            mouseOverAction = () => {}
          }
        }
      }
      aTimer.setRepeats(true)
      aTimer.start()
    }
  }
...
}

I can't reproduce this behavior. 我无法重现这种行为。 Please edit your question to provide a short code sample that demonstrates the problem. 请编辑您的问题以提供演示此问题的简短代码示例。

When I create a JPanel, and put something in it, the JPanel does not get mouseExit when the mouse moves over a child component of the JPanel. 当我创建一个JPanel并在其中放入一些内容时,当鼠标移动到JPanel的子组件上时,JPanel不会获得mouseExit。 I'm guessing that you've added MouseListeners to the children. 我猜你已经将MouseListeners添加到了孩子们身上。

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

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