Как я узнаю, если щелкнуть мышью по элементу JComboBox с автозаполнением?

Я использую SwingX AutoCompleteDecorator для JComboBox, Функция автозаполнения работает прекрасно...

Но мне сложно определить момент окончательного выбора пользователя; сохранять мои данные редко.

Позвольте мне попытаться объяснить: поле со списком запускает "comboBoxChanged"-ActionEvent для каждого выбора. Я должен игнорировать эти события, пока пользователь печатает символы, а выпадающий список автоматически сопоставляет и выбирает элементы. Если пользователь нажимает клавишу возврата "comboBoxEdited"-ActionEvent генерируется, и я могу сохранить выбранное значение. Большой;-)

Если мышь используется для открытия JComboBox-PopUp и чтобы выбрать элемент, единственное событие, которое вызывается, это "comboBoxChanged"-ActionEvent (например, при автоматическом сопоставлении или выборе элемента с помощью клавиш курсора). Событие, вызываемое мышью, расходуется как-то!? Вот почему я не могу определить окончательный выбор мыши.

Как я могу понять это? Мои неудачные попытки прослушивания mouseClicked-Event задокументированы в этом 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. То же самое поведение для простого и украшенного comboBox.

Это именно то, что вам нужно в большинстве контекстов: всегда реагируйте на выбор, как если бы он был окончательным (что бы это ни значило)

Если в вашем случае вы действительно хотите считать выбор, инициированный mouseEvent, более окончательным, чем выбор, инициированный чем-либо еще (опять же: это обычно не рекомендуется для хорошего пользовательского опыта, поэтому будьте очень, очень осторожны в своей оценке), вы можете проверить модификаторы, возвращаемые actionEvent:

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

редактировать

Увидев варианты использования (спасибо за их предоставление!) В комментариях, понял, что мои опасения частично лают на неправильное дерево:-)

В этом контексте жест "мышь против клавиатуры" действительно имеет разную семантику.

  • клавиатура: ввод в редакторе, а также навигация во всплывающем окне обозначают процесс создания окончательного выбора со специальной клавишей (ввод), обозначающей коммит
  • мышь: нажатие во всплывающем окне одновременно выбирает и фиксирует

JComboBox не поддерживает этот вариант использования оптимально, слишком много стреляя. Это проблема даже внутренне, например, при использовании comboBox в качестве CellEditor. Это частично исправлено волшебным clientProperty:

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

Обнаружив это свойство, навигация keyStrokes BasicComboBoxUI (фактически BasicComboPopup) выбирает только в списке всплывающее окно, откладывая синхронизацию listSelection с comboSelection до тех пор, пока не будет зафиксировано с помощью enter. Это частично, потому что упреждающий просмотр (он же: печатать и выбирать по первой букве) по-прежнему выбирает (и, следовательно, фиксирует) сразу в комбо.

Краткое резюме: уже есть внутренний сценарий использования Swing, который приводит к уже доступному внутреннему решению 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);
Другие вопросы по тегам