I want to do the following and I would need some suggestions on what is the best way to do it.
I have a JList which displays the files that the user adds by clicking add (+) button and removes the files when the user clicks remove (-) button. With each added file, I want to associate an icon which should indicate the status of the file. For example, if the user has only added the file and not run the file (I have another JButton for running the application with the selected file), then this icon should be red and once the user runs it, this icon should change to green. Also, if the user removes the file by clicking the (-) button, it should remove the icon associated with that particular file too. Below is a pictorial representation of what I want.
I was thinking of associating a ImageIcon with each added file but I am not sure how to change its appearance to display the status. I am also not sure how the ImageIcon can be removed when the file is removed. Is there any other way (other than ImageIcon) to do it? Any help/suggestions are appreciated.
In programming, data is king. How that data get's represented should not be of consideration to the data, that's the domain/responsibility of the UI/view layers.
This is often represented by the model-view-controller pattern
In your example, you have two pieces of (basic) information. A file and a status (not run, run, deleted), you want to combine this information as "data". In Java, that typically means a Plain Old Java Object (or Pojo)
Because the status only has a limited number of possibilities, we can use a enum
to represent it, and thereby restricting the valid values
public enum FileStatus {
NOT_RUN, RUN, DELETED;
}
And then we can create our own pojo...
public class FileOperation {
private File file;
private FileStatus status;
public FileOperation(File file, FileStatus status) {
this.file = file;
this.status = status;
}
public FileOperation(File file) {
this(file, FileStatus.NOT_RUN);
}
public File getFile() {
return file;
}
public FileStatus getStatus() {
return status;
}
public void setStatus(FileStatus newStatus) {
if (status == newStatus) {
return;
}
this.status = newStatus;
}
}
Now, when we want to know the status of the file, we know where to get it.
But what about the JList
? You ask, good question. What we really want is some way that the JList
can be informed when the status of any FileOperation
object changes.
Now, you could iterate over the ListModel
, but that's not a very clean solution, a better solution is to allow the FileOperation
to generate events when it changes and have the ListModel
listen for them and take it's own action.
This is a basic concept of an observer patternƒ
There's a number of ways you might do this, but I'm lazy, so I'm just going to use the available property change API
public class FileOperation {
private File file;
private FileStatus status;
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
public FileOperation(File file, FileStatus status) {
this.file = file;
this.status = status;
}
public FileOperation(File file) {
this(file, FileStatus.NOT_RUN);
}
public File getFile() {
return file;
}
public FileStatus getStatus() {
return status;
}
public void setStatus(FileStatus newStatus) {
if (status == newStatus) {
return;
}
FileStatus oldStatus = status;
status = newStatus;
propertyChangeSupport.firePropertyChange("status", oldStatus, status);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
}
And now we need a ListModel
which can respond to it...
public class FileOperationListModel extends AbstractListModel<FileOperation> {
private List<FileOperation> items = new ArrayList<FileOperation>(25);
private PropertyChangeListener handler = new PropertyChangeHandler();
public void add(FileOperation fo) {
fo.addPropertyChangeListener(handler);
int size = items.size();
items.add(fo);
fireIntervalAdded(this, size, size);
}
public void remove(FileOperation fo) {
int index = items.indexOf(fo);
if (index < 0) {
return;
}
fo.removePropertyChangeListener(handler);
items.remove(fo);
fireIntervalRemoved(this, index, index);
}
@Override
public int getSize() {
return items.size();
}
@Override
public FileOperation getElementAt(int index) {
return items.get(index);
}
public class PropertyChangeHandler implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (!(evt.getSource() instanceof FileOperation)) {
return;
}
FileOperation fo = (FileOperation) evt.getSource();
int index = items.indexOf(fo);
fireContentsChanged(FileOperationListModel.this, index, index);
}
}
}
Now, the final piece of the puzzle, is you're going to need a custom ListCellRenderer
which can display the information you want.
For that, you're going to have to start by reading How to use lists and Writing a Custom Cell Renderer