How to design a View interface for Java Swing MVP Architecture? - java

I am creating a Java Swing application. The architecture, I've chosen is a variation of MVP, MVP with Supervising Controller.
I am using JGoodies Binding Framework to bind model directly to the view.
After reading a lot of resources on MVP, I found that it is a good practice to make your view implement an interface with which a presenter can work. This will allow to swap in different views, without affecting presenter code. Also, it would help in unit testing by mocking the views.
But, I am having difficulty to think of a View interface design in the swing context.
Here is the code, for viewing a contact and launching its editor:
public class Contact extends Bean {
private Integer id;
private String name = "";
private String email = "";
public Contact() {}
// Getters
// Setters with fire property change methods
}
public Class ContactViewer {
private JPanel panel;
JLabel nameLabel;
JLabel emailLabel;
JButton edit;
ContactViewer() {
initComponents();
build();
}
private void initComponents() {
// Initializing panel, labels and button
}
private void build() {
// Layouts labels & button onto panel
}
}
public Class ContactViewerModel extends PresentationModel<Contact> {
ContactViewerModel(Contact contact) {
super(contact);
}
}
public Class ContactViewerPresenter {
private final ContactViewerModel model;
private final ContactViewer view;
public static ContactViewerPresenter create(Contact contact) {
ContactViewerModel model = new ContactViewerModel(contact);
ContactViewer view = new ContactViewer();
ContactViewerPresenter presenter = new ContactViewerPresenter(model, view);
return presenter;
}
ContactViewerPresenter(ContactViewerModel model, ContactViewer view) {
this.model = model;
this.view = view;
initBindings();
initEventHandlers();
}
private void initBindings() {
PresentationModelBinder binder = Binders.binderFor(model);
binder.bindBeanProperty(FIRST_NAME) .to(view.nameLabel);
binder.bindBeanProperty(EMAIL) .to(view.emailLabel);
}
private void initEventHandlers() {
view.edit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Launch Contact Editor
}
});
}
}
public class ContactDemo {
public static void main(String[] args) {
final Contact contact = new Contact("John Doe", "info#johndoe.com");
SwingUtilities.invokeLater(
new Runnable() {
#Override
public void run() {
ComponentFactory.createAndShowDialog(
ContactViewerPresenter.create(contact));
}
});
}
}
In this code, ContactViewer, ContactViewerModel, ContactViewerPresenter are in the same package. So that, presenter can directly refer to package-private component fields of the view, to bind them to the model and adding event handlers.
Now, when designing an interface for ContactViewer:
Should I define getter methods in interface for JLabel and JButton?
Or should I define a super interface first for all View, then sub-interfaces for each view, on which unit-testing can be performed by mocking events.
Also, what would be the best approach to show different views for multilanguage support?

Related

Concise way to expose View's JButtons objects to Presenter

