如何为 JButton 的翻转效果设置关闭延迟?

How to set dismiss delay on JButton's rollover effect?

通过类比 ToolTipManager setDismissDelay(int milliseconds) 方法,我想为 JButton 的翻转效果实现关闭延迟。

在我的 swing 应用程序中,我为我的 JButton 设置了不同的图标(setIcon、setPressedIcon 和 setRolloverIcon 方法),但我正在尝试解决在按下应该打开模式对话框的特定 JButton 时发生的问题. 当按下按钮并显示模态对话框时,jbutton 仍然显示滚动图标,即使我将 "normal" 图标传递给 setPressedIcon 方法。 此外,滚动图标不会消失,直到光标 returns 到主框架,即使 jdialog 已关闭。

我举了一个例子来说明我的意思。我只在主框架中放置了两个按钮,每个按钮都有一个绿色方形图标作为 "normal" 图标,以及一个红色图标用于翻转效果。 正如我所说,我希望按钮在按下时再次显示绿色图标。第一个按钮的行为为 "wrongly",因为红色图标在 jdialog 创建后可见。 对于第二个按钮,我通过在按下按钮时调用 setRollover (false) 解决了这个问题,覆盖了 isPressed () 方法(在其 DefaultButtonModel 中)。

我认为这不是最佳解决方案,我不想直接对 ButtonModel 进行操作。 所以我想知道您是否有更好的主意,也许类似于我之前所说的 setDismissDelay 方法。谢谢!

这里有一个 SSCE:

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.DefaultButtonModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SSCE
{
    public static void main (String[] a) {
        SwingUtilities.invokeLater (new Runnable () {
            public void run () {
                JFrame frame = new JFrame ("Icon Test");
                frame.setContentPane (new MainPanel (frame));
                frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
                frame.setResizable (false);
                frame.pack ();
                frame.setLocationRelativeTo (null);
                frame.setVisible (true);
            }
        });
    }
}
class MainPanel extends JPanel
{
    public MainPanel (JFrame parent) {
        JButton firstButton = createButton (createButtonImage (Color.GREEN), createButtonImage (Color.RED), parent);
        JButton secondButton = createButton (createButtonImage (Color.GREEN), createButtonImage (Color.RED), parent);
        secondButton.setModel (new DefaultButtonModel () {
            @Override public boolean isPressed () {
                boolean isPressed = super.isPressed ();
                if (isPressed) setRollover (false);
                return isPressed;
            }
        });
        add (firstButton);
        add (secondButton);
    }
    private JButton createButton (BufferedImage normalImage, BufferedImage rolloverImage, final JFrame parent) {
        ImageIcon normalIcon = new ImageIcon (normalImage), rolloverIcon = new ImageIcon (rolloverImage);
        JButton button = new JButton (new AbstractAction () {
            public void actionPerformed (ActionEvent e) {
                JDialog dialog = new JDialog (parent, "Test Dialog",true);
                dialog.setSize (400, 400);
                dialog.setLocationRelativeTo (parent);
                dialog.setVisible (true);
            }
        });
        button.setBorderPainted (false);
        button.setCursor (Cursor.getPredefinedCursor (Cursor.HAND_CURSOR));
        button.setFocusPainted (false);
        button.setContentAreaFilled (false);
        button.setIcon (normalIcon);    
        button.setPressedIcon (normalIcon);
        button.setRolloverEnabled (true);
        button.setRolloverIcon (rolloverIcon);
        return button;
    }
    private BufferedImage createButtonImage (Color color) {
        BufferedImage image = new BufferedImage (20, 20, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics ();
        g.setColor (color);
        g.fillRect (0, 0, 20, 20);
        g.dispose ();
        return image;
    }
}

编辑

正如@camickr 所建议的,我尝试将 ActionListener 代码包装在 SwingUtilities.invokeLater () 中。

我不会重新发布完整代码,我只替换了那些行:

JButton button = new JButton (new AbstractAction () {
                public void actionPerformed (ActionEvent e) {
                    JDialog dialog = new JDialog (parent, "Test Dialog",true);
                    dialog.setSize (400, 400);
                    dialog.setLocationRelativeTo (parent);
                    dialog.setVisible (true);
                }
            });

与 :

JButton button = new JButton ();
    button.addActionListener (new ActionListener () {
            public void actionPerformed (ActionEvent e) {
                SwingUtilities.invokeLater (new Runnable () {
                    public void run () {
                        JDialog dialog = new JDialog (parent, "Test Dialog",true);
                        dialog.setSize (400, 400);
                        dialog.setLocationRelativeTo (parent);
                        dialog.setVisible (true);
                    }
                });
            }
        });

但是,这并没有解决我的问题,创建对话框时红色图标仍然可见。 我尝试了一些小的调整,使用 addActionListener 或 setAction,也只在 invokeLater 调用中调用 setVisible,但它仍然不起作用。

此外,如果不使用我现在正在使用的 ButtonModel 上的相同代码,我怎么能使用 Timer? 我已经通过在 actionPerformed 中设置 "normal icon" 然后用 "custom" ActionEvent 调用另一个 Action 尝试了一些 "hacks",但我想要一个 "clean" 解决方案。

侦听器中的所有代码都在 Event Dispatch Thread (EDT) 上执行。

问题是在调用 ActionListener 代码之前按钮的状态没有改变。显示模态对话框后,直到对话框关闭才会执行按钮状态更改代码。

ActionListener 中的代码包装在 SwingUtilities.invokeLater() 中。此代码将添加到 EDT 的末尾,允许在显示对话框之前完成正常的按钮处理。

阅读 Swing 教程中有关 Swing 并发的部分,了解有关 EDT 的更多信息。

编辑:

i would prefer not to act directly on ButtonModel

花更多时间研究代码。问题是显示对话框时没有生成 mouseExited,因此 ButtonModel 的状态永远不会更新。

另一种选择可能是为 mouseExited 事件手动生成 MouseEvent 并在显示对话框之前将事件分派给按钮。

虽然这种方法也被认为是 hack。

how could i use a Timer

同样,问题出在按钮的状态上。即使您使用计时器,您也需要手动重置模型的状态。

您当前的解决方案似乎是合理的,因为所有逻辑都位于自定义行为的 class 中。