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

4
我已经实现了一个简单的鼠标监听器,当鼠标进入组件(一个JPanel)时背景颜色会改变,当鼠标离开时则恢复原样。但是这里存在一些问题:
  • 有时鼠标移动得太快,导致鼠标离开事件未被触发
  • 如果我的组件有子元素,在鼠标移动到子元素时它会触发鼠标离开事件
  • 如果我快速地将鼠标移动到子元素上,鼠标进入事件不会被触发
我猜这对于Swing老手来说很容易解决。有没有建议可以解决这个问题?我希望不要使用计时器等方法...
4个回答

6

如果我快速将鼠标移动到子元素上,鼠标进入事件不会被触发。

我从未见过这种情况,但如果确实存在问题,您可以使用mouseMoved事件来重置背景。

如果我的组件有子元素,当鼠标移动到子元素时,它会触发鼠标退出事件。

使用以下测试,代码将仅在您离开组件的边界时执行:

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

如果您将指针从外部移动到包含子元素(带有鼠标监听器),则父容器上的鼠标监听器将不会被触发。慢慢地将其移动到边框区域,它就会被触发。 - Tom Hawtin - tackline
我使用了你的技巧和Tom Hawtin的混合。不幸的是,只有一个人可以得到正确的答案。感谢大家。 - Miguel Ping

3

有几个解决方案:

  • 向子组件添加鼠标监听器。同时,添加容器监听器以在添加和删除组件时添加和删除监听器。不幸的是,添加鼠标监听器会破坏鼠标事件的冒泡(这是可怕的设计)。
  • 在顶部添加玻璃板。这非常丑陋,而事件的转发总是会导致问题。
  • 向默认的Toolkit添加AWTEventListener并过滤出您感兴趣的事件。不幸的是,这需要安全权限。
  • 推送自定义的EventQueue并过滤事件。这需要安全权限,但小程序和WebStart/JNLP应用程序已经获得了该权限。

我已经尝试过玻璃窗格,但没有成功。我能将玻璃窗格应用于简单的JPanels吗?我认为你只能将其应用于JFrames。由于我有多个JPanels,我真的需要将玻璃窗格应用于它们中的每一个。 - Miguel Ping

1

在尝试了各种方法后,都没有成功,我最终使用了一个计时器。我的容器中包含了已经需要鼠标监听器的元素,这让事情更加困难。

计时器方法还意味着我可以短暂延迟变化。(在我的情况下,我在树节点(容器)中显示附加按钮,同时改变背景。)

在容器的mouseEntered()上,如果不存在,则创建一个计时器,每260毫秒重复一次。在计时器的每次调用中,它确定鼠标是否在容器内。如果是,则在第一次信号鼠标悬停。如果不是,则信号非鼠标悬停并停止计时器。

在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()
    }
  }
...
}

0

我无法重现这种行为。请编辑您的问题,提供一个简短的代码示例来演示问题。

当我创建一个JPanel,并在其中放置一些内容时,当鼠标移动到JPanel的子组件上时,JPanel不会获得mouseExit。我猜测您已经为子组件添加了MouseListeners。


是的,你说得对。我尝试给子元素添加鼠标监听器。 - Miguel Ping

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接