I'm writing a GUI app in Java using the MVP design pattern. JButton objects belong in the View class and the ActionListener objects belong in the Presenter. I'm looking for a concise way to allow the Presenter to add ActionListeners to the View's JButtons without either (1) making the buttons public and (2) without having to add a bunch of methods to the view that look like
private JButton foo;
private JButton bar;
public void addActionListenerToButtonFoo(ActionListener l) {
foo.addActionListener(l);
}
public void addActionListenerToButtonBar(ActionListener l) {
bar.addActionListener(l);
}
// (imagine typing 10 more of these trivial functions and having
// them clutter up your code)
I found one technique that works reasonably well:
public class View {
class WrappedJButton {
private JButton b;
public WrappedJButton(String name){
this.b = new JButton(name);
}
public void addActionListener(ActionListener l) {
b.addActionListener(l);
}
}
public final WrappedJButton next = new WrappedJButton("Next");
public final WrappedJButton prev = new WrappedJButton("Previous");
public void setup() {
JPanel buttons = new JPanel();
buttons.setLayout(new FlowLayout());
buttons.add(previous.b);
buttons.add(next.b);
}
} // end view
class Presenter {
public Presenter() {
View view = new View();
view.next.addActionListener(event -> {
// Respond to button push
});
}
} // end Presenter
This wrapper works well. Making the wrapped buttons public allows the Presenter to reference them by name (which allows my IDE to use code completion); but, because they are WrappedJButton objects, the only thing the Presenter can do is add an ActionListener. The View can get "full" access to the objects by grabbing the "real" button through the private b field.
Questions:
Is there an even better/cleaner solution? Perhaps something that
would eliminate the need to access the b field in the View?
Is there a way to generalize this solution so I don't have to
cut-and-paste WrappedJButton into every View class I write? I
tried moving WrappedJButton into an interface (which View
implements); but, when I do that, View no longer has access to the
private b field.
I think it would be ok to avoid copy-pasting the WrapperJButton class by exposing the wrapped buttons on the package level (assuming the the Presenter resides in a different package):
public class WrappedJButton {
final JButton b;
WrappedJButton(String name){
this.b = new JButton(name);
}
public void addActionListener(ActionListener l) {
b.addActionListener(l);
}
}
A different approach could be to store the buttons in a map:
class ButtonMap<E extends Enum<E>> {
private final EnumMap<E, JButton> map;
ButtonMap(Class<E> buttonEnum){
map = new EnumMap<>(buttonEnum);
for(E e : buttonEnum.getEnumConstants()){
map.put(e, new JButton(e.toString()));
}
}
JButton get(E e){
return map.get(e);
}
}
a view using this map could look like this:
public class View {
private final ButtonMap<ViewButton> buttonMap = new ButtonMap<>(ViewButton.class);
public enum ViewButton{
NEXT("Next"),
PREV("Prev");
private final String name;
private ViewButton(String name){
this.name = name;
}
#Override
public String toString(){
return name;
}
}
public void setup() {
JPanel buttons = new JPanel();
buttons.setLayout(new FlowLayout());
buttons.add(buttonMap.get(ViewButton.PREV));
buttons.add(buttonMap.get(ViewButton.NEXT));
}
public void addActionListener(ViewButton button, ActionListener l){
buttonMap.get(button).addActionListener(l);
}
} // end view
The button map is hidden with a private field. Only the addActionListener method of the buttons is exposed.

java inter object communication

