[英]Swing JList with custom CellRenderer disappear after adding new element
我正在使用自定義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);
}
}
}
模型
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);
}
}
資料庫
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);
}
}
問題來自於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
來為您處理所有這一切。
我認為您必須在模型中實現數據偵聽器。 可以通過Swing添加偵聽器,並且希望在發生更改時得到通知。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.