Как избежать запуска события actionlistener JComboBox, когда элемент динамически добавляется в Java? - PullRequest
4 голосов
/ 10 марта 2011

Мне нужны ваши предложения и рекомендации по следующему заданию.

У меня есть фрейм, в котором есть два JComboBox, предполагается, что они называются combo1 и combo2, JTable и другие компоненты.

На начальном этапе, когда виден кадр с указанным выше компонентом. Поле со списком combo1 заполнено некоторыми значениями, но на начальном этапе значение не выбрано, поле со списком combo2 отключено и таблица пуста.

Я добавил actionListener для combo1 и combo2. В combo1 есть два типа значений, предположим, что это значения type1 и type2.

Состояние 1: Когда мы выбираем значение type1 из Combo1, вызывается метод actionListener для combo1, который вызывает метод, в котором combo2 остается отключенным и добавляет в таблицу несколько строк, связанных с выбранным значением type1 из combo1.

Состояние 2: когда мы выбираем значение type2 из combo1, вызывается метод actionListener для combo1, который вызывает метод, в котором combo2 заполняется некоторыми значениями, связанными с type2, и включается, но значение не выбирается из combo2, и таблица также должна оставаться пустой, пока мы не выберем любое значение из combo2.

таблица при каждом добавлении значения в combo2 метод слушателя действия combo2 запускается. В методе actionListener combo2, который получает выбранное значение combo2, но здесь нет выбранного значения combo2, которое приводит к исключению NullPointerException.

Итак, что мне делать, чтобы метод списка действий combo2 не выполнялся после добавления значений в combo2.

Ответы [ 8 ]

10 голосов
/ 10 марта 2011

Вы можете удалить слушатель действия, прежде чем добавлять новые элементы, и добавить его обратно, как только закончите.Swing является однопоточным, поэтому нет необходимости беспокоиться о том, что другим потокам нужно запускать прослушиватель.

Возможно, ваш слушатель также может проверить, выбрано ли что-то, и предпринять соответствующие действия, если нет.Лучше, чем получить NPE.

6 голосов
/ 26 марта 2014

Что я делаю вместо добавления и удаления слушателей действия, у меня в моих слушателях действия есть логическая переменная, которая имеет значение true, если она должна разрешать действие до конца, или false, если она должна его блокировать.

Затем я устанавливаю значение false, когда я делаю некоторые изменения, которые будут запускать слушателя действия

JComboBox test = new JComboBox();
test.addActionListener(new ActionListener()
{
  @Override
  public void actionPerformed(ActionEvent e)
  {
    if(testActionListenerActive)
    {
      //runn your stuff here
    }
  }
});

//then when i want to update something where i want to ignore all action evetns:
testActionListenerActive = false;
//do stuff here like add 

SwingUtilities.invokeLater(() -> testActionListenerActive = false);
//and now it is back enabled again
//The reason behind the invoke later is so that if any event was popped onto the awt queue 
//it will not be processed and only events that where inserted after the enable 
//event will get processed.
2 голосов
/ 03 мая 2016

Более чистый способ заключается в использовании лямбда-выражений, подобных этому:

do(comboBox, () -> comboBox.setSelectedItem("Item Name"));

Чтобы вышеприведенное сработало, вам понадобится где-то следующий метод:

public static void do(final JComboBox<String> component, final Runnable f) {
    final ActionListener[] actionListeners = component.getActionListeners();
    for (final ActionListener listener : actionListeners)
        component.removeActionListener(listener);
    try {
        f.run();
    } finally {
        for (final ActionListener listener : actionListeners)
            component.addActionListener(listener);
    }
}
2 голосов
/ 13 сентября 2014

, хотя уже поздно, лучшей альтернативой было бы отключение поля со списком, который нужно изменить перед изменением. тем самым вы предотвращаете события запуска измененного комбинированного списка, когда, например, вы используете методы like removeAllItems () или addItem ()

String orderByOptions[] = {"smallest","highest","longest"};

JComboBox<String> jcomboBox_orderByOption1 = new JComboBox<String(orderByOptions);
JComboBox<String> jcomboBox_orderByOption2 = new JComboBox<String(orderByOptions);
JComboBox<String> jcomboBox_orderByOption3 = new JComboBox<String(orderByOptions);

jcomboBox_orderByOption1.addItemListener(new ItemListener()
{
    public void itemStateChanged(ItemEvent itemEvent)
    {
            int eventID = itemEvent.getStateChange();

            if (eventID == ItemEvent.SELECTED)
            {
                Object selectedItem = jcomboBox_orderByOption1.getSelectedItem();

                jcomboBox_orderByOption2.setEnabled(false);
                jcomboBox_orderByOption2.removeAllItems();

                for (String item: string_orderByOptions)
                {
                    if (!item.equals(selectedItem))
                    {
                        jcomboBox_orderByOption2.addItem(item);
                    }
                }

                jcomboBox_orderByOption2.setEnabled(true);
            }
        }
    });



    jcomboBox_orderByOption2.addItemListener(new ItemListener()
    {
        public void itemStateChanged(ItemEvent itemEvent)
        {
            int eventID = itemEvent.getStateChange();

            if (eventID == ItemEvent.SELECTED)
            {
                Object selectedItem1 = jcomboBox_orderByOption1.getSelectedItem();
                Object selectedItem2 = jcomboBox_orderByOption2.getSelectedItem();

                jcomboBox_orderByOption3.setEnabled(false);

                jcomboBox_orderByOption3.removeAllItems();

                for (String item: string_orderByOptions)
                {
                    if (!item.equals(selectedItem1) && !item.equals(selectedItem2))
                    {
                        jcomboBox_orderByOption3.addItem(item);
                    }
                }

                jcomboBox_orderByOption3.setEnabled(true);

            }
        }
    });
