Блокировка выбора в JList

После почти целого дня попыток и поиска в Интернете/SO я прошу совета, и мой вопрос:

Как программно заморозить текущий выбор в JList и вернуться к нормальному поведению?

Прежде всего, немного контекста: у меня есть JList со списком элементов, для каждого из которых может быть запущено вычисление, которое может занять много времени (60 секунд), которое выполняется асинхронно с помощью Свингворкер. Индикатор выполнения показывает, что программа действительно выполняет вычисления и не зависает. Пока поток работает, я не хочу, чтобы пользователь мог изменить выбранный элемент в моем списке. Когда поток завершится, я хотел бы вернуться к нормальному поведению списка.

Я перечисляю мои попытки:

1) проверка моего «условия» (метод isComputing()) в ListSelectionListener и просто возврат, если он вычисляет:

@Override
public void valueChanged(ListSelectionEvent e) {
    if(e.getSource().equals(listSCCs)){
        if(isComputing())
            return;
[...]
}

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

2) Добавить полностью пустую реализацию MouseListener в список при вычислении, удалив ее в конце вычисления. По моему мнению, он перехватил бы события мыши в списке и аннулировал их, но список, похоже, не пострадал.

3) Мой последний шанс - вернуться к "исходному" выбору, когда статус вычисления активен в ListSelectionListener (если новый выбор отличается от "исходного", чтобы избежать бесконечной рекурсии по событию изменения), но я действительно не нравится это решение, и я надеюсь, что есть более элегантные.


person Alessandro S.    schedule 07.02.2014    source источник


Ответы (2)


Просто отключите JList во время вычислений. Вот демонстрация, в которой вы можете пометить JCheckBox для имитации начала вычислений и снять отметку, когда закончите. Выбор сохраняется.

package swing;

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

public class JListDisabledListDemo implements Runnable
{
  private static final String ITEMS[] =
    { "Black", "Blue", "Green", "Orange", "Purple", "Red", "White"};

  private JList jList;
  private JCheckBox chkEnable;

  public static void main(String args[])
  {
    SwingUtilities.invokeLater(new JListDisabledListDemo()); 
  }

  public void run()
  {
    jList = new JList(ITEMS);
    jList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    JScrollPane scroll = new JScrollPane(jList);
    scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

    chkEnable = new JCheckBox("Enable", true);
    chkEnable.addItemListener(new ItemListener()
    {
      public void itemStateChanged(ItemEvent e)
      {
        jList.setEnabled(chkEnable.isSelected());
      }
    });

    JFrame f = new JFrame("Colors");
    Container contentPane = f.getContentPane();
    contentPane.setLayout(new BorderLayout());
    contentPane.add(scroll, BorderLayout.CENTER);
    contentPane.add(chkEnable, BorderLayout.NORTH);

    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setSize(180, 220);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}
person splungebob    schedule 07.02.2014

Вы можете отключить JList перед выполнением SwingWorker (конечно, делайте это в Поток отправки событий) и включите список, переопределяющий done():

protected void done()

Выполняется в потоке отправки событий после завершения метода doInBackground. Реализация по умолчанию ничего не делает. Подклассы могут переопределить этот метод для выполнения действий завершения в потоке отправки событий. Обратите внимание, что вы можете запросить статус внутри реализации этого метода, чтобы определить результат этой задачи или была ли эта задача отменена.

Предполагая, что у вас есть кнопка, которая запускает вычисления, ваш код будет выглядеть так:

final JProgressBar progressBar = new JProgressBar();
final JList list = new JList();
final JButton button = new JButton("Process...");

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        if(list.getSelectedValue() != null) {
            list.setEnabled(false);
            button.setEnabled(false);
            progressBar.setValue(0);

            SwingWorker<Void, Integer> worker = new SwingWorker<Void, Integer>() {
                @Override
                protected Void doInBackground() throws Exception {
                    // your code here
                    return null;                       
                }

                @Override
                protected void process(List<Integer> chunks) {
                    progressBar.setValue(chunks.get(chunks.size() - 1));
                }

                @Override
                protected void done() {
                    list.setEnabled(true);
                    button.setEnabled(true);
                }
            };                    
            worker.execute();
        }
    }
});
person dic19    schedule 07.02.2014
comment
Спасибо за ваш ответ, на самом деле мне очень неловко, что я не подумал об отключении списка. Я принял другой ответ, потому что splungebob ответил перед вами. В любом случае, это +1 за предложение EDT. - person Alessandro S.; 08.02.2014
comment
@АлессандроС. пожалуйста :) Не волнуйтесь, это совсем не стыдно, мы все застреваем в проблемах, которые однажды решены, кажутся глупыми, но в то же время они превосходят нас. Так происходит все время. Речь идет не только о EDT, но самое главное, что SwingWorker автоматически активирует список, когда doInBackground завершится;) - person dic19; 08.02.2014