Java ListCellRenderer и JList: обработка выбора

Я застрял с проектом, который я должен сделать в школе.

я пытался создать пользовательский JList для «простого» пользовательского интерфейса: вариант использования должен отображать список заказов, и пользователь должен иметь возможность выбрать один и установить, если он прочитан/сделан, нажав две кнопки (прочитано и сделано), которые также действует как «неустановщик» (я использую 2 логических значения в классе Order, которые продолжают проходить через t, f, t, f...). После нажатия кнопки «Готово» выбранная строка должна стать зеленой (и постоянно оставаться в этом цвете), пока в этой строке снова не будет нажата кнопка «Готово» (= снято). То же самое с кнопкой «Читать», которая должна сделать выбранную строку постоянно желтой, относящейся к флагу «is_read» в классе Order.

Прочитав некоторые похожие сообщения, я написал этот пользовательский ListCellRenderer:

public class CookListCellRender extends JLabel implements ListCellRenderer<Order>{

public CookListCellRender(){
    setOpaque(true);
}

@Override
public Component getListCellRendererComponent(JList<? extends Order> list, Order value, int index, boolean isSelected, boolean cellHasFocus) {

    setLayout(new FlowLayout());
    setText(value.getNameEl());
    list.setVisibleRowCount(30);
    list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    setFont(new Font("Arial",Font.ITALIC,17));

    if (isSelected) {
        setBackground(Color.BLUE);
        setForeground(Color.BLUE);
    } else {
        setBackground(getBackground());
        setForeground(getForeground());
    }

    if(value.getOrderRead()){
        setBackground(Color.YELLOW);
    }else{
        setBackground(Color.WHITE);
    }

    if(value.getOrderDone()){
        setBackground(Color.GREEN);
    }else{
        setBackground(Color.WHITE);
    }
    return this;
}

}

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

Я почти уверен, что это проблема, связанная с моим пользовательским средством визуализации ячеек, а также потому, что я все еще учусь, как его использовать, и я начал сильно запутываться.

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

Класс InitGraphics (напрямую вызывается моим тестовым классом):

public class InitGraphics {

Cook c;
JList list;

public InitGraphics(Cook cuoco){
    c=cuoco;
    initUi();
  }

private void initUi() {
    JFrame frame = new JFrame("Cucina");
    list =new JList(c.getOrdersCopy().toArray());
    list.setCellRenderer(new CookListCellRender());
    JScrollPane panel = new JScrollPane(list);
    panel.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
    panel.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    ButtonPanel buttonPanel = new ButtonPanel(c.getOrdersCopy(),list);
    buttonPanel.setLayout(new FlowLayout());
    frame.add(buttonPanel,BorderLayout.NORTH);
    frame.add(panel,BorderLayout.CENTER);
    frame.setVisible(true);
    frame.setSize(600, 400);
    frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
  }
}

person A7X    schedule 04.08.2016    source источник
comment
Я настоятельно рекомендую, а не призываю вас создать и опубликовать действительный sscce, если вам нужны серьезные и своевременная помощь. В противном случае ваш код почти бесполезен для нас и ограничивает нашу способность понять вашу проблему.   -  person Hovercraft Full Of Eels    schedule 05.08.2016
comment
Например: никто из нас не может ни скомпилировать, ни запустить этот код. Мы не хотим видеть всю вашу программу или пробираться через большое количество кода, не относящегося к проблеме, но если бы вы могли сократить свой код до самого маленького примера, который компилируется и работает для нас без изменений, который показывает нам вашу проблему, и это достаточно мало, чтобы опубликовать здесь свой вопрос в виде текста в формате кода, мы, вероятно, сможем решить вашу проблему довольно быстро.   -  person Hovercraft Full Of Eels    schedule 05.08.2016
comment
Извините, здесь плохое интернет-соединение. Кстати, я никогда раньше не слышал о sscce, но я попытался... ну, я получил 140-строчный код, который, я думаю, не очень подходит, но делает свою работу. Не будучи ветераном Java, я старался изо всех сил, я оставляю это здесь с pastebin, чтобы не редактировать уже опубликованное код   -  person A7X    schedule 05.08.2016
comment
Я дал вам два SSCCE с моим ответом ниже. Пожалуйста, просмотрите раздел справочного центра, чтобы задать вопросы - никаких ссылок на код, пожалуйста, только код, размещенный здесь. Но еще раз, пожалуйста, проверьте мой ответ   -  person Hovercraft Full Of Eels    schedule 05.08.2016


