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.
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.
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.
I've been working on a Java Swing project where I need to retrieve the object/instance that created a panel in order to call a simple save method particular to that instance.
You have a JFrame with a JTabbedPane that has tabs created by instancing a class which builds a JPanel and adds it to the JTabbedPane, I need to find the specific instance from the selected JPanel/tab on the JTabbedPane to then call it's save method.
Any ideas?
Thanks for your time!
public class frame extends JFrame implements ActionListener{
Builds a frame dubbed "frame" that is static.
Builds a static JTabbedPane dubbed "pane"and adds it to the frame.
Creates a button that creates a new instance of sheet.
public void actionPerformed(MAGIC!){
See if a button on the panel has been pressed and uses the currently selected tab to locate the correct instance of sheet to run it's save method.
}
}
public class sheet extends JPanel{
In constructor makes a JPanel and adds it to "pane"
Describes a save method that outputs a variable unique to the instance.
}
I figured out all I needed to do was store new tab objects in an ArrayList derp. Thanks for your attempts though guys!
Rather than just connecting back to the original creator, my approach to this was to create / use an interface that expicitly supports saving. I created something for this in TUS, my sourceforge project
http://tus.svn.sourceforge.net/viewvc/tus/tjacobs/io/filepersist/
Check out Persistable and Persistable2. Of course anything can be a Persistable, but the abstraction let's you get away from explicit ties back to the creator class
You can add a field in the new JPanels that point to the instance of the creator. I don't think there is any such method to point back to parent class in the API.
--EDIT--
You may want to check
http://docs.oracle.com/javase/tutorial/uiswing/components/tabbedpane.html
getSelectedIndex() may be what you are looking for.
I'm new to JSwing, so pardon me what might be some really beginners' questions.
After reading the tutorial on how to use top level containers, I tried the following code inside the actionPerformed event in a button:
private void colgarActionListener(java.awt.event.ActionEvent evt) {
auxButton = new JButton();
auxButton.setSize(100,30);
auxButton.setText("Me button");
getContentPane().add(auxButton);
getContentPane().doLayout();
}
As you expected, it occurs that it does not work. The button just does not appear. If I try a ridiculous thing such as:
getContentPane().setBackground(Color.red);
instead of
getContentPane().doLayout();
it works. What am I doing wrong?
And the last one: if I write a class which works as a custom ActionListener (with its constructor with parameters), where should I put it? As a private class inside the GUI code? It just feels so dirty... Or as a public class inside another package. maybe?
Thank you very much.
Regards.
MartÃn.
You will want to read up on how the layout managers work and how to use them for that is one of the keys to using Swing (not JSwing by the way). The Layout Manager Tutorial is a great place to start.
For one, avoid using null layout and setBounds(...) For another, contentPane's usually use BorderLayout. Also, I've never seen doLayout() used before in this way. Instead I've usually seen validate() or revalidate() followed by repaint() called on the container after changing its components.
Yes, an ActionListener is typically implemented as not only a private class, but an anonymous class, exactly at the use site. Anonymous class is when you write
x.addActionListener(new ActionListener() { public void actionPerformed(Event e) {
... stuff to do ...
}});
I am trying to program a java application that consists of several windows using JFrame.
Each JFrame contains a JTextField and buton to go to the next JFrame.
I need to retrieve all the information entered by the user at the end.
I created an event click on the buton to save to a public class all the data that the user introduce in the JTextField. I named that public class myData, which has a static attributes.
The problem is that I can not access this class from the button listener function.
I get an error: cannot refer to an non final variable inside an inner class defined in a different method.
My goal is to be able to share the class myData between different methods of a different class.
E.g. I have a class named myClass1 and myClass2, so I want to share the myData attribute between myClass1 methods and myClass2 methods.
Please anyone can someone help me? or propose another way to do this!
Thanks in advance !
All of the calls about MVC etc. are valid, but this isn't that hard.
What you want to do is in your Main, you can create your Data (Model) class, the class that holds all of your information.
So, you can do something like this:
public class F1 ... {
private final Data myData;
public F1(Data theData) {
myData = theData;
}
....
}
public class Main {
Data myData;
public static void main(String args[]) {
Main m = new Main();
m.setMyData(new Data());
F1 f = new F1(m.getMyData());
...
}
}
Then, later, when F1 calls F2, simply do the same thing -- create F2 with the Data passed in earlier by the constructor. That way, as each Frame runs its course, they're all working on the same instance of Data. When all is done, the single instance of Data is left within the Main class for you to do with what you will.
There are better ways to reorganize your entire program, but this should give you ideas on how to get over the hump you're having right now.
Addenda:
There are several things you can do.
When your get the ActionEvent, it contains a source. That source is the component that generated the event (most likely a Button in this case). If you know where the button is located in the hierarchy of things, you get to your Frame directly. In the pastebin example, you have Frame -> Panel -> Button. So, if you have the Button, you cat get to the Frame.
public void actionPerformed(ActionEvent e) {
JButton sourceButton = (JButton)e.getSource();
F1 f1 = (F1)sourceButton.getParent().getParent();
Data myData = f1.getMyData();
data.setField(...);
}
Again, this is not the recommended ways of doing things. The tutorials have decent examples of using MVC and property change listeners and the whole kit. But this should get you to where you want to go.
Sorry, but your design needs alot of work. I'm going to recommend you read up on MVC. it may seem like alot to chew on right now but it will help you immensely in the long run. On a side note, dont nest your data class definition(s), and remember to always distinguish between classes and objects.
Your overall design of swapping JFrame's seems a bit iffy to me. Why not instead use either dialogs such as a JDialog or JOptionPane or even better a CardLayout to swap views. Also I urge you not to use static fields for any of this as this can cause significant problems in the future and makes your code less compliant with good object oriented principles. With regards to information sharing, about all I can say is that it's all about one class having the proper reference to the other class. For more specific advice you'll likely need to show us more information and code.
Edit
Also, you know of course that you can get a reference to the JButton that stimulated the ActionListener by calling getSource() on the ActionEvent object passed into the actionPerformed method. This may allow you to get a reference to the class that holds the JButton if necessary.