1

我正在使用自定义 CellRenderer 编写带有复选框的 JList,以及用于选择/取消选择、添加或删除列表元素的上下文菜单。

一切正常,我可以选择项目,打开上下文菜单并通过它删除/添加元素。我的问题是,当我添加一个元素并且列表中的元素比开始时更多时,列表消失并且我得到一个空白面板。例如,当在列表中添加一个新元素而之前没有删除一个元素时,就会发生这种情况。

为了查明问题,我一直试图将代码降到最低,但我仍然不明白为什么它不能正常工作。

我想知道的是为什么我的列表会变成空白,以及如何防止它这样做。

当然,欢迎任何旁白或建议:)

谢谢。


如果您想尝试一下,这是完整的代码(左键单击以选择项目,右键单击以打开上下文菜单):

CheckBoxList.java

package misc;

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

public class CheckBoxList extends JList {

    private int selection = -1;

    protected static final Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);

    public CheckBoxList(Model m) {
        this();
        this.setModel(m);
    }

    public CheckBoxList() {
        this.setCellRenderer(new CheckboxCellRenderer());
        this.addMouseListener(new MouseAdapter() {

            @Override
            public void mousePressed(MouseEvent e) {
                int index = locationToIndex(e.getPoint());
                selection = index;
                CheckBoxList cbl = (CheckBoxList) e.getSource();
                cbl.setSelectedIndex(index);
                if(index != -1) {
                    if(e.getButton() == MouseEvent.BUTTON1) {
                        Data v = (Data) getModel().getElementAt(index);
                        v.setSelected(!v.isSelected());
                        JCheckBox checkbox = new JCheckBox(v.getS(), v.isSelected());
                        checkbox.setSelected(!checkbox.isSelected());
                        repaint();
                    } else if(e.getButton() == MouseEvent.BUTTON3) {
                        ContextMenu pum = new ContextMenu(cbl);
                        pum.show(e.getComponent(), e.getX(), e.getY());
                    }
                }
            }
        });
        this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    }

    protected class CheckboxCellRenderer implements ListCellRenderer {

        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index,
                boolean isSelected, boolean cellHasFocus) {
            Data v = (Data) value;
            JCheckBox checkbox = new JCheckBox(v.getS(), v.isSelected());
            checkbox.setBackground(isSelected ? getSelectionBackground() : getBackground());
            checkbox.setBorderPainted(true);
            checkbox.setBorder(isSelected ? UIManager.getBorder("List.focusCellHighlightBorder")
                    : noFocusBorder);
            return checkbox;
        }
    }

    public int getSelection() {
        return this.selection;
    }

    public class ContextMenu extends JPopupMenu {

        private JMenuItem deleteItem;
        private JMenuItem addItem;
        private CheckBoxList cbl;

        public ContextMenu(CheckBoxList cbl) {
            this.cbl = cbl;
            this.deleteItem = new JMenuItem("Delete");
            this.deleteItem.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    JMenuItem jmi = (JMenuItem) e.getSource();
                    ContextMenu cm = (ContextMenu) jmi.getParent();
                    Model m = (Model) cm.cbl.getModel();
                    m.remove((Data) m.getElementAt(cm.cbl.getSelection()));
                    cm.cbl.repaint();
                }
            });
            this.add(deleteItem);

            this.addItem = new JMenuItem("Add new");
            this.addItem.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    JMenuItem jmi = (JMenuItem) e.getSource();
                    ContextMenu cm = (ContextMenu) jmi.getParent();
                    ((Model) cm.cbl.getModel()).add(new Data("Added :)"));
                    cm.cbl.repaint();
                }
            });
            this.add(addItem);
        }
    }
}

模型.java

package misc;

import java.util.ArrayList;
import java.util.List;

import javax.swing.ListModel;
import javax.swing.event.ListDataListener;

public class Model implements ListModel {

    private List<Data> data = new ArrayList<Data>();

    @Override
    public void addListDataListener(ListDataListener l) {}

    @Override
    public Object getElementAt(int index) {
        return data.get(index);
    }

    @Override
    public int getSize() {
        return data.size();
    }

    @Override
    public void removeListDataListener(ListDataListener l) {}

    public void add(Data string) {
        this.data.add(string);
    }

    public void remove(Data d) {
        data.remove(d);
    }
}

数据.java

package misc;

public class Data {

    private String s;
    private boolean selected = false;

    public Data(String s) {
        super();
        this.s = s;
    }

    public String getS() {
        return this.s;
    }

    public void setS(String s) {
        this.s = s;
    }

    public boolean isSelected() {
        return this.selected;
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
    }

    @Override
    public String toString() {
        return "Data [s=" + this.s + ", isSelected=" + this.selected + "]";
    }

}

主.java

package misc;

import java.awt.Dimension;

import javax.swing.JFrame;
import javax.swing.JScrollPane;

public class Main {

    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setPreferredSize(new Dimension(500,200));
        f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        Model m = new Model();
        m.add(new Data("test1"));
        m.add(new Data("test2"));
        CheckBoxList cbl = new CheckBoxList(m);
        JScrollPane jsp = new JScrollPane(cbl);
        f.add(jsp);
        f.pack();
        f.setVisible(true);
    }
}
4

2 回答 2

4

问题来自您的实现,您ListModel没有实现 addListDataListener 和 removeListDataListener,也没有触发适当的模型事件来通知它们。

只需将以下内容放入您的 ListModel 中,它应该会更好地工作:

    private List<ListDataListener> listeners = new ArrayList<ListDataListener>();

    @Override
    public void addListDataListener(ListDataListener l) {
        listeners.add(l);
    }

    @Override
    public void removeListDataListener(ListDataListener l) {
        listeners.remove(l);
    }

    public void add(Data string) {
        this.data.add(string);
        ListDataEvent addition = new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, data.size() - 1, data.size() - 1);
        for (ListDataListener l : listeners) {
            l.intervalAdded(addition);
        }
    }

    public void remove(Data d2) {
        data.remove(d2);
        ListDataEvent removal = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, data.size(), data.size());
        for (ListDataListener l : listeners) {
            l.intervalRemoved(removal);
        }
    }

可能考虑扩展或直接DefaultListModel使用它来为您处理这一切。

于 2012-06-19T13:29:55.877 回答
2

我认为您必须在模型中实现数据侦听器。侦听器可以通过 Swing 添加,它们希望在发生更改时得到通知。

于 2012-06-19T13:21:01.103 回答