I have a JList
that is supported by a DefaultListModel
and has a custom CellRenderer
.
The CellRenderer
is for adding support for JCheckBox
in my JList
.
When i try to modify the checked state for an element in the DefaultListModel
it does not appear to update my JList
. This happens when i try to use any of the three methods to get the element at a certain index: DefaultListModel.elementAt(...)
, DefaultListModel.get(...)
or DefaultListModel.getElementAt(...)
.
Why is my JList
not getting the update?
The section of code below in my JList
section that is commented out, will work. But, i do not like having to copy a new object into that position just to change the boolean.
Here is my code, simplified for easy reading and understanding.
JList
section
public AppsPanel() {
super(new BorderLayout());
this.appsListModel = new DefaultListModel<AppListItem>();
this.appsList = new JList<AppListItem>(this.appsListModel);
this.appsList.setCellRenderer(new AppsListRenderer());
this.appsList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
JList list = (JList) e.getSource();
int index = list.locationToIndex(e.getPoint());
AppListItem appListItem = (AppListItem)list.getModel().getElementAt(index);
appListItem.setSelected(!appListItem.isSelected());
list.repaint(list.getCellBounds(index, index));
}
});
this.appsScrollPane = new JScrollPane(this.appsList);
this.add(this.appsScrollPane);
}
...
public void selectAllApps() {
// for (int i = 0; i < this.appsListModel.size(); i++) {
// this.appsListModel.set(i, new AppListItem(this.appsListModel.get(i).getApp(), true));
// }
for (int i = 0; i < this.appsListModel.size(); i++) {
this.appsListModel.getElementAt(i).setSelected(true);
}
}
CellRenderer
section.
public class AppsListRenderer extends JCheckBox implements ListCellRenderer<AppListItem> {
@Override
public Component getListCellRendererComponent(JList<? extends AppListItem> list, AppListItem value, int index, boolean isSelected, boolean cellHasFocus) {
this.setSelected(value.isSelected());
if (value.getApp() != null) {
this.setText(value.getApp().getAppName());
}
return this;
}
}
AppListItem
pojo
public class AppListItem {
private App app;
private boolean selected;
public AppListItem(App app, boolean selected) {
this.app = app;
this.selected = selected;
}
public App getApp() {
return app;
}
public void setApp(App app) {
this.app = app;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
App
pojo
public class App {
private String appName;
private String appPath;
public App(String appName, String appPath) {
this.appName = appName;
this.appPath = appPath;
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getAppPath() {
return this.appPath;
}
public void setAppPath(String appPath) {
this.appPath = appPath;
}
}
Your selectAllApps()
does not trigger any event. Since you can’t invoke one of the fire…
methods on the default model you should at least invoke repaint
on the JList
after the update.
But I recommend implementing your own model; it’s not that hard.
public class MyListModel extends AbstractListModel<AppListItem> {
ArrayList<AppListItem> list=new ArrayList<>();
public void selectAllApps() {
for(AppListItem i:list) i.setSelected(true);
fireContentsChanged(this, 0, getSize()-1);
}
public int getSize() {
return list.size();
}
public AppListItem getElementAt(int index) {
return list.get(index);
}
// if you need such updates:
public void add(int index, AppListItem item) {
list.add(index, item);
fireIntervalAdded(this, index, index);
}
public boolean remove(AppListItem i) {
int index = list.indexOf(i);
if(index<0) return false;
remove(index);
return true;
}
public void remove(int index) {
list.remove(index);
fireIntervalRemoved(this, index, index);
}
}