Java Swing and the Observer Pattern - java

I'm looking for a way to cleanly organize my UI code in Swing.
Let's say my UI-code is structured in the following way:
class MainWindow extends JFrame {
// SomePanel panel is added here
}
class SomePanel extends JPanel {
// buttons, checkboxes and so on are added here
}
Lets say I'm instantiating a MainWindow-object inside my main method:
MainWindow frame = new MainWindow("I am an App");
What is the best practice for listening to ActionEvents of buttons (which are declared inside SomePanel, which is declared inside MainWindow) from within my main-method?
Thank you very much for your help!

Use a PropertyChangeEvent, seen here and here, to communicate results from one container to another. Other ways to implement the observer pattern are mentioned here.
Addendum: You're suggesting writing custom ActionEvents?
EventListenerList is another way to implement the observer pattern. Such a list is common to every JComponent, and it is appropriate when more than one event type must be managed. JFreeChart is another popular example that uses diverse events to update chart subcomponents when the data model is changed.

Related

How to cast a reference with an object's original class?

I'm working on a GUI library for Processing, and I have got a class Component. I have a bunch of other classes that extend Component: Label, Button(Which extends Label), Panel and Icon. These classes continue just the "logic" part (position, text, function checking if they are clicked etc...).
The class Panel has inside of it an ArrayListof Component, and you can add to it buttons, labels etc...
To display the components I decided to use a class Theme, which has inside some overloadings of the function display(Component component) (one for Label, one for Button, one for Panel etc...).
Inside display(Panel panel), at some point, I display all the children of the panel, but, instead of treating them (the children) as Button or Label, it treats all of them as Component. How can I solve this ISSUE?
I've already tried to use a method display()inside the classes Component, Button etc... it works partly, but it gives some "coding problems": to override the function display you have to create a new class (Example: GreenBackgroundButton extends Button{}). Also, by putting all the function in an external class, you can control all the grapihcs of you Program with a single class, so you can have the same background for all the components with a single function displayBackground() etc...
instanceof can't be used, because, if I have to use it to cast the children of the panel, the user can't create custom components, because they would be displayed as Component not as TextField (example).
I've tried casting directly display((Button)panel.getChildren.get(0)), but, as it Should, when I use a label, it gives an error (as expected).
I've tried casting like this too: panel.getChildren().get(0).getClass().cast(panel.getChildren().get(0)), and it doesn't work, I don't know why. (I tried all its variants, like creating an external object etc...)
If you need more code, you can ask...
public void display(Panel panel) {
println("panel");
if (panel.isVisible()) {
pushMatrix();
translate(panel.getX(), panel.getY());
displayBackground(panel, #AAAAAA);
for (int i = 0; i < panel.getChildren().size(); i++) {
this.display(panel.getChildren().get(i).getClass().cast(panel.getChildren().get(i))); //THIS IS THE LINE WITH THE ISSUE
//println(panel.getChildren().get(i).getClass().cast(panel.getChildren().get(i)));
}
popMatrix();
}
}
Theme basicTheme;
Button close;
Panel panel;
void settings() {
size(400, 600, P2D);
}
void setup() {
basicTheme = new Theme();
close = new Button();
close.setBounds(10, 10, 100, 25);
close.setText("close");
panel = new Panel();
panel.setBounds(50, 200, 200, 200);
panel.add(close);
}
void draw() {
background(255);
basicTheme.display(panel);
}
I expect to see on the canvas a working button inside the panel, instead of seeing a component. I don't have any particular error I can think of right now.
Using instanceof is almost always a hack, and is probably a symptom of a bad design.
What you've described sounds exactly like an existing Java library called the Abstract Window Toolkit (AWT), so you might consider "borrowing" some of their design patterns.
Component is an abstract class that defines an abstract paint() function. The paint() function takes a Graphics argument. More on that in a second.
Button and Label extend Component and overide the paint() function.
Graphics is a class that contains functions like drawRect() and setBackground(). It encapsulates shared drawing code, similar to what I think your Theme class would do.
If I were you, I would consider taking a similar approach for your library.
I'll try to address some of your other comments:
Example: GreenBackgroundButton extends Button{}
Favor composition over inheritance for cases like this. You should not need to extend Button to set a background. Instead, have a setBackground() function in the Button() class (or in the Component class).
Also, by putting all the function in an external class, you can control all the rapihcs of you Program with a single class, so you can have the same background for all the components with a single function displayBackground() etc.
It sounds like this belongs in your Component class.
Also, taking a step back, I would encourage you to avoid the temptation to over-engineer a solution. You might think you need a complicated design with several levels of inheritance and many different classes, but chances are a simpler design will probably get you almost everything you need. Specifically, I don't see a huge benefit to extracting any code into the Theme class. I would probably put everything directly in my component subclasses like Button and Label.

EventListeners and custom gui components

I have a SWING GUI class that instantiates a custom JPanel for a portion of the display. This custom class has buttons and textfields and etc. My GUI class that owns the custom JPanel also has a controller class that handles the modification of the my data models. How can I pass actions from the custom panel to it's owner (my gui class) to handle the events?
I've had the thought that perhaps I can add to my constructor of the custom panel a reference to my controller class in the gui so that I can then set it as the actionListener on my buttons. Is this approach advisable? Is there a better approach?
Your View code (your custom JPanel) should have a Controller field (or some other way of obtaining your controller class). That way when you receive an action from the user - e.g. a mouse click on a button - you can call controller.doTheAppropriateAction(). Either pass the Controller in at construction, or use a Javabean setter pattern on it, and set it just after construction in your start-up logic (which sounds like your "GUI class"). I prefer the Javabean pattern, because GUI Editors need no-parameter constructors.
You should register your View as a Listener to the relevant Controller (or Model) classes, so that you will automatically be told when anything changes - so you can repaint() your Components (or do something more advanced). That will involve setting up your own interface (for the View to implement) and listener handling logic in the Controller (or Model).
Lombok PG takes the boilerplate out of the latter.
akf gives an alternative: register your Controller as an ActionListener to your View code. The advantage of that approach is that your View code will not be tied to a specific Controller - but the disadvantage is that your Controller will be tied to your View code. I tend to re-use Controller code for different UI implementations (e.g. Swing, GWT, JSP) so my Controllers and Models are never dependent on any particular View or atomic user action.
You can pass a reference to your parent GUI as an ActionListener to your custom panel. Your custom panel can then register your ActionListener-implementing parent class with all of your buttons, etc.
Your parent class would then get notified on each action.

Is ActionListener an example of Delegation pattern?

In my college days, I never realized what patterns were there in the Java API.
Now at work I came across Delegation pattern in Objective C n Cocoa on iOS where one screen sets itself as a delegate on coming screen so that that screen can pass some message to that delegate and it can take some action when it comes back to the previous screen.
I realize that I use to do something similar with when I used to pass "this" as as ActionListener [by implementing the interface] to a JButton and it would automatically call actionPerformed implemented by me in this class and thus I could change any instance data in my JFrame class.
So Is ActionListener an example of Delegate If I am correct ?
EDIT: As correctly mentioned below, It is Observer pattern. We dont set ActionListener we add one. Thus there can be many Listeners to that action.
ActionListener is an example of the observer pattern. You register observers (or listeners) on a component that get called when a specific event occurs.

How to switch between application's windows and communicate with the controller?

When writing a graphical interface, using Java, what's the appropriate way of switching between the different windows of the application, when clicking a button for example? I.E. what are the windows supposed to be, JPanels, JFrames...? And how do all the components 'see' the 'domain controller' (the class that links the graphical package to the application logic package)?
Any guide or reference would be appreciated.
You start your application with your Controller. In the constructor of your controller, you are going to initialize the first GUI you want to open, lets say GUI_A:
private GUI_A gui_a = null;
Controller() {
gui_a = new GUI_A(this);
}
As you might notice, I called the constructor of GUI_A with one parameter: this. this is referencing the instance of the current class, so this is type of Controller. The constructor of GUI_A has to look something like this:
private Controller controller = null;
GUI_A(Controller ctrl) {
controller = ctrl;
}
This is a simple way to get the GUI known to the Controller.
The next thing you would do is displaying GUI_A:
gui_a.setVisible(true);
If you now want to handle button-clicks, you would do it like this:
First, you add the action-performed method to your button. And, as it is best practice in MVC, you don't want to do logic in your view/GUI. So you also create a corresponding method in your Controller for the action-performed, and call it from your GUI:
// Controller
GUI_A_button1_actionPerformed(ActionEvent evt) {
// Add your button logic here
}
// GUI_A
button1_actionPerformed(ActionEvent evt) {
controller.GUI_A_button1_actionPerformed(evt);
}
Usually you don't need to pass the ActionEvent-var to the Controller, as you will not need it often. More often you would read a text out of a TextField and pass it on to your Controller:
// Controller
GUI_A_button1_actionPerformed(String text) {
// Add logic for the text here
}
// GUI_A
button1_actionPerformed(ActionEvent evt) {
controller.GUI_A_button1_actionPerformed(textField1.getText());
}
If you now want to access some fields on your GUI_A from the Controller, be sure not to mark the fields as public in your GUI, but to create public methods which handle how to display the values.
The preferable way is using Actions. You can attach action to each control. When user action happens (e.g. click on button) the appropriate Action is called. Actions can delegate calls deeper into the application logic and call graphical components (JFrams, etc).
suggestion: use tabbed-panel should do this, JPanel is just a Java container, while JFrame should be the outside windows, they are different things. there should be several JPanels on top of One JFrame. your app can have multiple JFrames.
When writing a graphical interface, using Java, what's the appropriate way of switching between the different windows of the application, when clicking a button for example?
Add an ActionListener to the button. In the actionPerformed(ActionEvent) method, do what needs to be done.
I.E. what are the windows supposed to be, JPanels, JFrames...?
I would recommend making the main window a JFrame and using either a JDialog or JOptionPane for most of the other elements. Alternately, multiple GUI elements can be added into a single space in a number of ways - CardLayout, JTabbedPane, JSplitPane, JDesktopPane/JInternalFrame, ..
And how do all the components 'see' the 'domain controller' (the class that links the graphical package to the application logic package)?
One way is to pass a reference to the object between the UIs.

Standards for using inner classes for GUI?

I'm wondering about the standard practice with inner classes (in Java but I suppose it applies to all OO languages).
So I have a JFrame subclass ControllerWindow that contains a JPanel subclass MapPanel which I draw onto (so it needs to overwrite paintComponent method) and which needs to implement a mouse listener. My current solution which works is to have MapPanel in a seperate class implementing MouseListener but when I showed this to the guy who runs my course the other day he seemed to think (we have a bit of a language barrier) this should be in an inner class in ControllerWindow or at least the MouseListener should be an inner class.
So my question is what would be the standard solution here, to put a MouseListener in the inner class, the JPanel in a different inner class or still in its seperate class? The JPanel implementing MouseListener in one inner class? And why?
The most important thing to me is that it works but I'd like to know about and understand the standard practices behind these things if possible.
EDIT: Very simplified version of current code below.
class ControllerWindow extends JFrame{
...
MapPanel drawPanel = new MapPanel();
...
}
and a separate class:
class MapPanel extends JPanel implements MouseListener{
...
public void paintComponent(Graphics g){
...//fillRects etc.
}
//MouseListener methods
public void mouseReleased(MouseEvent e){
requestFocus();
...
repaint()
...
}
public void mousePressed(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseClicked(MouseEvent e){}
}
Also could this be a situation where it would be acceptable to put both classes in the same file? I don't envisage using MapPanel for anything other than ControllerWindow.
It is common to use anonymous inner classes as event listeners because the code is usually quite simple (so a separate class may be overkill) and keeping the handler code "close" to the code that registers the listener can improve readability for people trying to understand your code, since all code related to the event is in one place.
EDIT: This is particularly true for classes that implement only one listener method. Perhaps less true for multi-method interfaces like MouseListener, since a class that implements the full interface will be more verbose.
I think it's somewhat arbitrary how you go about it (as Tom Hawtin commented, GUI standards=mud), since you're trading off complexity in the number of classes versus complexity in a single class. If you want to produce code simply for a demonstration, a single file might be easiest. If you want code that you're going to put into production and modify/maintain over time, abstracting out into different classes is almost certainly the way you want to go.
For example, if you embed MapPanel as an inner class in ControllerWindow, and then later want to replace it with a different type of MapPanel, you've got a massive update to ControllerWindow rather than just swapping out MapPanel for a different component type.
With the MouseListener, I'd be inclined to include it in MapPanel if it's handling events specifically for that component (that is, if only the MapPanel "knows" what a click means, it should be the one to process that click). I definitely wouldn't put it in ControllerWindow, since then you're "leaking" implementation detail from MapPanel. (The only case I can think of: in addition to your MapPanel, you have multiple panels type that all need to respond to clicks in the same way, so rather than implementing in each panel you could have the ControllerWindow do it. But even then, I'm not sure the code should be in ControllerWindow).
Whether MapPanel's mouse listener is an inner class implementation of MouseListener, or whether MapPanel implements it (as in your code above) probably comes down to a question of which style you prefer.
inner class would be better if it has a simpler syntax.
button1.click( function(event){ do something x... } );
button2.click( function(event){ do something y... } );
radio2.check ( function(event){ do something z... } );
java 7 may give us something like that and change the whole situation. as it is now, using a lot of annonymous inner classes can mess up the code and make it impossible to read. you should choose whichever style that makes your code beautiful and legible.
Because of multiple event handling requirement anonymous inner classes are required. Anonymous class can be written anywhere like in a class, in a method, in the argument. Therefore to abstain from creating many classes for each listener anonymous is preferred.
I found this article useful:
http://www.retrologic.com/innerclasses.doc3.html
In general, when you need to use a method pointer; extend adapter classes as inner classes to simplify your code.

Categories