Still learning Java.
Again Swing has caused me to ask this but it is really a general OO question. If I have a master class (that contains main()) it creates a new object "A" that does something, the master class now has a reference to that object, how does object "B" get access to the attributes of that object?
The only way I can think of is for the master class to create a new object "B", passing object "A" as a parameter to the constructor, which I suppose is O.K. but doesn't this make event handling potentially difficult.
For example, and perhaps this is a poor design which is causing the problem. I have a master class with the programme logic, that creates a standard Swing frame, with a menu, the menu items having action listeners. But the actionlistener needs to interact with external objects.
So some code (ignoring the details) :
The main class, containing the programme logic and the save and load methods, etc :
public final class TheProgramme implements WindowListener }
private static final TheProgramme TP = new TheProgramme();
// Declare Class variables, instance variables etc.
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShewGUI();
}
});
}
private static void createAndShewGUI() {
TP.populateAndShew();
}
private void populateAndShew() {
final StandardFrame sF = new StandardFrame("TheProgramme");
theFrame = sF.getMainFrame();
theFrame.addWindowListener(this);
theFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
theFrame.pack(); theFrame.setVisible(true);
}
...
}
So we have created a standard frame object which has created a menu, empty panel and status bar, but with event listeners on the menu items :
public class StandardFrame {
// Declare instance variables that must be visible to the ActionListener inner class
public StandardFrame(String theTitle) {
mainFrame = new JFrame(theTitle);
mainFrame.setJMenuBar(createMenuBar()); // ... the menu bar and ...
mainFrame.setContentPane(createBlankPanel()); // ... a blank panel
java.net.URL imageURL = TheProgramme.class.getResource("images/icon.png");
if (imageURL != null) {
ImageIcon icon = new ImageIcon(imageURL);
mainFrame.setIconImage(icon.getImage());
}
}
public JMenuBar createMenuBar() {
ActionListener menuEvents = new MenuListener();
JMenuBar aMenuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic(KeyEvent.VK_F);
...
aMenuBar.add(fileMenu);
...
JMenuItem newItem = new JMenuItem("New", KeyEvent.VK_N); newItem.addActionListener(menuEvents);
newItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
...
fileMenu.add(newItem);
...
return aMenuBar;
}
}
class MenuListener implements ActionListener {
public void actionPerformed(ActionEvent ae) {
String actionCommand = ae.getActionCommand();
switch (actionCommand) {
case "New":
// !!! here we need to call a method in an object to create a new document object !!!
break;
case "Reformat":
// !!! here we need to call a method in the object created above
}
}
}
The first problem is the actionlistener on the menu items calls a method to create an object but it is not a call to a static method.
The second problem is it needs to be be able to call a method in that new object later on as a result of another menu choice.
The classic way to do this in Model-View-Controller is to bind the objects together at runtime. The controller, your action listener, takes parameters to indicate which view and model it is to act on.
This use of parameters is also called "Dependency Injection," as Aqua mentions.
public static void main( String[] args )
{
Model model = new Model();
View view = new View();
ActionListener listener = new MyActionListener( model, view );
view.addActionListener( listener );
}
private static class MyActionListener implements ActionListener
{
private Model model;
private View view;
public MyActionListener( Model model, View view )
{
this.model = model;
this.view = view;
}
}
In Java you can cheat a little since the ActionEvent has a pointer to the source of the event (normally the view/JComponent that generated the event.
private static class MyActionListener implements ActionListener
{
private Model model;
public MyActionListener( Model model )
{
this.model = model;
}
#Override
public void actionPerformed( ActionEvent e )
{
JComponent source = (JComponent) e.getSource();
// source == "view"...
}
}
To set a new document, you can create a "document holder" class. The "new" menu item puts a new document in the holder class. All other menu items "get" the document from the holder class. This is a fairly strict OO paradigm which uses no static methods or fields, although it is a little tedious.
Set up:
public static void main( String[] args )
{
ModelDocumentHolder model = new ModelDocumentHolder();
View view = new View();
ActionListener listener = new NewDocument( model );
view.addActionListener( listener );
View view2 = new View();
view2.addActionListener( new RegularListener( model ) );
}
New document listener:
private static class NewDocument implements ActionListener
{
private ModelDocumentHolder model;
public NewDocument( ModelDocumentHolder model )
{
this.model = model;
}
#Override
public void actionPerformed( ActionEvent e )
{
model.setDoc( new Document() );
}
}
Most other menu items:
private static class RegularListener implements ActionListener
{
private ModelDocumentHolder model;
public RegularListener( ModelDocumentHolder model )
{
this.model = model;
}
#Override
public void actionPerformed( ActionEvent e )
{
JComponent source = (JComponent) e.getSource();
Document doc = model.getDoc();
// do stuff...
}
}
The holder class:
private static class ModelDocumentHolder
{
private Document doc;
public Document getDoc()
{
return doc;
}
public void setDoc( Document doc )
{
this.doc = doc;
}
}
Generally, (and it's really hard to know if this is an answer to your question, as it's quite vague) this is what setModel(...) and addListener(...) are for.
The "constructor coordinator" aka "master class", creates the models (the swing Model classes). It creates the views (JWidgets) and it sets the models of the views. In Swing it is easy to rely upon the default constructed model (populated with the JWidget default constructor), but maybe in your case, you should find the one widget causing problems and rewrite it make the model setting explicit.
If you have extended a Jwhatever, then keep in mind that setModel(...) typically does something like
if (this.model != null) {
this.model.removeListener(this);
}
// clear the cached last "view" of the model
clearCachedData(...);
if (model != null) {
this.model = model;
// restore the "view" of the new model.
grabCachedData(...);
this.model.addListener(this);
}
I hope my interpretation of the question is correct. You can inject/provide whatever object you need to the action implementation. Here is an example that uses an interface for better abstraction as a callback from actionPerformed. When action completes it call its callback to notify whoever is interested. In this case, the panel is notified and updates its text area with some text.
The interface:
public interface ActionCallback {
public void documentCreated(String name);
}
Here is the UI:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class TestAction extends JPanel implements ActionCallback {
private JTextArea area;
public TestAction() {
setLayout(new BorderLayout());
area = new JTextArea();
add(new JScrollPane(area));
}
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void documentCreated(String name) {
area.append(String.format("Created %s\n", name));
}
public static class NewAction extends AbstractAction {
private ActionCallback callback;
private Component parent;
public NewAction(ActionCallback callback, Component parent){
super("New");
this.callback = callback;
this.parent = parent;
}
#Override
public void actionPerformed(ActionEvent e) {
String value = JOptionPane.showInputDialog(parent, "Name", "new name");
if (value != null){
callback.documentCreated(value);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
TestAction panel = new TestAction();
frame.add(panel);
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Menu");
menuBar.add(menu);
JMenuItem item = new JMenuItem(new NewAction(panel, frame));
menu.add(item);
frame.setJMenuBar(menuBar);
frame.pack();
frame.setVisible(true);
}
});
}
}
Passing a reference of yourself (A) to an other object (B) tends to happen quite frequently in GUI code. You could use an context object, which you pass to all classes and which contains references to relevant references and might hold some global information. In simple cases, "main program class" is used as context and passed around.
Depending on what classes you use, this might also be useful: Component#getParent().

