When ArrayList is updated in Model JList should be updated in View
arraylist, java, jlist, model-view-controller, swing
Solution
Use appropriate MVC-pattern here: Views listen for changes in the model and update according to notifications from the model:
- Add `PropertyChangeSupport` on your model and fire `PropertyChangeEvent`'s when modifying your model
- Add the view as a `PropertyChangeListener` of your model
- React to the notifications accordingly (if a `Person` is added to your model, when you receive the event that says "New person added", add it to the `ListModel` of your `peopleList`, same for "Person removed", etc...)
http://docs.oracle.com/javase/7/docs/api/java/beans/PropertyChangeSupport.html
Note: Since your model will be modified by another Thread than the EDT (Event dispatching Thread), make sure that you modify your UI on the EDT (Take a look at `SwingUtilities.isEventDispatchingThread()` and `SwingUtilities.invokeLater()`)
UPDATE:
Here is a snippet that illustrates what I was trying to explain above (sorry if it is a bit lengthy but I tried to stay as close as possible to your original code):
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class Main {
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return this.name;
}
}
public class Producer extends Thread {
private Model model;
public Producer(Model model) {
this.model = model;
}
@Override
public void run() {
Random random = new Random();
Person fred = new Person("Fred Flintstone", 37);
Person wilma = new Person("Wilma Flintstone", 18);
Person pebbles = new Person("Pebbles Flintstone", 15);
Person dino = new Person("Dino Flintstone", 45);
Person barney = new Person("Barney Rubble", 76);
Person betty = new Person("Betty Rubble", 76);
Person bamm = new Person("Bamm-Bamm Rubble", 76);
while (true) {
try {
model.addPerson(fred);
Thread.sleep(1500);
model.addPerson(wilma);
Thread.sleep(1500);
model.addPerson(pebbles);
Thread.sleep(1500);
model.addPerson(dino);
Thread.sleep(1500);
model.addPerson(barney);
Thread.sleep(1500);
model.addPerson(betty);
Thread.sleep(1500);
model.addPerson(bamm);
while (model.getPeople().size() > 0) {
Person p = model.getPeople().get(random.nextInt(model.getPeople().size()));
model.removePerson(p);
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class Model {
private static final String PEOPLE = "people";
private List<Person> people;
private PropertyChangeSupport propertyChangeSupport;
public Model() {
people = new ArrayList<Person>();
propertyChangeSupport = new PropertyChangeSupport(this);
}
public PropertyChangeSupport getPropertyChangeSupport() {
return propertyChangeSupport;
}
public List<Person> getPeople() {
return people;
}
public void addPerson(Person aPerson) {
people.add(aPerson);
System.out.println("Person object added to people list:" + aPerson);
getPropertyChangeSupport().firePropertyChange(PEOPLE, null, aPerson);
}
public void removePerson(Person aPerson) {
people.remove(aPerson);
getPropertyChangeSupport().firePropertyChange(PEOPLE, aPerson, null);
}
}
public class View extends JFrame implements PropertyChangeListener {
private JPanel topPanel, botPanel;
private JList peopleList;
private JScrollPane scrollPane;
private Model model;
private DefaultListModel peopleListModel;
public View(Model model) {
this.model = model;
setSize(200, 220);
setTitle("View");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
topPanel = new JPanel();
botPanel = new JPanel();
peopleListModel = new DefaultListModel();
for (Person p : model.getPeople()) {
peopleListModel.addElement(p);
}
peopleList = new JList(peopleListModel);
model.getPropertyChangeSupport().addPropertyChangeListener(Model.PEOPLE, this);
scrollPane = new JScrollPane(peopleList);
topPanel.setLayout(new GridLayout(1, 1));
topPanel.add(scrollPane);
topPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "People list"));
Container cp = getContentPane();
cp.add(topPanel, BorderLayout.NORTH);
cp.add(botPanel, BorderLayout.SOUTH);
}
@Override
public void propertyChange(final PropertyChangeEvent evt) {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
propertyChange(evt);
}
});
return;
}
if (evt.getSource() == model) {
if (Model.PEOPLE.equals(evt.getPropertyName())) {
if (evt.getOldValue() != null && evt.getNewValue() == null) {
peopleListModel.removeElement(evt.getOldValue());
} else if (evt.getOldValue() == null && evt.getNewValue() != null) {
peopleListModel.addElement(evt.getNewValue());
}
}
}
}
}
public static void main(String[] args) {
new Main().init();
}
private void init() {
final Model model = new Model();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
View theView = new View(model);
theView.setVisible(true);
}
});
Producer producer = new Producer(model);
producer.start();
}
}
Problem
I've been playing around with this for a while now with no success. I've had a look at ListModel but have struggled to implement it into my current project. I have a Producer class thread adding elements to an ArrayList in the Model. This works fine and the ArrayList is being updated at runtime. My problem is, I then want the new objects added to the ArrayList to be added to the JList in the View class. I can't see how to incorperate ListModel or DefaultListModel into my current setup. Help much appreciated. ``` public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } public String toString() { return this.name; } } public class Producer extends Thread { private Model model; public Producer(Model model) { this.model = model; } public void run() { Person fred = new Person("Fred Flintstone", 37); Person wilma = new Person("Wilma Flintstone", 18); Person pebbles = new Person("Pebbles Flintstone", 15); Person dino = new Person("Dino Flintstone", 45); Person barney = new Person("Barney Rubble", 76); Person betty = new Person("Betty Rubble", 76); Person bamm = new Person("Bamm-Bamm Rubble", 76); try { model.addPerson(fred); Thread.sleep(1500); model.addPerson(wilma); Thread.sleep(1500); model.addPerson(pebbles); Thread.sleep(1500); model.addPerson(dino); Thread.sleep(1500); model.addPerson(barney); Thread.sleep(1500); model.addPerson(betty); Thread.sleep(1500); model.addPerson(bamm); } catch(Exception e) { System.out.println("Error adding Person object to Model.people ArrayList" + e); } } } public class Model { private List <Person> people; public Model() { people = new ArrayList<Person>(); } public List<Person> getPeople() { return people; } public void addPerson(Person aPerson) { people.add(aPerson); System.out.println("Person object added to people list:" + aPerson); } public void removePerson(Person aPerson) { people.remove(aPerson); } } public class View extends JFrame { private JPanel topPanel, botPanel; private JList peopleList; private JScrollPane scrollPane; private Model model; public View(Model model) { this.model = model; setSize(200, 220); setTitle("View"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); topPanel = new JPanel(); botPanel = new JPanel(); peopleList = new JList(model.getPeople().toArray()); scrollPane = new JScrollPane(peopleList); topPanel.setLayout(new GridLayout(1, 1)); topPanel.add(scrollPane); topPanel.setBorder(BorderFactory.createTitledBorder (BorderFactory.createEtchedBorder(), "People list")); Container cp = getContentPane(); cp.add(topPanel, BorderLayout.NORTH); cp.add(botPanel, BorderLayout.SOUTH); } } public class Main { public static void main(String[] args) { Model model = new Model(); View theView = new View(model); theView.setVisible(true); Producer producer = new Producer(model); producer.start(); } } ```