Ответы (1)


Это убивает тебя:

    } else {
        setBackground(getBackground());
        setForeground(getForeground());
    }

Это вообще ничего не меняет. Я думаю, что вы хотите:

    } else {
        setBackground(null);
        setForeground(null);
    }

Обратите внимание, что для выполнения какой-либо работы с этим кодом мне пришлось создать собственный SSCCE:

import java.awt.*;
import javax.swing.*;

public class InitGraphics {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            final DefaultListModel<Order> orderModel = new DefaultListModel<>();
            orderModel.addElement(new Order("One"));
            orderModel.addElement(new Order("Two"));
            orderModel.addElement(new Order("Three"));
            orderModel.addElement(new Order("Four"));
            orderModel.addElement(new Order("Five"));

            JList<Order> orderList = new JList<>(orderModel);
            orderList.setCellRenderer(new CookListCellRender());

            JPanel panel = new JPanel();
            panel.add(new JScrollPane(orderList));

            JOptionPane.showMessageDialog(null, panel);
        });
    }
}

class CookListCellRender extends JLabel implements ListCellRenderer<Order> {

    public CookListCellRender() {
        setOpaque(true);
    }

    @Override
    public Component getListCellRendererComponent(JList<? extends Order> list, Order value, int index,
            boolean isSelected, boolean cellHasFocus) {

        setLayout(new FlowLayout());
        setText(value.getNameEl());
        list.setVisibleRowCount(30);
        list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        setFont(new Font("Arial", Font.ITALIC, 17));

        if (isSelected) {
            setBackground(Color.BLUE);
            setForeground(Color.BLUE);
        } else {
            // TODO: fix!
            // setBackground(getBackground());
            // setForeground(getForeground());
            setBackground(null);
            setForeground(null);
        }

        if (value.getOrderRead()) {
            setBackground(Color.YELLOW);
        } else {
            setBackground(Color.WHITE);
        }

        if (value.getOrderDone()) {
            setBackground(Color.GREEN);
        } else {
            setBackground(Color.WHITE);
        }
        return this;
    }

}

class Order {

    private String nameE1;
    private boolean orderDone;
    private boolean orderRead;

    public Order(String nameE1) {
        this.nameE1 = nameE1;
    }

    public String getNameEl() {
        return nameE1;
    }

    public boolean getOrderDone() {
        return orderDone;
    }

    public boolean getOrderRead() {
        return orderRead;
    }

    public void setOrderDone(boolean orderDone) {
        this.orderDone = orderDone;
    }

    public void setOrderRead(boolean orderRead) {
        this.orderRead = orderRead;
    }

}

В будущем, пожалуйста, делайте эту работу для нас, так как вы тот, кто просит о помощи, а мы волонтеры.

Также это:

    list.setVisibleRowCount(30);
    list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

Никогда не делайте такого рода вещи изнутри рендерера. Этот код должен находиться там, где вы создаете JList, а не в средстве визуализации.

Вот мой последний SSCCE, который включает в себя кнопки очистить/прочитать/готово

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.*;
import java.util.List;

import javax.swing.*;

public class InitGraphics {
    @SuppressWarnings("serial")
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            final DefaultListModel<Order> orderModel = new DefaultListModel<>();
            orderModel.addElement(new Order("One"));
            orderModel.addElement(new Order("Two"));
            orderModel.addElement(new Order("Three"));
            orderModel.addElement(new Order("Four"));
            orderModel.addElement(new Order("Five"));

            final JList<Order> orderList = new JList<>(orderModel);
            orderList.setCellRenderer(new CookListCellRender());
            orderList.setVisibleRowCount(5);

            JPanel panel = new JPanel();
            // panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
            panel.add(new JScrollPane(orderList));

            panel.add(new JButton(new AbstractAction("Read") {

                @Override
                public void actionPerformed(ActionEvent e) {
                    List<Order> selectedOrders = orderList.getSelectedValuesList();
                    for (Order order : selectedOrders) {
                        order.setOrderRead(true);
                    }
                    orderList.repaint();
                }
            }));
            panel.add(new JButton(new AbstractAction("Done") {

                @Override
                public void actionPerformed(ActionEvent e) {
                    List<Order> selectedOrders = orderList.getSelectedValuesList();
                    for (Order order : selectedOrders) {
                        order.setOrderDone(true);
                    }
                    orderList.repaint();
                }
            }));
            panel.add(new JButton(new AbstractAction("Clear") {

                @Override
                public void actionPerformed(ActionEvent e) {

                    for (int i = 0; i < orderModel.size(); i++) {
                        orderModel.getElementAt(i).setOrderDone(false);
                        orderModel.getElementAt(i).setOrderRead(false);
                    }
                    orderList.repaint();
                }
            }));


