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 ...
}});
Related
I'm new to GUI's so sorry if the error is really blatant.
I'm trying to create a simple window with a couple of buttons, but every time I run the code, it opens four windows instead of just one. Any help would be appreciated.
public class CISUC extends JFrame implements Serializable {
//interface
JFrame mainFrame;
JPanel mainPanel;
JButton createProject, manageProject,listActive, listUnfinished,listaFinished;
public CISUC(){
//interface
mainFrame = new JFrame();
mainFrame.setResizable(false);
mainPanel = new JPanel();
mainPanel.setLayout(new GridLayout(2,3));
createProject= new JButton("Create Project");
mainPanel.add(createProject);
manageProject = new JButton("Manage Project");
mainPanel.add(manageProject);
listActive = new JButton("List Active Projects");
mainPanel.add(listActive);
listUnfinished = new JButton("List Unfinished Projects");
mainPanel.add(listUnfinished);
listFinished = new JButton("Listar Finished");
mainPanel.add(listFinished);
mainFrame.setSize(800, 500);
mainFrame.add(mainPanel);
mainFrame.setVisible(true);
}
public static void main(String[] args) {
CISUC cisuc = new CISUC();
}
}
As ohers already said to you, there is nothing wrong with the number of Frames opened, i just tried the code and it opens a single JFrame.
In my opinion there are a few mistakes in your code: you don't need to extend JFrame and implement serializable for this purpose... this way you are making your application heavier because of unused inherithed (unused) fields and methods from parent class. Moreover (as others already told you), there is no close operation linked to your JFrame, so when you press the X button your application just keep running until you shut down your computer.
To avoid this jut add the following statement:
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
I suggest you to put main method, main Object, and JPanel in separate
classes, making your application easier to read, debug, and maintain. If you are planning to have many funcionalities it can be a good idea to have a custom class extending JFrame, where you can assemble different custom GUI objects, each one extending a Swing/AWT component.. and finally call them in you main object or directly in main class! There is no limit to this, but use inheritance wisely: many classes mean much more boilerplate code... there is always a tradeoff!
Another great idea should be to put graphics initialization (size,
colours, etc...) in a separate method outside from constructor. Just
create something like `private void initializeGraphics(){...}
Other to this, you can do the same when mapping actionListeners to
buttons, putting them in separate named classes or at least in a
separate method like private void addListeners(){...}, and just
call this at the end of your constructor code.
I'm telling you this because when using Swing it's very easy to have something like 5000 lines of unreadable and unmaintainable code if you start adding everyting in the same place!
Good luck with your application :)
PS: take a look to MigLayout... i think you are going to like it, even it could seem a bit complicated at the beginning.
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.
Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 11 years ago.
Improve this question
As a contrast to this wiki, I am looking for the proper way to implement Swing GUI controls from a coding standpoint.
I have been on a quest to learn Java and its GUI tools but I find internet tutorial after internet tutorial that throws everything in main and I know this isn't right.
I've also tried RAD systems like Netbeans and other "visual" editors but by the time I get to coding I've got a heap of code that I don't know half of what it does, so I'm intent on learning to hand code swing, and I know the basic controls and layout, but want to do it the right way.
Is there a model or standard I'm missing?
example questions...
do I extend JFrame and create my own frame object? (I would assume yes)
do I encapsulate the main menu inside that frame object? or do I create its own? etc...
How to I separate "View" logic from "Application" logic?
Basically, I'm looking for what the industry standard is, on how to organize GUI code.
Since there seems to be some argument about what constitutes "best practices", I'll give you what I have found works best for me, and my reasoning:
1.
Each window should extend either JFrame or JDialog (depending on the type of window). This makes it easy to control the properties of the window without specifying a specific object every time. This is more of the general case, though, as I have been known to do it both ways.
2.
The main() method should be in a separate class. This increases the likelihood of being able to use your window classes elsewhere, as they are not tied to specific implementations. Technically it doesn't make a difference, but application startup code just doesn't belong in a window.
3.
Listeners should be in anonymous inner classes. Your top-level class should not implement any listeners. This prevents hacks like calling the listener methods from anywhere except the object to which they are attached.
Here is a simple application with a single frame to demonstrate these practices:
public class Main {
public static void main(String[] args) {
final String text = args[0];
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
final MyWindow wnd = new MyWindow(text);
wnd.setVisible(true);
}
});
}
}
public class MyWindow extends JFrame {
public MyWindow(String text) {
super("My Window");
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
MyWindow.this.setVisible(false);
MyWindow.this.dispose();
}
});
final JButton btn = new JButton(text);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(MyWindow.this, "Button Pressed", "Hey", JOptionPane.INFORMATION_MESSAGE);
}
});
setLayout(new FlowLayout());
add(btn);
pack();
}
}
I agree with all of Jonathan's points.
Each window should extend either JFrame or JDialog...
The main() method should be in a separate class...
Listeners should be in anonymous inner classes...
I would also like to add the following:
Use GridBagLayout (GBL) judiciously. GBL is a powerful Layout Manager, difficult to learn, but quite powerful.
Consider hand coding all your UI. I'm personally not a fan of the code that is produced by visual editors. But, with that said I have not used a visual editor in several years (2000ish). They might be better at this point.
Use JPanels judiciously. Look at your ui and determine which components should behave the same as the screen size changes and then group those components together on a JPanel. Consider using JPanels inside of JPanels to get your correct resizing behavior.
I normally take a slightly different approach on having my components handle events then Jonathan does, but I believe his approach is a bit cleaner then mine.
Also, really study the use of MVC and Layered Architecture. It is truly best not to be mixing UI and Business Logic together.
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.
Is there a way (e.g., via an event?) to determine when a Swing component becomes 'displayable' -- as per the Javadocs for Component.getGraphics?
The reason I'm trying to do this is so that I can then call getGraphics(), and pass that to my 'rendering strategy' for the component.
I've tried adding a ComponentListener, but componentShown doesn't seem to get called. Is there anything else I can try?
Thanks.
And additionally, is it OK to keep hold of the Graphics object I receive? Or is there potential for a new one to be created later in the lifetime of the Component? (e.g., after it is resized/hidden?)
Add a HierarchyListener
public class MyShowingListener {
private JComponent component;
public MyShowingListener(JComponent jc) { component=jc; }
public void hierarchyChanged(HierarchyEvent e) {
if((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED)>0 && component.isShowing()) {
System.out.println("Showing");
}
}
}
JTable t = new JTable(...);
t.addHierarchyListener(new MyShowingListener(t));
You can listen for a resize event. When a component is first displayed, it is resized from 0,0 to whatever the layout manager determines (if it has one).
You need to check up the component hierarchy. Check after AncestorListener.ancestorAdded is called.
I've always used Coomponent.addNotify to know when the component is ready to be rendered.Not sure if is the the best way, but it works for me. Of course you must subclass the component.
Component.isDisplayable should be the right answer but I know it didn't worked for me as I thought it will(I don't remember why, but there was something and I switched to addNotify).
Looking in the SUN's source code, I can see addNotify fires a HierarchyEvent.SHOWING_CHANGED so this is the best way to be notified.