1 голос
/ 27 марта 2018

попробуйте это:

       indicatorComboBox = new JComboBox() {

        /**
         * Do not fire if set by program.
         */
        protected void fireActionEvent() {
            // if the mouse made the selection -> the comboBox has focus
            if(this.hasFocus())
                super.fireActionEvent();
        }
    };
1 голос
/ 05 апреля 2016

Это работает:

/** Implements a Combo Box with special setters to set selected item or
  * index without firing action listener. */
public class MyComboBox extends JComboBox {

/** Constructs a ComboBox for the given array of items. */
public MyComboBox(String[] items) {
  super(items);
}

/** Flag indicating that item was set by program. */
private boolean isSetByProgram;

/** Do not fire if set by program. */
protected void fireActionEvent() {
  if (isSetByProgram)
    return;
  super.fireActionEvent();
}

/** Sets selected Object item without firing Action Event. */
public void setSelection(Object item) {
  isSetByProgram = true;
  setSelectedItem(item);
  isSetByProgram = false;
}

/** Sets selected index without firing Action Event. */
public void setSelection(int index) {
  isSetByProgram = true;
  setSelectedIndex(index);
  isSetByProgram = false;
}

}

Примечание: Вы не можете просто переопределить setSelectedItem(...) или setSelectedIndex(...), потому что они также используются внутри, когда элементы фактически выбираются действиями клавиатуры или мыши пользователя, когда вы не хотите запрещать увольнение слушателей.

0 голосов
/ 01 июня 2017

Чтобы определить, следует ли выполнять различные методы в интерфейсных методах actionListener (блоки кода actionPerformed ()), используйте setActionCommand () для исходных компонентов (combo1 или combo2).

Для вашего примера, перед добавлением элементов в combo2, вызовите setActionCommand ("doNothing") и защитите свой метод comboBoxActionPerformed ().

Вот скомпилированный пример, который использует этот принцип, чтобы одно комбо устанавливало индекс другого комбо, одновременно отображая строку в JTextField. Используя setActionCommand () и охраняя блок кода comboActionPerformed (), JTextField будет перебирать каждое слово в wordBank. Если метод comboActionPerformed () не был защищен или строка ActionCommand не была изменена, сработает 2 actionEvents, а textField пропустит слова.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

/** @author PianoKiddo */
public class CoolCombos extends JPanel {
    JComboBox<String> candyCombo;
    JComboBox<String> flavorCombo;
    JTextField field;
    String[] wordBank;
    int i = 0;

CoolCombos() {
    super();
    initComponents();
    addComponentsToPanel();
}

private void initComponents() {
    initCombos();
    initTextField();
}

private void initCombos() {
    ActionListener comboListener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            comboActionPerformed(e);
        }
    }; 
    String[] candyList = {"Sourpatch", "Skittles"};
    String[] flavorList = {"Watermelon", "Original"};
    candyCombo = new JComboBox<>(candyList);
    candyCombo.addActionListener(comboListener);
    flavorCombo = new JComboBox<>(flavorList);
    flavorCombo.addActionListener(comboListener);
}

private void initTextField() {
    wordBank = new String[]{"Which", "Do", "You", "Like", "Better?"};
    field = new JTextField("xxxxx");
    field.setEditable(false);
    field.setText(wordBank[i]);
}

private void addComponentsToPanel() {
    this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    this.add(candyCombo);
    this.add(flavorCombo);
    this.add(field);
}

public void comboActionPerformed(ActionEvent e) {
    String command = e.getActionCommand();
    if (!command.equals("doNothing")) {
        JComboBox combo = (JComboBox) e.getSource();
        if (combo.equals(candyCombo)) {
            setOtherComboIndex(candyCombo, flavorCombo); }
        else {
            setOtherComboIndex(flavorCombo, candyCombo); }
        displayText(); //replace here for toDo() code
    }
}

private void setOtherComboIndex(JComboBox combo, JComboBox otherCombo) {
    String command = otherCombo.getActionCommand();
    otherCombo.setActionCommand("doNothing"); //comment this line to skip words.
    otherCombo.setSelectedIndex(combo.getSelectedIndex());
    otherCombo.setActionCommand(command);
}

private void displayText() {
    i++; 
    String word;
    if (i > 4) { i = 0; }
    word = wordBank[i]; 
    field.setText(word);
    this.repaint();
}

/**
 * Create the GUI and show it.  For thread safety,
 * this method should be invoked from the
 * event-dispatching thread.
 */
private static void createAndShowGUI() {
    //Create and set up the window.
    JFrame frame = new JFrame("CoolCombos");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //Create and set up the content pane.
    JComponent newContentPane = new CoolCombos();
    newContentPane.setOpaque(true); //content panes must be opaque
    frame.setContentPane(newContentPane);

    //Display the window.
    frame.pack();
    frame.setMinimumSize(frame.getSize());
    frame.setVisible(true);
}

public static void main(String[] args) {
    //Schedule a job for the event-dispatching thread:
    //creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            createAndShowGUI();
        }
    });
}

}
0 голосов
/ 04 сентября 2015

Я пошел по этому глупому простому пути для моей программы, так как я новичок в программировании.

Я изменил слушателей действия, чтобы иметь счетчик if:

if(stopActionlistenersFromFiringOnLoad != 0){//action performed ;}

Затем в конце создания Java-программы я добавил 1 к счетчику:

topActionlistenersFromFiringOnLoad += 1;

...