Java 处理JDialog和JToggleButton的状态和事件序列

Java 处理JDialog和JToggleButton的状态和事件序列,java,swing,jdialog,jtogglebutton,focuslistener,Java,Swing,Jdialog,Jtogglebutton,Focuslistener,我试图构建一个JToggleButton,当按下JToggleButton时,它会显示一个JDialog(其中包含一个JList)。当再次按下JToggleButton时,或者如果用户在画面中的某个地方或远处单击时,让JDialog消失(当焦点丢失时,我通过JList上的FocusListener模拟) 按顺序按下按钮将正确显示和隐藏JDialog 但是,问题是当JDialog可见时,我单击帧上的其他位置,焦点丢失时JDialog会正确地消失。但是,JToggleButton的状态仍然错误地设置

我试图构建一个
JToggleButton
,当按下
JToggleButton
时,它会显示一个
JDialog
(其中包含一个
JList
)。当再次按下
JToggleButton
时,或者如果用户在画面中的某个地方或远处单击时,让
JDialog
消失(当焦点丢失时,我通过
JList
上的
FocusListener
模拟)

按顺序按下按钮将正确显示和隐藏
JDialog

但是,问题是当
JDialog
可见时,我单击帧上的其他位置,焦点丢失时
JDialog
会正确地消失。但是,
JToggleButton
的状态仍然错误地设置为选中状态。这意味着现在单击
JToggleButton
将不会显示
JDialog
,因为
JToggleButton
的状态现在不同步。相反,我需要按两次
JToggleButton
,才能再次看到
JDialog
。下面的代码示例演示了这个问题

我似乎无法使
JList
失去的焦点与
JToggleButton
的状态同步。这似乎是一个直截了当的问题,但我一直在努力寻找解决方案。有人能帮忙吗?谢谢

请参见下面的我的代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;

public class MultiComboBox extends JToggleButton
{
    public MultiComboBox(JFrame frame, String buttonText)
    {
        super(buttonText);

        JDialog dialog = new JDialog(frame, false);
        dialog.setLayout(new BorderLayout());

        Object[] items = new Object[] { "one", "two", "three" };
        JList list = new JList(items);
        list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        JScrollPane listScrollPane = new JScrollPane(list,
            JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        listScrollPane.setPreferredSize(list.getPreferredSize());

        dialog.add(listScrollPane, BorderLayout.CENTER);
        dialog.pack();

        addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                final JToggleButton button = (JToggleButton) e.getSource();
                System.out.println("button clicked: " + button.isSelected());
                if (button.isSelected())
                {
                    Point p = button.getLocation();
                    p.setLocation(p.getX() + 300, p.getY());
                    SwingUtilities.convertPointToScreen(p, button);
                    dialog.setLocation(p);
                    dialog.setVisible(true);
                }
                else
                    dialog.setVisible(false);
            }
        });

        list.addFocusListener(new FocusListener()
        {
            @Override
            public void focusGained(FocusEvent e)
            {
            }

            @Override
            public void focusLost(FocusEvent e)
            {
                System.out.println("list focusLost, dialog: " + dialog.isVisible());
                dialog.setVisible(false);
            }
        });
    }

    public static void main(String[] args)
    {
        JFrame frame = new JFrame("Test");
        frame.setPreferredSize(new Dimension(300, 300));
        frame.setLayout(new BorderLayout());

        MultiComboBox mcb = new MultiComboBox(frame, "Toggle");

        JPanel buttonPanel = new JPanel(new BorderLayout());
        buttonPanel.setPreferredSize(new Dimension(80, 30));
        buttonPanel.add(mcb, BorderLayout.CENTER);

        JPanel blankPanel = new JPanel(new BorderLayout());
        frame.add(blankPanel, BorderLayout.CENTER);

        frame.add(buttonPanel, BorderLayout.PAGE_START);
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }
}
建议:

  • 不要将ActionListener添加到JToggleButton
  • 而是添加一个ItemListener。这将响应切换选择状态的更改
  • 在此侦听器中,更改对话框的可见状态
  • 在FocusListener中,不要更改对话框的可见状态,而是更改切换的选择状态
  • 使用添加到JDialog本身的WindowFocusListener,在其失去焦点时收到通知。通过这种方式,侦听器代码可以位于对话框组件的代码之外,这是一种更干净的OOPs解决方案
例如:

import javax.swing.*;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

public class MultiComboBox2 extends JToggleButton {
    public MultiComboBox2(JFrame frame, String buttonText) {
        super(buttonText);

        JDialog dialog = new JDialog(frame, false);
        dialog.setLayout(new BorderLayout());

        Object[] items = new Object[] { "one", "two", "three" };
        JList list = new JList(items);
        list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        JScrollPane listScrollPane = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        listScrollPane.setPreferredSize(list.getPreferredSize());

        dialog.add(listScrollPane, BorderLayout.CENTER);
        dialog.pack();

        addItemListener(new ItemListener() {

            @Override
            public void itemStateChanged(ItemEvent e) {
                final JToggleButton button = (JToggleButton) e.getSource();
                if (e.getStateChange() == ItemEvent.SELECTED) {
                    Point p = button.getLocation();
                    p.setLocation(p.getX() + 300, p.getY());
                    SwingUtilities.convertPointToScreen(p, button);
                    dialog.setLocation(p);
                    dialog.setVisible(true);
                } else {
                    dialog.setVisible(false);
                }
            }
        });
        // addActionListener(new ActionListener() {
        // @Override
        // public void actionPerformed(ActionEvent e) {
        // final JToggleButton button = (JToggleButton) e.getSource();
        // System.out.println("button clicked: " + button.isSelected());
        // if (button.isSelected()) {
        // Point p = button.getLocation();
        // p.setLocation(p.getX() + 300, p.getY());
        // SwingUtilities.convertPointToScreen(p, button);
        // dialog.setLocation(p);
        // dialog.setVisible(true);
        // } else
        // dialog.setVisible(false);
        // }
        // });

        list.addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent e) {
            }

            @Override
            public void focusLost(FocusEvent e) {
                System.out.println("list focusLost, dialog: " + dialog.isVisible());
                // dialog.setVisible(false);
                MultiComboBox2.this.setSelected(false);
            }
        });
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("Test");
        frame.setPreferredSize(new Dimension(300, 300));
        frame.setLayout(new BorderLayout());

        MultiComboBox2 mcb = new MultiComboBox2(frame, "Toggle");

        JPanel buttonPanel = new JPanel(new BorderLayout());
        buttonPanel.setPreferredSize(new Dimension(80, 30));
        buttonPanel.add(mcb, BorderLayout.CENTER);

        JPanel blankPanel = new JPanel(new BorderLayout());
        frame.add(blankPanel, BorderLayout.CENTER);

        frame.add(buttonPanel, BorderLayout.PAGE_START);
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }
}

谢谢你的建议,但不幸的是,这仍然是一个问题。使用示例代码,
JDialog
会在焦点丢失后消失,但它引入了一个新问题,单击
JToggleButton
会使
JDialog
始终可见;而不是以前的行为,每次选择/取消选择
JToggleButton
时,
JDialog
会出现并消失。