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

Java Swing:在鼠标悬停时更改背景色,java,swing,mouse,background,mouseout,Java,Swing,Mouse,Background,Mouseout,我已经实现了一个简单的鼠标侦听器,当鼠标进入组件(JPanel)时,背景颜色会改变,当鼠标离开时,背景颜色会恢复。这有一些问题: 有时,鼠标移动得太快,以至于没有触发mouseExit事件 如果我的组件有child,当鼠标移动到child时,它会触发mouseExit 如果我将鼠标快速移动到孩子的身上,则不会触发mouseEnter事件 我猜这对摇摆老手来说很容易。有没有关于如何解决这个问题的建议?我不想使用计时器之类的…有很多解决方案: 将鼠标侦听器添加到子组件。还有容器侦听器,用于在添

我已经实现了一个简单的鼠标侦听器,当鼠标进入组件(JPanel)时,背景颜色会改变,当鼠标离开时,背景颜色会恢复。这有一些问题:

  • 有时,鼠标移动得太快,以至于没有触发mouseExit事件
  • 如果我的组件有child,当鼠标移动到child时,它会触发mouseExit
  • 如果我将鼠标快速移动到孩子的身上,则不会触发mouseEnter事件

我猜这对摇摆老手来说很容易。有没有关于如何解决这个问题的建议?我不想使用计时器之类的…

有很多解决方案:

  • 将鼠标侦听器添加到子组件。还有容器侦听器,用于在添加和删除组件时添加和删除侦听器。不幸的是,添加鼠标侦听器打乱了鼠标事件的冒泡(可怕的设计)
  • 在顶部添加一个玻璃窗格。这是非常丑陋的,事件的转发总是会引起问题
  • AWTEventListener
    添加到默认的
    Toolkit
    中,并筛选出您感兴趣的事件。不幸的是,这需要安全许可
  • 推送自定义
    EventQueue
    并过滤事件。这需要一个安全权限,put小程序和WebStart/JNLP应用程序无论如何都会获得该权限

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

      当我创建一个JPanel并在其中放入一些东西时,当鼠标移动到JPanel的子组件上时,JPanel不会得到mouseExit。我猜你给孩子们增加了鼠标听筒

      如果我把鼠标移到孩子们身上 很快,mouseEnter事件就不存在了 发射

      我从未见过这种情况发生,但如果这是一个问题,那么您可以处理mouseMoved来重置背景

      如果我的组件有child,则 鼠标移动到它所触发的孩子 穆塞西特

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

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

      在容器上尝试了各种方法之后,没有成功,我最终使用了计时器。我的容器包含已经需要鼠标侦听器的元素,这对我没有帮助

      计时器方法也意味着我可以将更改延迟一小段时间。(在本例中,我在树节点(容器)中显示其他按钮,并更改背景。)

      在容器上的mouseenterned()上,会创建一个计时器(如果还没有),该计时器每260毫秒重复一次。每次调用计时器时,它都会确定鼠标是否在容器内。如果是这样的话,在第一次发出鼠标悬停的信号时。如果没有,它会发出非鼠标移动的信号并停止计时器

      在Scala中,这如下所示,其中对entryExit()的方法调用对鼠标是否结束进行编码(其中具有相同值的多个调用没有影响):


      我试过玻璃窗,但没有用。我可以将玻璃窗格应用于简单的JPanel吗?我以为你只能用在JFrames上。因为我有多个JPanel,所以我真的需要对每个JPanel应用玻璃窗格。是的,你是对的。我尝试将鼠标侦听器添加到子对象。如果将指针从外部移动到包含的子对象(使用鼠标侦听器),则父容器上的鼠标侦听器将不会被触发。慢慢地把它移到边界区域,它就会移动。我把你的技术和汤姆·霍丁的技术结合起来。不幸的是,你们中只有一个人能得到正确的答案。谢谢大家。
      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()
          }
        }
      ...
      }