How can I use Program a game after swing.Timer stopped?

I'm writing a simple pikachu game in Java, and I use swing.Timer with JProgress Bar, my code is like this:
public static void TimeBarListener(final View scr){
ActionListener updateProBar = new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
int val = scr.timeBar.getValue();
if (val <= 0) {
scr.gameOver();
scr.timer = null;
return;
}
scr.timeBar.setValue(--val);
}
};
int t = n*400;
scr.timer = new Timer(t, updateProBar);
}
The "src" here is a class which extends JFrame to display the game I wrote, and 'n' is the number of pikachu pieces on a level. It works perfectly but after I add "timer", there are lots of problems occured:
I set the variable 't' change by level, but seems like it doesn't work ( I test the value, it was the right value but seems like 'timer' could'n get it). The time ran out too fast, faster if I click more on the pieces, and even if I set it longer it didn't change anything.
When I clicked "New Game" the second times, timer ran out even faster. But if I close the programs and then run again, the time return normal.
If the time ran out and then I click the "New Game" button again, It appears for 1 second then return to the "Game Over screen". Sometimes it works, but "IndexArrayOutOfBounds" Ecception appears.
I want to use the "Pause" button, which means that timer must pause and then continue to run, too. Is there anyway that I can do it?
I guess my problems are based on the code
if (val <= 0) {
scr.gameOver();
scr.timer = null;
return;
}
which makes the Game Over screen appears if the timer run out. But I'm new to this and I cant understand how I works, so I can't think of any solutions myself, or maybe it's not the problem.
Hope that I'll get some helps from you guys. Thanks a lot :)
Your problem is that you don't use correct architecture pattern. You should separate your business logic from your presentation. Also you should store variables (like time_left) in model, not in the controller (i.e. ActionListener). Please read about: Model View Controller pattern. It's a basic pattern and it'll solve most of yours problems.
Basic Example
Editor.java - main class
public class Editor {
public static void main(String[] args) {
Model model = new Model();
View view = new View(model);
new Controller(model, view);
}
}
View.java
public class View extends JFrame {
private Model model;
private JButton btn;
public View(Model model) {
this.model = model;
this.btn = new JButton("Test");
}
public void addViewControlListener(ActionListener al){
btn.addActionListener(al);
}
}
Controller.java
public class Controller {
public Controller(Model model, View view) {
view.addViewControlListener(new ViewControlListener(model, view));
}
}
ViewControlListener.java - implements ActionListener
public class ViewControlListener implements ActionListener {
private Model model;
private View view;
public ViewControlListener(Model model, View view){
this.model = model;
this.view = view;
}
public void actionPerformed(ActionEvent e) {
//update model
//refresh view
}
}
As you can see I have the one place (Controller.java) where I create listeners and add
these to components in view. You created multiple instances of the same listeners and lost control.
Also my ActionListener (ViewControlListener.java) holds instances of model and view, so it can update variables in model and refresh view.
Your application
And now something more about your application. You need thread (not realy action listener) that would decrement time variable and update view to show actual state (but only when game is active).
So you could create in model leftTime and isActive variables with setters and getters:
private int leftTime;
private boolean isActive;
public int getLeftTime() {
return leftTime;
}
public void setLeftTime(int leftTime) {
this.leftTime = leftTime;
}
public void decLeftTime() {
this.leftTime--;
}
public boolean isActive() {
return isActive;
}
public void setActive(boolean isActive) {
this.isActive = isActive;
}
And create thread that decrement time every second and repaint the view:
public class Timer extends Thread {
private Model model;
private View view;
public Timer(Model model, View view) {
this.model = model;
this.view = view;
}
public void run() {
while(true) { //could be better
if(model.isActive()) {
model.decLeftTime();
view.repaint();
}
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Then create and start that thread in Controller:
public class Controller {
public Controller(Model model, View view) {
view.addViewControlListener(new ViewControlListener(model, view));
...
Timer timer = new Timer(model, view);
timer.start();
}
}
In view you would add some component that shows left time from model and that's it.
PS do not forget set leftTime on game start.

How do swing component's model can request redraw?

I have my custom component which holds it's data in my custom data class. My component extends JComponent while data is fully custom.
What is conventional pattern a data class can notify component it was changed? Should I just implement some event inside data class and let component subscribe to it when data is set? Or there is predefined pattern is Swing library?
I am looking, for example, at ListModel<E> interface and see that is just has addListDataListener(ListDataListener l) and removeListDataListener(ListDataListener l). Is this that mechanism model notifies List?
For the record, this is mostly my personal opinion (since this is more of an opinion question).
Typically with a Swing application it's best to separate your code out into Model/View/Control (MVC). This means that the actual Swing components are your Viow, your listeners are your Control, and your code that actually does stuff is the Model. In this case both your View and Model only know about the Control (and the Control knows about both the View and Model).
So if your Model updates - it notifies the Control, which updates the View. It's the same thing with the View (Listeners in the View execute, notifying the Control, which updates the Model).
The advantage of this is that it loosely couples the View and the Model (the view only cares about showing stuff to the user, and the model only cares about the data, and they don't care what each other are doing so long as they get the right information).
Here's an example (for simplicity they're all in one file, but usually you'd have the MVC each in their own file at the very least):
import java.awt.event.*;
import java.util.ArrayList;
import javax.swing.*;
public class MVCSeparation {
// Model: (Number crunching math-y) or (data processing) stuff
public static class Model{
ArrayList<String> data = new ArrayList<String>();
public void addData(String value){
data.add(value);
}
public int getCount(){
return data.size();
}
public String randomValue(){
String result = "";
if(data.size() > 0){
int index = (int)(Math.random() * data.size());
System.out.println(index);
result = data.get(index);
}
System.out.println("Getting Value: " + result);
return result;
}
}
// View: Pretty graphics and visuals
public static class View extends Box{
JLabel text = new JLabel("Random Value:");
JTextField newItem = new JTextField(10);
JButton submit = new JButton("Submit");
public View(){
super(BoxLayout.Y_AXIS);
add(text);
add(newItem);
add(submit);
}
public void setSubmitAction(ActionListener submitAction){
submit.addActionListener(submitAction);
}
public void setDisplayText(String value){
text.setText("Random Value: " + value);
}
public String getText(){
String result = newItem.getText();
newItem.setText("");
return result;
}
public void startupApp(){
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(this);
frame.validate();
frame.pack();
frame.setVisible(true);
}
}
// Processing User Interactions and Data Updates (links two above together)
public static class Control{
Model m = new Model();
View v = new View();
public Control(){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
v.setSubmitAction(new SubmitText());
v.startupApp();
}});
//Randomly update label
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
v.setDisplayText(m.randomValue());
}});
}
}
// Listener to notify us of user interactions on the View
public class SubmitText implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
m.addData(v.getText());
}
}
}
public static void main(String[] args) {
new Control();
}
}
You can use Observable or PropertyChangeListener.

