I'm working on an existing project and am wanted to simplify and make usable it. I have two GUI that I want they to feed from just one resource else their some GUI codes. I mean there are gui1.java and gui2.java consist of their GUI codes. And, one for their common parts. Let's call it as common.java. With gui selection part I can satisfy which gui selected (gui1 or gui2). I think to extend common.java by JFrame then extend gui1.java and gui2.java with common.java. Moreover, If there is external part of one of that GUIs, I can add external part using if condition (as I said I can determine which gui selected.) For example:
protected void MovementStateControl() {
try {
URL url = new URL(NameofMyproject.GetWepIp() + "<MESSAGE><Command_No>4</Command_No></MESSAGE>");
URLConnection connection = url.openConnection();
Document doc = parseXML(connection.getInputStream());
NodeList Settings = doc.getElementsByTagName("SYSTEM_SETTINGS");
Node MovementSystem = Settings.item(0);
Element MovementElem = (Element) MovementSystem;
jLabel7.setText(MovementElem.getElementsByTagName("device_name").item(0).getTextContent());
SystemGuiSelect.DeviceName = MovementElem.getElementsByTagName("device_name").item(0).getTextContent();
NameofMyproject.signal_max_level = Integer.parseInt(MovementElem.getElementsByTagName("signal_max_val").item(0).getTextContent());
/* If gui1 is selected, the part should be done as well. Otherwise, just above part okay. */
if (gui1) {
NameofMyproject.signal_min_level = Integer.parseInt(MovementElem.getElementsByTagName("signal_min_val").item(0).getTextContent());
if (!"EXISTS".equals(MovementElem.getElementsByTagName("polarization_system").item(0).getTextContent())) {
jLabel24.setVisible(false);
LblPolAngle.setVisible(false);
lblPolTarget.setVisible(false);
jLabel13.setVisible(false);
jTextField3.setVisible(false);
jButton16.setVisible(false);
jButton8.setText("Tx-Xy");
jButton3.setVisible(false);
jButton4.setVisible(false);
jProgressBar3.setVisible(false);
jLabel36.setVisible(false);
jLabel37.setVisible(false);
jLabel5.setVisible(false);
jButton18.setVisible(false);
} else {
jLabel24.setVisible(true);
LblPolAngle.setVisible(true);
lblPolTarget.setVisible(true);
jLabel13.setVisible(true);
jTextField3.setVisible(true);
jButton16.setVisible(true);
jButton8.setText("Tx-Xy-Zu");
jButton3.setVisible(true);
jButton4.setVisible(true);
jProgressBar3.setVisible(true);
jLabel36.setVisible(true);
jLabel37.setVisible(true);
jLabel5.setVisible(true);
jButton18.setVisible(true);
}
}
} catch (Exception e) { }
}
The problem is here I want the common GUI parts to put into common.java to remove code duplication because of having two GUI same codes too much. Of course because common.java is super class it cannot be aware of elements of its sub class. (JLabels, JButtons, etc. can't be recognized) Even if their thread parts are same, I can't feed them from same source. The GUIs have been created using NetBeans. By the way, my solution for the problem is adding parameter all methods but what about for the method how many arguments are there? (Maybe I use vargs) however, I wonder whether there are more efficient solution.
As far as I understand you have 2 basic problems you need to deal with. First is the separation of UI view from control logic, the Second is to increase the reuse within your Swing code.
My solution would be introducing an In Process Event Bus for communication between View and Control and Extract Components with common layout pattern from your current UI classes to increase the reuse within you Swing code.
Separating View from Control using an Event Bus
You need this because your view shall differ, but your control logic shall stay the same. You need to find a way to tell your UI that the device_name has changed. I would suggest to use a Event Bus implementation therefore. There are some realizations that already solve the EventDispatchThread handling for you.
Separated by an Event Bus your gui2.java will only react to the events concerning the visual elements it contains and gui1.java which has all currently known fields will react to all events. Your controller just publishes the events without knowing if it is consumed by someone or just dropped.
Ectract Components to increase reuse within Swing code
I would suggest to identify common parts of the UI, like Groups of labels, textfields and so on, that functionally belong to each other (e.g. Surname and Forename of a person). Extract them to a separate class extending JPanel and reuse them wherever you need it.
Do not register these fragments directly to the EventBus or at least inject the event names they should react to. Otherwise you may not use two instances of the same component within one JFrame because they would automaticallyshow the same values.
I hope this provides some ideas where to start your refactoring and which direction to go.
As you mentioned it, the super class should not know about its subclasses.
Instead of using inheritance, use composition.
You common.java should be composed of its specialized containers. This will decoupled common.java from gui1.java and gui2.java, in the future if you require to add a new gui3.java you won't break any APIs and its easy to just create a new SpecializedGUI.java
By basing on your example of method, i propose you to use a simple solution to solve your problem.
First, as Gilbert has suggested, the common widgets (textfield, label, etc...) must be in the abstract common class and if possible layed in the common class (it is more simple). Of course, the specific widgets to a concrete gui will be in the concrete class.
To ease the use of common widgets which are manipulated by the concrete classes, declare your fields as protected in your abstract common class.
Second, in your snippet, you do
if (gui1) { then...
Instead of conditional statements which are not needed (it's not the role of the parent class to know the children guy) and cumbersome (not maintenable in the time) , I propose you to create listeners. These will be simple interfaces that your concrete classes must implement.
It does simply dispatching to the concrete classes the responsibility to act or to no act when a event is dispatched by the parent class.
In order to force your concrete classes to implement the interface, make your abstract class Common to implement this interface.
In this way, the concrete class will not have the choice to implement it.
Here a very simple example with two guis and illustrating the presented concepts :
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public abstract class CommonFrame extends JFrame implements MyFrameListener {
protected JLabel myLabelForControlMove;
private JButton btnControlMove;
private JPanel panel;
public CommonFrame() {
panel = new JPanel();
add(panel);
myLabelForControlMove = new JLabel("waiting for information...");
panel.add(myLabelForControlMove);
btnControlMove = new JButton("click to control move");
panel.add(btnControlMove);
setVisible(true);
pack();
// call concrete class with no aware which one is used
onMovementStateCreation();
btnControlMove.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// call concrete class with no aware which one is used
onMovementStateControl();
}
});
}
}
public interface MyFrameListener {
void onMovementStateCreation();
void onMovementStateControl();
}
public class Gui1 extends CommonFrame {
#Override
public void onMovementStateCreation() {
myLabelForControlMove.setText("control starts");
}
#Override
public void onMovementStateControl() {
myLabelForControlMove.setText("control with state Gui1");
}
}
public class Gui2 extends CommonFrame {
#Override
public void onMovementStateCreation() {
myLabelForControlMove.setText("control with state Gui2");
}
#Override
public void onMovementStateControl() {
// does nothing
}
}
Related
I created a JFrame with buttons, labels and texts and I want to show it two times when I execute the main program, so I did like that:
import java.net.SocketException;
public class Main {
public static void main(String[] args) throws SocketException {
new MyFrame("client1");
new MyFrame("client2");
}
}
The result: I get two frames: one with the component of the other one inside, and one empty.
How to resolve this issue?
You are using static instance fields with your MyFrame for your components
A component can only reside within a single container, the moment you create your second frame, the static components are removed from the first container before been added to the second.
The solution, don't do this, ever...
I assume you are using static because you want to access these fields from another class, in that case, you should appropriate getters in the MyFrame class and pass a reference of it to those classes that need it.
Alternatively, you could establish a series of observers who monitor for changes and take appropriate action, this helps to decouple the code.
Personally, if you need to modify the MyFrame instances in some way, I'd provide setter methods that update your components instead, as I don't like exposing UI elements without good reason, to much opportunity for others to mess with them in appropriately
Firstly, I come from a big PHP background with MVC, when I started with PHP, I browsed a lot of times, to try my best and perfect my MVC-Like design in PHP. A lot of people like answered with answers which helped me a lot.
But after starting GUI development in Swing, the answers about MVC in Swing are totally different. For instance, a model is also a view? According to Oracle's Suggestions TextElementModel doesn't have any logical business here, all it does is markup (set color etc) and setting data such as set text and so on. When I developed in PHP, there is no such thing as AbstractModel because I always got told that a model is not a class, or more, it's a whole layer that processes logical business.
In PHP I used Services, Data Mappers and Domain Objects, suggested from this amazing answer which helped me in PHP a lot: How should a model be structured in MVC?
My attempt
After reading again, I thought to give it a go and do a similar thing in Java:
I have the ControllerContainer which creates all controllers:
public class ControllerContainer {
private JFrame frame;
public ControllerContainer(JFrame rune) {
this.frame = frame;
}
public void initControllers() {
Atest test = new Atest(frame);
test.registerView("test", new ViewTest(test));
}
}
As you see, I add the view named "test" with instance of ViewTest to the controller, now it will be visible in the frame, and can take input.
My Controller class, which should be abstract, but I didn't make it abstract yet:
public class Controller {
private JFrame frame;
private Map<String, Component> views = new HashMap<String, Component>();
public Controller(JFrame frame) {
this.frame = frame;
}
protected void registerView(String title, Component c) {
this.views.put(title, c);
this.frame.add(c);
}
protected void deregisterView(String title) {
this.frame.remove(this.views.get(title));
this.views.remove(title);
}
protected Component getView(String title) {
return this.views.get(title);
}
}
And an test controller:
public class Atest extends Controller {
public Atest(JFrame frame) {
super(frame);
}
public void hit() {
((ViewTest) super.getView("test")).changeColorBlue();
}
}
And my TestView:
public class ViewTest extends JPanel {
private final Atest controller;
public ViewTest(Atest c) {
this.controller = c;
setBackground(Color.RED);
setLocation(0,0);
setSize(300, 300);
setLayout(null);
JButton b = new JButton("hello");
b.setSize(150, 150);
b.setLocation(0,0);
b.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent arg0) {
controller.hit();
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
});
add(b);
}
public void changeColorBlue() {
setBackground(Color.BLUE);
}
}
The problem
As you see, my view creates a new button, and adds a mouse listener to it. the listener will have access to the controller to pass input. The controller gets the input and changes the view.
Basically the controller instantly forced to update the view, without any serious logical business because it's not needed in my case.
According to the link I have posted above, answered by tereško, how can I use his idea & suggestions with Java Swing correctly?
I am really confused, after the PHP background.
Maybe I am misunderstanding and everything should be done differently in different languages? But I thought that patterns should always be implemented the same way.
If you need more information, let me know.
MVC pattern it is a common paradigm, so there is no differences between pragramming languages in general.
However the implementation and some terminologies sometimes look different. In Java Swing it is often to see two following approaches:
1. Classic MVC
Controller - Listens user interface actions, performs corresponding Model updates. Can listen actions from different Views.
Model - Represents the state and the domain logic, methods to modify the state. Notifies listeners about the model updates (several Views can listen the updates). Model is independent and knows nothing about the listeners and their logic.
View - Responsible for user interface, UI elements layout, also listens Model updates and update the graphic inteface if required. Has some knowledge about the model, in example shown below it knows how to process list of "items".
Design of some simple "To Do" app can look like:
2. MVP (Model View Presenter)
Controller acts as a Mediator between the View and the Model. View become very thin and knows nothing about the Model and interact with Controller only. Controller listens both View and Model and perform corresponding actions.
Swing itself adds some confusion because it uses MVC pattern for its UI components. Each UI control has a Model and View. It makes easier to design new UI components, however in a "big picture" of the whole Application design - UI controls stay on the View layer.
Maybe I am misunderstanding, and everything should be done differently in different languages?
There is no misunderstanding; the pattern is merely applied differently.
As noted in a comment by #ordous and this this answer by #udalmik, a Swing application may have multiple implementations of the MVC pattern. As noted here and here, "not every interaction needs to pass through your application's controller." In contrast, a web application may well "have a 1:1 relation between views and controllers."
The Swing separable model architecture, cited here, "collapses the view and controller parts of each component into a single UI (user-interface) object." Swing controllers are scattered among the descendants of JComponent, typically in the component's UI delegate. As a concrete example, BasicButtonUI contains a BasicButtonListener that handles user mouse interaction.
Almost used the answer in the link, but the fact that his controller extends JPanel ruined it, totally confused me there.
This can be confusing, as a simple Swing program may have no explicit controller at all. As suggested in this outline, the controller has direct access to any relevant view and model; it may also listen to user interaction with the view. The example was intended to illustrate the simplest such interaction. It is mere coincidence that the effect is evoked by user interaction with a view component. The simulation cited here, for example, has a ControlPanel of view components that update the application's view and model. The DisplayPanel listens directly for a ComponentEvent that requires a model update. Etc.
Your application's controller is then free to focus on the application's needs.
#Marco13 elaborates on this and cites additional examples in this related answer.
I'm just trying to figure some things out in my head about getting information to and from swing components from other classes.
I have a main class that uses a few classes to build a swing gui. How do I go about writing information to these components from another class. As far as I understand I need to use an arraylist to store references to these components but I'm not exactly sure how to do this, can someone please help me out?
I would suggest that you try to separate the model from the view. Don't store data relevant for the application logic in the actual GUI components.
Storing references to the components in an array list, and then access the data via the array list and various getText methods just seems like a bad idea to me.
Store the values in an object representing some model, and let the GUI reflect the state of the model.
This is one way of accessing another class's methods:
public class MyFrame extends JFrame implements ActionListener
{
private final MyBusinessClass bc = new MyBusnessClass();
#Override public void actionPerformed(ActionEvent e) {
this.bc.someBusinessMethod();
}
}
I'm currently trying to develop a simple GUI notepad to increase my skills in OOP.
I stick to the Single Responsibility Principle while developing.
I've divided the application into several parts following the principle.
// This class runs the whole application
public class Notepad {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new NotepadFrame();
}
});
}
}
// This class is responsible for setting up a frame
public class NotepadFrame extends JFrame {
public NotepadFrame() {
super("Notepad");
MenuBar menuBar = new MenuBar();
setJMenuBar(menuBar.createMenuBar());
pack();
setVisible(true);
}
}
// This class sets up a menu bar
public class MenuBar {
public JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
Buttons buttons = new Buttons();
menuBar.add(buttons.createFileMenuItems());
menuBar.add(buttons.createEditMenuItems());
menuBar.add(buttons.createFormatMenuItems());
menuBar.add(buttons.createHelpMenuItems());
return menuBar;
}
}
There are also the other classes.
Buttons - this class is used to create the buttons of the menu bar.
MenuItemActionListeners - this class handles all of the events fired by the buttons.
Have I divided the application in a correct way according to the Simple Responsibility Principle?
I'd also like to find out the best ways to handle action listeners, mouse listeners etc.
So far, I have used one actionPerformed(SomeEvent e) method to handle all of the events.
private JButton button;
private JButton button2;
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == button) {
} else if (source == button2) {
} // etc.
}
I realize this is the bad way of handling event listeners. As the application's size grows, adding so many conditional statements will make the code lest readable and the CPU performance will decrease.
How do you handle event listeners in your applications?
How would you write event listeners when developing huge and serious applications like MS Office or AutoCAD?
See How to Use Actions, which shows how Action "can be used to separate functionality and state from a component." Charles Bell's HTMLDocumentEditor is an example that shows how to reuse existing actions, as well as creating your own based on AbstractAction.
You are using one location where you handle all the events, which is a problem. In bigger applications you will handle events differently, different classes in your application will handle different events. Components listen to a few events only in which they are interested and not to all. For exmaple, a undo event makes sense to a paragraph object only and not to File object, a save event would make sense to the menu object and the document object.. etc.
Ideally, you classes would raise certain events and it will allow any other classes to subscribe to those events. Those subscribes are notified when an event occurs.
I would suggest you to look at the Observer Design Pattern.
I am very happy to see you want to use SRP while programming. The way you have done is perfectly acceptable. However, there is always a question on what level of these principles do you want to apply?
Also, in the code you provided (actionPerformed method), it is not OCP (Open Closed Principle) compliant and hence a bad design. Instead I would have separate methods for each type of event in each of those components that you have.
To keep the code DRY, you can have a method which tells you what to do when a component is selected. Delegate to this method from the several methods (due to different type of events) you associate for this component.
Hope this helps..
I'm currently working on a hobby project, written in Java, containing about two different JFrames with about 3-4 JPanels each. The problem I'm facing is that I'm not so sure how to handle the interaction with my controller and different view classes.
For instance, I've an article by Sun on Java App. design with MVC, where they list the option to let all the communications between model and view go through the controller as the most modern one, a tactic I've been taught as well and should do just fine. However, it doesn't quite go as deep as to explain how to handle all the ActionListeners involved for all the elements and different panels.
The 'way to go' I've been taught so far is to either use one controller and set the ActionListener to the various objects through getters and keep track of the active panel. The alternative my awesome Java book gives is to just stick to inner classes for each view class. Either method seems to result in pretty nasty code, while I'd prefer one or several controllers with as little bloat as possible.
My question of course is; how would you assign the ActionListeners as neatly and efficiently as possible while keeping the controller(s) usable?
Even though Markus' solution remains the neatest I've seen so far, I've made some slight changes, which are hopefully improvements in the eyes of the original author as well ;).
In case you add/remove an item of the list in response to an event, there is a chance of ending up with concurrent changes to the list; the event is handled, but the loop will continue in case there are more items after the one catching the event.
Therefore it's required to use a slightly different approach. I've decided to use the ListIterator.add() method, as this one supports modifications to the list while walking through its items. Possibly there are more elegant solutions available, but this one does the job pretty decent.
In case it will be of help to others; the modified code would similar be the following:
ListIterator<ActionListener> iterator = mActionListeners.listIterator();
public void actionPerformed(ActionEvent e)
{
iterator = mActionListeners.listIterator();
while (iterator.hasNext())
{
ActionListener actionListener = (ActionListener)iterator.next();
actionListener.actionPerformed(e);
}
}
public void addListener(ActionListener listener)
{
iterator.add(listener);
}
public void removeListener(ActionListener listener)
{
iterator.remove(listener);
}
In case there are better solutions or improvements I'll be glad to hear those as well.
The controller can implement ActionListener and contains a List of ActionListeners.
The controller you have to add to the The 3-4 top panels action listener.
Now you can add any ActionListener you want to the controller action listener list.
public void addListener(ActionListener listener)
{
mActionListeners.add(listener);
}
public void removeListener(ActionListener listener)
{
mActionListeners.remove(listener);
}
When the controller Action listener is called you have to call all action listener which are inside the controll list.
public void actionPerformed(ActionEvent e) {
List listeners = mActionListeners; // Edit 20090903
for (Iterator iterator = listeners .iterator(); iterator.hasNext();) {
ActionListener actionListener = (ActionListener)iterator.next();
actionListener.actionPerformed(e);
}
}