Note: This is for a SWING course I am taking.
I have an assignment to make a simple graphics package (draw circles, squares, etc).
I was thinking of having multiple dialog boxes for entering the shape parameters, i.e:
Point has x,y
Circle has x,y,radius
Rectangle has x,y,width,height
etc.
I was thinking of creating a super dialog class with X,Y and extending it to allow for Width,Height or Radius etc.
For example, the rectangleDialog would invoke the super constructor with the additional parameters required:
public abstract class XYDialog extends JFrame {
public XYDialog(PARAMETERS ... params) {
// build the dialog by iterating through PARAMETERS
}
}
public class RectangleDialog extends XYDialog {
public RectangleDialog() {
super(PARAMETERS.WIDTH, PARAMETERS.HEIGHT);
}
}
then the super class is responsible for building the GUI
Does this seem like a reasonable approach? Does this make sense?
Thanks
Yes, I think it's a good solution. But, as stated before, reconsider the naming of your classes. If you extend a JFrame, call it SomethingFrame. If PARAMETERS is a normal class, it should not be in capitals.
I would also suggest extending JPanel instead of JFrame, and let the one instatiating these classes determine if to put them in a JFrame or a JDialog. A JFrame creates a whole new window, and you normally only have one main window for your application, whereas dialogs and panels are created on the fly.
Related
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.
This question already has answers here:
Why shouldn't you extend JFrame and other components? [duplicate]
(5 answers)
Closed 7 years ago.
I was used to do java swing programming with netbeans drag and drop, and never really cared too much about the code it generated. Now I am in the process of learning to code GUIs without drag and drops.
The fundamental problem occurred to me was, whether the window I am going to make IS_A JFrame or HAS_A JFrame. i.e whether to use inheritance or composition.
if MyWindow is a JFrame
public class MyWindow extends JFrame{
}
if MyWindow has a JFrame
public class MyWindow{
private JFrame frame;
}
Both seems fine to me. But I guess there should be a right way to do it out of these two. What is the correct way, and why?
If you want your class to behave as a window, then, you should extend it from JFrame (at least in my opinion). To my knowledge, this is how you should go about it.
If on the other hand, you want a class which has access to a window then you would go with the second option. That being said, you would still, at some point need to initialize a class which extends from JFrame.
EDIT: The answer to the question does say that, however it also says that it depends on what you are after. If I am understanding the answer correctly (maybe others could comment on this), if you have a scenario where you need a frame to print a list to a table, you could have a class which extends Frame and provides a utility method which takes in a list and prints it to a table. Your logic would then instantiate this class instead of the actual JFrame and use it to show the data.
My approach is that usually, I have a class which extends JFrame and provides a series of methods which make printing data easy. I would then have another class, which links logic and view layers. This class will have a reference to the JFrame extending class.
I have a JFrame application with some variables and a number of SWING widgets. In it I create an instance of another class and pass the JFrame to the child in the constructor. From the child, I can reference the variables, but not the widgets. Why?
// My JFrame
public class Prot2Prom extends JFrame {
// My Child
public Prot2Prom() {
super( "Protocol To PROM" );
Child child = new Child(this);
In the Child class my constructor does
Prot2Prom frame = null;
public Child(Prot2Prom gui) {
frame = gui;
}
The following works:
frame.<parent variable>=x;
The following does not:
frame.textArea.append("Hello");
The textArea cannot be resolved. There were all added with "new". Why can't I see them?
Some notes and recommendations:
This has nothing to do with "widgets" or Swing and all to do with visibility of variables. I'm guessing that textArea is not a public field of the Prot2Prom class.
If variables are public outside classes can "see" them, access them, modify them.
A possible solution is to in fact make the variables that you want other classes to see, public.
In general you really don't want to do this.
Instead much better is to give a class public methods that allow other classes to call and by doing so alter the original class's behavior. In other words, your Swing GUI classes should adhere to good OOPs principles just as any Java class should.
Later you'll want to read up on the MVC or Model, View, Control design pattern as a way of separating out behaviors of your code into separate logical entities, which can make your code much more flexible and powerful.
Edit 1
Regarding your comment:
The "widgets" are all created by WindowsBuilder Pro. I am trying to use the textArea to create my Eclipse Console for a stand alone (jar) application. How can I print to it from a class instantiated by the Frame?
You'll want to give the class that holds the textArea variable a public method:
public void appendTextAreaText(String text) {
textArea.append(text);
}
Then your other classes can append text to the JTextArea. Why is this important? One reason is that if the class that holds textArea will at some times not want to allow other classes the ability to append to this widget, it can have the logic to control this in the method. Thus it gives much more control over the widget to the class that holds it.
e.g.,
public void appendTextAreaText(String text) {
if (allowTextAreaAppend) { // a class boolean field
textArea.append(text);
}
}
As an aside, I also recommend that you put the code generation tool to the side and instead create your Swing GUI's by hand for a bit until you get a firm grasp of Swing and Java fundamentals. This will make your future use of the Swing code generation tool much better and productive.
The textArea cannot be resolved
This message indicates that there is no member class variable called textArea in Prot2Prom. This is possibly a typo. Perhaps the variable is called textarea or defined only locally in the constructor scope.
To work your class would look something like this
public class Prot2Prom extends JFrame {
JTextArea textArea = new JTextArea();
...
A better approach to updating text in a parent component is to create a method to Prot2Prom like so:
public void addText(String text) {
textArea.append(text);
}
This provides more control over how text is added to the JTextArea.
After some advice on using jpanel - I'm new to java and playing around with the GUI elements.
Bascially what I'm curious about is if I can set up a Jpanel in one class, then somehow add labels etc to the that container, but from another class.
Is this possible ? or do i have to set the entire GUI up in one class, but then I guess I would have the same issue, if I wanted to update those fields I had set up in the main class from another class?
Apologies I don't really have any code that's usefull to demostrate here - I'm just trying to get the idea going, working out if its possible before I go ahead. And I'm not even sure if this is possible. Any advice would be greatly appreciated.
Thanks
As long as you have a reference to the JPanel, you can add whatever GUI-element you want, by calling add(JComponent comp) on the JPanel.
So, you can do something like this:
class Panel extends JPanel{
...
}
class Main{
public Main(JPanel thePanel){
thePanel.add(new JButton("Hello"));
}
}
Was this what you were looking for?
You can also update the fields added to the panel from another class, if you have a public accessor-method set up, in the class. So in your panel class, you have a method:
public JButton getButton(){
return button;
}
Then you can access the button from whatever class with a reference to your panel class, like this:
panel.getButton().setText("Some text");
Note that the button could just as well be public, then you could simply call the method directly: panel.button.setText("Some text"); but this is not considered good code, as it violates some general good OOP practices, not relevant to mention here.
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.