java Swing -- JPanel and PropertyChangeListener

My use case is that a List<String> is passed to a Jpanel and for each String in the List, the JPanel renders a UI component. This UI component consists of 3 buttons and my current code for my given use case is as follows. -- The code for the 'UI component' follows --
public class MacroEditorEntity implements ActionListener {
private String macro;
private JButton upButton;
private JButton downButton;
private JButton MacroDetailsButton;
public MacroEditorEntity(String macro) {
this.macro = macro;
upButton = new JButton("Up");
downButton = new JButton("Down");
MacroDetailsButton = new JButton(macro);
upButton.addActionListener(this);
downButton.addActionListener(this);
MacroDetailsButton.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent evt) {
if(evt.getSource().equals(MacroDetailsButton))
{
System.out.println(macro);
}
}
public JButton GetUpButton()
{
return upButton;
}
public JButton GetDownButton()
{
return downButton;
}
public JButton getMacroDetailsButton()
{
return MacroDetailsButton;
}
}
The code for my Panel is as follows --
public class MacroEditor extends JPanel implements PropertyChangeListener {
private static final long serialVersionUID = 1L;
private List<String> stringlist;
public MacroEditor(List<String> list) {
this.stringlist = list;
setupComponents();
validate();
setVisible(true);
}
public void setupComponents()
{
Box allButtons = Box.createVerticalBox();
for(String string : stringlist)
{
MacroEditorEntity entry = new MacroEditorEntity(string);
Box entryBox = Box.createHorizontalBox();
entryBox.add(entry.GetUpButton());
entryBox.add(Box.createHorizontalStrut(15));
entryBox.add(entry.getMacroDetailsButton());
entryBox.add(Box.createHorizontalStrut(15));
entryBox.add(entry.GetDownButton());
allButtons.add(entryBox);
}
add(allButtons);
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
revalidate();
repaint();
}
}
The code works fine for all Strings in the passed List. I want my Panel to pick up any change that may happen to the List like additions or deletions and add/remove relevant corresponding UI components accordingly. I think this can be done by using PropertyChangeListener but have not been able to account for that in my code.
Any ideas or suggestions on how i can make my Panel render/rerender stuff as soon as there are changes to the List would be of help.
What you need here is an observable collection. This should do it: http://commons.apache.org/dormant/events/apidocs/org/apache/commons/events/observable/ObservableCollection.html
Edit:
Here's the code snippet you requested:
public class ObservableListExample implements StandardPostModificationListener,
StandardPreModificationListener {
public static void main(String[] args) {
new ObservableListExample();
}
public ObservableListExample() {
ObservableList list = ObservableList.decorate(new ArrayList<>(),
new StandardModificationHandler());
list.getHandler().addPostModificationListener(this);
list.getHandler().addPreModificationListener(this);
//....
}
#Override
public void modificationOccurring(StandardPreModificationEvent event) {
// before modification
Collection changeCollection = event.getChangeCollection();
if (event.isTypeAdd()) {
// changeCollection contains added elements
} else if (event.isTypeReduce()) {
// changeCollection contains removed elements
}
}
#Override
public void modificationOccurred(StandardPostModificationEvent event) {
// after modification
Collection changeCollection = event.getChangeCollection();
if (event.isTypeAdd()) {
// changeCollection contains added elements
} else if (event.isTypeReduce()) {
// changeCollection contains removed elements
}
}
}
By the way: Another concept that helps to bind buisness objects to your GUI and react to modifications (bidirectionally) is Data Binding. Have a look at this, a Data Binding Library commonly used with Swing.

Categories

Resources