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);
        }
    }
}

Model.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);
    }
}

Data.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 + "]";
    }

}

Main.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

問題は、ListModeladdListDataListenerと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 に答える