            JFrame frame = new JFrame("Test List");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(panel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}

@SuppressWarnings("serial")
class CookListCellRender extends JLabel implements ListCellRenderer<Order> {

    public CookListCellRender() {
        setOpaque(true);
    }

    @Override
    public Component getListCellRendererComponent(JList<? extends Order> list, Order value, int index,
            boolean isSelected, boolean cellHasFocus) {

        // setOpaque(true); // !! 

        // !! setLayout(new FlowLayout());
        setText(value.getNameEl());
        // !! list.setVisibleRowCount(30);
        // !! list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        setFont(new Font("Arial", Font.ITALIC, 17));

        if (isSelected) {
            setBackground(Color.BLUE);
            setForeground(Color.BLUE);
        } else {
            // TODO: fix!
            // setBackground(getBackground());
            // setForeground(getForeground());
            setBackground(null);
            setForeground(null);
        }

        if (value.getOrderRead()) {
            setBackground(Color.YELLOW);
        } else {
            setBackground(Color.WHITE);
        }

        if (value.getOrderDone()) {
            setBackground(Color.GREEN);
        } else {
            setBackground(Color.WHITE);
        }
        return this;
    }

}

class Order {

    private String nameE1;
    private boolean orderDone;
    private boolean orderRead;

    public Order(String nameE1) {
        this.nameE1 = nameE1;
    }

    public String getNameEl() {
        return nameE1;
    }

    public boolean getOrderDone() {
        return orderDone;
    }

    public boolean getOrderRead() {
        return orderRead;
    }

    public void setOrderDone(boolean orderDone) {
        this.orderDone = orderDone;
    }

    public void setOrderRead(boolean orderRead) {
        this.orderRead = orderRead;
    }

}
person Hovercraft Full Of Eels    schedule 04.08.2016
comment
Ничего себе, здесь много нового... могу я спросить вас, связано ли действие AbstractAction с функцией invokeLater, которую вы использовали раньше? Это причина, по которой мои цвета строк меняются после выбора новой строки? Я снова попытался написать это (и я обнаружил, что sscce также является отличным способом отладки, заставляющим меня очищать мой код и глубоко пересматривать его ... был полный беспорядок, я потерялся между 20 классами) и теперь установить строки правильно на зеленый, но все еще после выбора новой строки. Извините, если я заставил вас тратить время, теперь я узнал о sscce, поэтому в следующий раз я определенно воспользуюсь им! - person A7X; 05.08.2016
comment
@A7X: AbstractAction похож на ActionListener на стероидах. Он предоставляет JButton слушателя и несколько других важных полей состояния и функций. SwingUtilities.invokeLater(...) используется, чтобы гарантировать, что код, который вы запускаете, выполняется в потоке событий Swing или EDT (поток диспетчеризации событий). Это строго для безопасности потоков Swing и не имеет никакого отношения к тому, что ваш код не работает. - person Hovercraft Full Of Eels; 05.08.2016
comment
Понятно. Я также заметил, что вы использовали перерисовку в списке и, в частности, поместили все элементы, которые должны оставаться выбранными, в другой список массивов. Я думаю, что это еще одна причина, по которой мой код не работал должным образом... Но я не могу этого сделать, если принудительно выбираю один элемент в списке, верно? Или это не повлияет на метод getSelectedValueList? - person A7X; 05.08.2016
comment
@A7X: вы помещаете все элементы, которые должны оставаться выбранными, в другой список массивов - нет, мой код просто допускает множественный выбор и все. - person Hovercraft Full Of Eels; 05.08.2016
comment
Вау, здесь много нового... В том числе, надеюсь, что ваш пост не был "длинным" (часть, которую я отредактировал, не может быть и речи). Видите ли, оба примера, предоставленные @HovercraftFullOfEels, представляют собой Sscce (где первая буква «S» означает «короткий»), иначе известный как минимальный воспроизводимый пример (и да, они тоже были "минимальными"!). - person Andrew Thompson; 05.08.2016