I have a main controller class that shows a JFrame containing a JTable and, for each row in this table, I have to show a specific "form" on doubleclick.
This secondary window will need information about the specific row selected on the main JTable, as well as some objects saved as fields in the controller class.
An conceptual example of what I need to do is the following:
I have a set of Shops (listed in the JTable in the main JFrame) and, on double click on a row, another window has to appear, allowing for a management of the Shop (sending orders, checking deliveries, etc...).
My question, me being such a newbie with Swing, is: what is the best organization for a common pattern like this one?
Should I model another JFrame and pass as arguments all the data that I could happen to need (I really don't like this), or should I pass only a reference to the Controller class (this would be against the MVC pattern, I think).
Or maybe I should use a JDialog instead of another JFrame? The thing is that, really, the functionality that I need from this second window are a little too big for a dialog, I think...
I am confused, any tip/suggestion/advice will be much appreciated!
Thank you
Regards
Or maybe I should use a JDialog instead of another JFrame?
Bingo.
I actually don't like the idea of having a listener inside my Model class (aka Shop) – implementing the ActionListener. I think I would extend the JDialog class (let’s call it MyJDialog) then when a row is double clicked … create a new instance of MyJDialog class and pass in the Shop object in the constructor. Within the MyJDialog class you can modify the Shop object by calling mutators (setters). Moreover, the Shop class should have a way of notifying observers when a property is changed – take a look at PropertyChangeSupport.
Related
I'm developing a desktop app in java for a school project and I want to know how should I be designing the code of the GUI, specially because I will later have to run JUnit and functional tests on the app. I know it kinda sounds like an opinion-based question, but all I'm not asking for the "best" method, I just want to be pointed to a specific design pattern that can fit the need of my app.
To elaborate a little more on the type of application, it's "stage based", one button leads to a different section of the app. One main window, only error or warning popups (only one frame).
My current approach so far:
So far I've designed 3 different JPanel classes using eclipse's WindowBuilder (main menu, admin menu and user menu) and a AppGUI class that creates and empty frame with a CardLayout layout and fills it with one of the 3 panels (starts with main menu). Then the buttons on such panels point to the next panel. (Example, main menu -> user menu -> create profile)
The panels need to communicate with the AppGUI instance in order to execute the panel changes and later on, the functionality that the other options will have. To do this, I've been passing the AppGUI instance as a parameter to the panels on initialization and storing it inside each panel (I first thought of having a singleton AppGUI and have static reference to it, but I later on read that it's bad practice, specially for later mock testing).
Also I'd like to have the logic for the application on different classes other than the GUI classes and I'm not sure if passing the AppGUI as parameter to every method out there is good practice or not
In conclusion, it would be great if someone could point me to a specific design pattern for this kind of GUI, thanks in advance!
I would suggest the Model View Controller (MVC) pattern.
The idea of MVC is that the models (your app logic), the views (UI elements), and the controllers all work together. The models and the views are completely independent of each other i.e. you don't pass a model class object to a view class object or vice versa. The models and the views should talk to the controllers and the controllers makes all the decisions.
Controllers
They contain your app's UI logic, like how to layout the views. They are notified by the model once in a while (e.g. some data have changed!). They are also notified by the views (e.g. the user pressed a button!). And controllers need to respond to these notifications. For example, when the user pressed a button, the controller might tell another view to show, or tell the model to do some calculations or some other stuff. In your case, your controller is most likely to have all the ActionListener for the buttons and text boxes on the UI.
Models
They contain your app's logic. Try to design your model to be UI independent as much as possible. If designed correctly, your model should still work without the UI i.e. you should be able to make a command line version of your app without changing the model. For example, if you are making a calculator, the model should do the actual calculation.
Views
Most of the time, you don't need to worry about this part because swing already provides you with lots of view classes, such as JButton, JPanel, JTextArea etc. If you happen to be creating your own views, remember that it should not contain anything related to the model i.e. the view should work fine with a different model.
You can always search for more info on the web.
I want to fill values of multiple jTextBox from a jFrame into another, using accessor methods like
String getNameVal()
{
return jTextBox1.getText();
}
How to call these methods from another jFrame?
Suggestions:
It sounds like your GUI code is geared towards making JFrames, and if so, you will want to avoid this. You are painting yourself in a corner by having your class extend JFrame, forcing you to create and display JFrames, when often more flexibility is called for. In fact, I would venture that most of the Swing GUI code that I've created and that I've seen does not extend JFrame, and in fact it is rare that you'll ever want to do this.
More commonly your GUI classes will be geared towards creating JPanels, which can then be placed into JFrames or JDialogs, or JTabbedPanes, or swapped via CardLayouts, wherever needed. This will greatly increase the flexibility of your GUI coding.
This question has direct bearing on your problem. I will guess that your main problem isn't how to give classes getter methods, and how to have other classes call the getter methods. More often then not, when faced with the issue of extracting information from one GUI view to another, the issue is one of when to extract the information. If you displayed your second window as a non-modal JFrame, and then had the calling class immediately extract the data from that second JFrame, you'd get nonsense data, because you'd be extracting data before the user would have time to interact with the 2nd window and enter data.
One possible solution to this when using non-modal windows to get information from the user is to use a WindowListener so you can be notified when the user has completed his dealing with the second window, and so now data can be safely extracted.
Often better is for the 2nd window not be non-modal, as JFrames are, but instead to be a modal window such as a modal JDialog. When the calling code displays a modal dialog, all code flow in the calling code stops until the dialog is no longer visible. In this situation, no WindowListener is needed since you will know exactly when the dialog has been dealt with -- on the code line immediately after you set it visible -- and so can extract your data from it with ease.
A nice variant on this has already been mentioned in by Andrew Thompson in comments -- use a JOptionPane. Don't poo-poo this option since JOptionPanes are powerful tools, likely much more powerful than you realize as they can hold fully formed complex JPanel views, and behave just as described above, as modal dialogs.
If you need more specific help, then please don't hesitate to comment to this answer. Also if so, then consider creating and posting a Minimal, Complete, and Verifiable Example Program where you condense your code into the smallest bit that still compiles and runs, has no outside dependencies (such as need to link to a database or images), has no extra code that's not relevant to your problem, but still demonstrates your problem.
Edit
For my mcve code examples of the above suggestions, please my answers to the following StackOverflow Questions:
Using a modal JDialog to extract information
Using a JOptonPane to extract information
I assume the textfields are present in frame1 and you want to access them in frame2. The following can be a way to achieve this:
First create getters for all JTextFields that you have in your frame1. Alternatively you can have them in a panel and call getComponents() method.
Create a private variable of JFrame type in your frame2.
Modify the constructor of frame2 to receive the frame1 object and assign it to the private variable of JFrame type.
Now you can create a close() method in frame2 which disposes the frame2 and sets frame1 to visible.
But in my opinion you should create a class which handles the data in these textfields. Initialize the class object in any button click of frame1 and check for any inconsistency in the input. I can guess there is something wrong with your design.
I am building a graphical user interface in Java and I was wondering if anyone can give me some directions.
Specifically, When clicking on a button in this GUI a big JDialog (it's a class that extends a JDialog) opens up for the user to enter some information, see some information, click on some things etc.
This class has multiple JLabels and other components in it. My question is how would one create such an instance? Right now I have everything in the constructor, making it a 500(!) lines constructor. There has to be an alternative to that!
The constructor is about 300 lines of code of components placement and settings for them and another 200 lines for the listeners give or take.
Also another question that comes in mind is that right now I open this window from another class by calling
MyClassExtendsJDIalog temp = new MyClassExtendsJDIalog();
but I don't use this "temp" variable at all in the class that I create it, as the constructor of "temp" does EVERYTHING. It seems like I am, again, doing something the wrong way.
Thanks for reading this.
If you want split up the code to make it more readable and manageable you can allways group the fields into subclasses of JPanel, i.e. Panel1, Panel2 etc and then just add those in the JDialog subclass constructor. But setting up a GUI in swing takes a lot of lines, yes...
as for the temp variable not being used, I'm guessing you call show() in the constructor?
Normally I would not do that, but instead call it in the code creating the dialog, ie.
MyDialog dialog = new MyDialog();
dialog.setVisible(true);
Without seeing your code it's difficult to give you specific tips on how to improve your code and design.
I agree, ~500 lines in your constructor intuitively feels like a problem. At a minimum, you might want to defer some of that work until after the instance has been created. Moving this into a init() method (or series of methods) is one approach.
You also might want to consider designing your class such that it contains a JDialog instead of extending one (See Composition over inheritance for a discussion on this topic) I suspect otherwise you are conflating the concerns of several classes.
Finally, you might want to review the Java Swing Tutorial for general tips and techniques for creating Swing based user interfaces.
Yes you are doing something wrong, but you are in good (or bad) company.
You should apply the rules of good OO design and clean code to your swing classes as to anything else.
What exactly you can do is hard to tell without looking at the 300+ lines of code (and I really don't want to see the ;-) but here are somethings that are probably applicable.
My main design rule is the Single Responsibilite Principle. According to your description (and my guesswork), you are doing the following in your constructor:
* creating (including configuring) components
* placing them in some kind of layout
* registering Listeners
* implementing Listners
These are 4 completely different concerns.
After some heavy refactoring you might end up with something like this:
You might want to have a factory that creates your components.
You might have a Layouter class that takes a component or a group of components (like label plus matching textbox) and places them on a panel.
You might have a class takes components (and maybe models or whatever) and wires them together using Listeners.
And a class that gets passed all this uses it in a suitable way and spits out a JDialog with the panel with all your components on.
Note: Idealy nothing extends JDialog anymore. You should only extend Janything if you intend to build a new Swing component.
This example is using Scala but it still should offer some inspiration: http://blog.schauderhaft.de/2011/06/26/clean-code-with-swing-and-scala/
In the GUI book we use in class there are many examples of how graphical user interfaces are made in Java. So many examples, that I'm very confused regarding which one should be used when it comes down to a big application.
So I've seen examples
in which the main class extends JFrame
where the JFrame object is created inside the main method
where the main class extends JFrame AND implements ActionEvent interface
where Listener classes are declared inside the main class
Sure, I can work with all of these, but right now, as I don't have any kind of experience, I don't see the benefit of using any of them. Is actually one of them the correct way to do it or it depends on my sittuation?
Thank you!
"Is A" or "Has A"? This is the question that should be asked when considering extending a class. If the new class "Is A" frame, extend frame, but if the class just needs a reference to a frame, don't extend.
In fact, if a custom component is required, extend a JComponent or JPanel, then add that to a frame, ..applet, window, JInternalFrame, dialog, constraint of a layout, part of a split pane..
Listeners
As to the listeners. Rather than traverse a huge if/else structure in the single actionPerformed() method to determine the required action, it is more optimal to either:
Create a listener for each control that needs it.
Create an instance of an AbstractAction that might be used for multiple controls ('copy' button, menu item etc.).
Summary
So (generally) for the:
JFrame, don't extend.
Listeners, create and add as needed.
Honestly, it depends on the situation. One basic rule when coding is to "code to abstract classes or interfaces".
So, in a nutshell, have a class extending (or implementing) a JFrame (or whatever interface or class) and/or have one doing the same thing with ActionListener.
It is all about the maintainability, flexibility and cleanness of your code.
Standard approach: use EventQueue in method main, that creates main form. In that case all your operations will be asynchronous
in which the main class extends JFrame
the main calss doesn't have to extend JFrame. if it doesn't you should create a JFrame object like you do with any other class
where the JFrame object is created inside the main method
If the MainClass extend JFrame it created inside the c'tor (in the super() ).
Set up:
I have a mainPanel with a tabbedPane on it, i have a separate JPanel 'extra', extra creates an objects and i wish to pass that object back through to the mainPanel where i can actually use it/add it to the data structure.
Frame > Panel > TabbedPane > Panel (Separate Class, instantiated as new object)
I tried to add a listener in mainPanel that checks if a boolean in PanelExtra changes and then runs a method etc, but it didn't work.
I would make a method in mainPanel to accept the object but i don't know how to refer back to it. (getRootPane() didn't return anything)
Also im not sure if im using correct terminology, while i was taught Java in a command line Unix environment, Swing is very new to me.
I tried Listeners, Observers and am currently considering an object created at root and passed DOWN through all objects (As java passes by reference, i could pass information back as far as i want) If that isn't an entirely stupid idea, let me know and we can all go about our lives. If there is a better way to do it that'd be great.
(File overview)
[programApp]>[programView>tabbedPane]>[panel]
Panel is created using new, and exists in a separate class.
I want to pass data back from Panel to programView (projectNameView as it is in netbeans)
I will attempt to add as much information as i can.
You can use the MVC-pattern for that.
Actually there are couple of variants depending on your preferences or exact task.
For very simple case you can just use class with static fields to pass objects.
Another way is to define your own listeners like:
private myPanel extends JPanel implements Notification {
...
and where you create that panel:
myPanel.addNotificationReciever(mainPanel);
Later you just call fireNotification or whatever and implement similar listener for your mainPanel.
SirVaulterScoff gave the best answer in my opinion, but I thought I'd add some more (can't add comments yet, so I'm creating a new answer).
You should also read the Observer-Observable pattern (Wikipedia article), which is used along with the MVC pattern. It will be useful when linking your MVC classes together, to make sure everything is as loosely coupled as possible.
As a side comment: MVC is really a pattern you should focus on if you are to create applications with a user interface. It will save you many headaches, and will make your application easier to maintain and extend.