提问者:小点点

我如何知道自动完成装饰的 JComboBox 中的项目是否被鼠标单击?


我正在使用 SwingX AutoCompleteDecorator 作为 JComboBox。自动完成功能运行良好...

但是我很难确定最终用户选择的时刻;很少保留我的数据。

让我试着解释一下:组合框为每个选择触发一个“comboBoxChanged”-ActionEvent。当用户键入字符并且组合框自动匹配和选择项目时,我必须忽略这些事件。如果用户点击返回键,则会生成“comboBoxEdited”-ActionEvent,我可以保存所选值。很棒;-)

如果使用鼠标打开JComboBox-PopUp并选择项目,则唯一触发的事件是“comboBoxChanged”-ActionEvent(例如使用光标键自动匹配或选择项目时)。鼠标点击事件以某种方式被消耗!?这就是为什么我无法识别最终的鼠标选择。

我怎么能弄清楚呢?我侦听鼠标单击事件的失败尝试记录在此 SSCCE 中:

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;


public class SearchForThePopUpMouseClick extends JPanel
{
  private JComboBox<String> comboBox;

  public SearchForThePopUpMouseClick()
  {
    comboBox = new JComboBox<String>(new String[] { "Anna", "Marc", "Maria", "Marten", "Peter" });
    add(comboBox);
    add(new JTextField("textfield to click"));

    AutoCompleteDecorator.decorate(comboBox);


    comboBox.addActionListener(new ActionListener()
    {
      @Override
      public void actionPerformed(ActionEvent e)
      {
        System.out.println("Action Event with '" + e.getActionCommand() + " " + e.getID() + "'");
      };
    });


    ((Component) comboBox.getUI().getAccessibleChild(comboBox, 0)).addMouseListener(new MouseListener()
    {
      @Override
      public void mouseReleased(MouseEvent e)
      {
        System.out.println(e);
      }
      @Override
      public void mousePressed(MouseEvent e)
      {
        System.out.println(e);
      } 
      @Override
      public void mouseExited(MouseEvent e)
      {
        System.out.println(e);
      }
      @Override
      public void mouseEntered(MouseEvent e)
      {
        System.out.println(e);
      }
      @Override
      public void mouseClicked(MouseEvent e)
      {
        System.out.println(e);
      }
    });
  }


  public static void main(String[] args) throws Exception
  {
    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    SwingUtilities.invokeLater(new Runnable()
    {
      @Override
      public void run()
      {
        SearchForThePopUpMouseClick autoCompletePanel = new SearchForThePopUpMouseClick();
        JFrame frame = new JFrame("SwingX Autocomplete Example");
        frame.add(autoCompletePanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
      }
    });
  }

}

共1个答案

匿名用户

comboBox 没有最终选择的概念:所有选择都具有相等的语义权重,独立于其触发器(鼠标、键盘导航、编程方式、按核心中的第一个字母进行选择)并触发 actionEvent。对于普通和装饰的组合框,行为相同。

这正是您在大多数情况下所需要的:始终对选择做出反应,就好像它是最终的一样(无论这可能意味着什么)

如果你真的想把由mouseEvent触发的选择视为比其他任何东西触发的选择更最终(同样:为了获得良好的用户体验,通常不建议这样做,所以在评估时要非常非常小心),你可以检查actionEvent返回的修饰符:

if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) {
    // triggered by mouse
}

编辑

在评论中看到用例(感谢您提供它们!),意识到我的当心部分在错误的树上吠叫:-)

在这种情况下,鼠标与键盘手势确实具有不同的语义。

  • 键盘:在编辑器中键入以及在弹出窗口中导航表示构建最终选择的过程,特殊键 (Enter) 表示提交
  • 鼠标:在弹出窗口中单击既是选择又是提交

JComboBox不能最佳地支持该用例,触发太多。即使在内部也是如此,例如当使用组合框作为CellEditor时。这部分是由一个魔术客户端属性修复的:

public DefaultCellEditor(final JComboBox comboBox) {
    editorComponent = comboBox;
    comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);

检测到该属性后,BasicComboBoxUI(实际上是 BasicComboPopup)键击导航仅在弹出窗口列表中选择,将列表选择的同步推迟到组合选择,直到通过 Enter 提交。这是部分的,因为前瞻(又名:按第一个字母键入和选择)仍然在组合中立即选择(从而提交)。

简短摘要:已经有一个 swing-internal 用例,它导致了一个已经可用的 swingx 内部解决方案,用于在表中自动完成编辑 - 一个名为 ComboBoxCellEditor 的类。也可以单独使用:

AutoCompleteDecorator.decorate( withEditor );
ComboBoxCellEditor editor = new ComboBoxCellEditor(withEditor);
CellEditorListener listener = new CellEditorListener() {

    @Override
    public void editingStopped(ChangeEvent e) {
        // do commit stuff
    }

    @Override
    public void editingCanceled(ChangeEvent e) {
    }
};
editor.addCellEditorListener(listener);
contentPane.add(withEditor, BorderLayout.SOUTH);