I've got a beginner question here that I was hoping someone with some Java experience could help me with. Currently taking an intro to OOP course focused on Java. My instructor is currently covering awt and swing, specifically the need to override the paint method so that graphics are redrawn when a window is resized, etc. I like to do as much outside reading as possible, and my concern is that the examples that my professor gives involve things that I've read are not best practices. To get to the point...
I understand that it's necessary to override the paint method, but I don't know the best way to do so. My professor's examples are all similar to the following:
class Example extends JFrame {
public void paint(Graphics g) {
super.paint(g);
g.drawString("Blah, blah");
}
public static void main(String[] args) {
Example a = new Example();
a.setDefaultCl...
\\Etc...
}
}
This bothers me because it doesn't seem right to include everything for the GUI in the same class as my main method. Also, I read on a different thread here that you shouldn't extend JFrame, but there wasn't an explanation as to why. My solution was to create a class handling the gui and instantiate JFrame within the constructor. However, unless I'm mistaken, doing so won't let me override the paint method. I feel compelled to extend JFrame to allow me to override paint, but again I read that was the wrong thing to do.
Any help would be sincerely appreciated, I know I can just model my code off of what he has but I really want to understand this and know the best way to handle it.
I understand that it's necessary to override the paint method
No you should not override the paint() method.
You should override the paintComponent() method of a JPanel and then add the panel to the frame.
Read the section from the Swing tutorial on Custom Painting for more information and working examples.
The tutorial will also show you how to better structure your code so that the GUI is created on the Event Dispatch Thread (EDT). The tutorial also has a section on Concurrency which will explain why this is important.
Related
I cannot seem to force a layout in Swing. I have a JComponent added to a JLayeredPane and I set a border on the JComponent. Then, I want to immediately re-draw everything - not in the sense of "please do this asap" like invalidate(), but synchronously and immediately. Any help? I cannot seem to find the right method of doing this, and all my reading about invalidate(), validate(), repaint(), doLayout(), etc is just confusing me more!
According to this (see the section titled "Synchronous Painting") the paintImmediately() method should work.
The most reliable way to get Swing to update a display is to use SwingUtilities.invokeLater. In your case, it would look something like
SwingUtilities.invokeLater(new Runnable {
public void run() {
somecomponent.repaint();
}
});
I realize the 'invokelater' does not exactly sound like it does anything immediate, but in practice, events posted in this way tend execute pretty quickly compared to just calling e.g. somecomponent.repaint() directly. If you absolutely must make your control code wait for the GUI to update, then there is also invokeAndWait, but my experience is that this is rarely necessary.
See also: document on event dispatch in Swing.
This is super old, but I found a simple solution, which I'll show with a JPasswordField:
var pw = new JPasswordField();
...
pw.paint(pw.getGraphics()); // paints immediately
When adding new class extending JFrame (or java.awt.Frame) the class is added with main() method inside like this:
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewFrame5().setVisible(true);
}
});
}
Every JFrame class has its own main method and I guess all classes starts simultaneously.
How do I add frames without main methods?
Firstly, see The Use of Multiple JFrames, Good/Bad Practice?. Generally, recommended not to do it. See accepted answer for other possibilities (for example a JDialog)
As for your main concern, there's no way around netbeans creating the main method for top level containers like JFrame and JDialog. The logic seems right in the case of JFrame, as an application should only have one JFrame as the main top-level container for the application, but I'm not sure the logic behind the JDialog having a main method (as the dialog is usually run in the same JVM as the main JFrame). The only thing I can think is that the JDialog is created with a main for development purpopsed, if you want to test the dialog in stand-alone mode. But ultimately, you should delete the main method of the JDialog should you choose to use one.
Going back to the first point about multiple JFrames, other options I might recommend
Use a JDialog. Yes you will have to delete the main method, when going into production, as the dialog will be instantiated within the context of the main JVM and generally shouldn't run its own process.
Another option, depending on your requirements is to use a Cardlayout which will let you switch between views/panels (You can create JPanel forms in netbeans). See How to Use CardLayout in Netbeans GUI Builder for a working guide. And the official How to use CardLayout tutorial
An aside, if you are a beginner, I strongly suggest you put aside the builder tool and learn to hand code first. There may be many tutorials teaching you how to use the builder tool, but they may miss out on important concept in the swing architecture and swing in general. IMO this will greatly affect your understanding of how and why things work with the builder, causing a lot a headache when trying to debug. Keep Creating a GUI With JFC/Swing, the official tutorial handy and go through it.
When making an application using Swing, I've seen people do one of the two things to create a JFrame. Which is a better approach and why?
I'm a beginner at Java and programming. My only source of learning is books, YouTube and Stack Overflow.
import {imports};
public class GuiApp1 {
public static void main(String[] args) {
new GuiApp1();
}
public GuiApp1() {
JFrame guiFrame = new JFrame();
guiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
guiFrame.setTitle("Example GUI");
guiFrame.setSize(300,250);
................
}
AND
import {imports};
public class GuiApp1 extends JFrame {
public Execute() {
getContentPane().setBackground(Color.WHITE);
getContentPane().setLayout(null);
setSize(800, 600);
.............
}
public static void main(String[] args) {
Execute frame1 = new Execute();
frame1.setVisible(true);
}
}
Thoughts:
Avoid extending JFrame as it ties your GUI to being, well a JFrame. If instead you concentrate on creating JPanels instead, then you have the freedom to use these JPanels anywhere needed -- in a JFrame, or JDialog, or JApplet, or inside of another JPanel, or swapped with other JPanels via a CardLayout.
Avoid inheritance in general, especially of complex classes. This will prevent pernicious errors, such as inadvertent method overrides (try creating a JFrame or JPanel that has a getX() and getY() method to see what I mean!).
Avoid inheritance of complex classes if you are using an IDE: If you override a complex class, when you call methods on objects of these classes, you will have many, too many, choices of methods offered to you.
Encapsulation is good, is and allows for creation of safer code. Expose only that which needs to be exposed, and control that exposure as much as possible.
Prefer composition over inheritance.
The 2nd example uses inheritance, but for no good reason, since it does not change the functionality of JFrame.
As an aside, if those are examples of code you are seeing, find a new source1 supplementary. Even in the few code lines shown, each does highly questionable things. E.G.
Neither GUI is created on the Event Dispatch Thread.
getContentPane().setBackground(Color.WHITE);
getContentPane().setLayout(null);
setSize(800, 600);
The first part of the 1st line (getContentPane()) has not been necessary since Java 1.5
The second line uses a null layout, which will break in more ways I can count or describe.
The third line should best be replaced with pack();
JFrame guiFrame = new JFrame();
guiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
guiFrame.setTitle("Example GUI");
guiFrame.setSize(300,250);
The first and 3rd lines could be contracted to: JFrame guiFrame = new JFrame("Example GUI");
The 2nd line is better set to guiFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
The 3rd line again sets a size to the frame.
Supplement
Having mentioned you do search SO, here is a tip. Check out the posts of the top 15 providers of answers in the Swing top users. Whatever advice/code you glean from these people, would commit few if any of the mistakes in those code samples. Some don't often (or ever) provide self contained examples like a few of us commonly do (and don't look to those examples necessarily for OO design as opposed to just technique), but whatever code they provide, or advice they give, should be highly considered.
Personally, the first approach (creating an instance of JFrame) is preferred, I prefer this because...
It doesn't lock your application into a dedicated container...you see a lot of people wanting to add applets to frames and frames to applets, if they had simple put the majority of there GUI in a JPanel to start with, they wouldn't have these issues.
It also means that the UI you create is much more flexible. For example, you can re-use it, either in the current application or future applications, you don't lock yourself in.
The main gripe I have with extending JFrame is, you're not actually adding any new features or functionality to it, which could be effectively re-used beyond using setVisible
The other issue I have with extending JFrame is people then promptly override paint, which is really, really bad. There are so many issues with doing this it's simply painful to have to repeatedly list them...
So...for more 2 cents worth. Create an instance of JFrame and add your content to it. If required, create a static method call showMyAwesomeGUI which does it for you...
The first approach is better.
Typically you are not adding any new functionality to the frame so creating a direct instance of the class makes sense.
Go for the first approach.
Because with that you can have more frames to be created. Because the application can have more than one window. As in the second case you can't create more frames.
It does not matter.
There are reasons why you might do one or the other, but absent any of those reasons it makes no difference whatsoever.
Now, if you were writing something that might operate from the command line or might be a GUI program, obviously you could want a 'main' class that was not a GUI class.
If you worked in a programming shop where one or the other was the standard, by all means follow the standard. There is no right answer to this one, and in fact very little to choose between them.
i am working on an applet with around ten different datasources(e.g. statistics/error-log/...). Each datasource is updated by a single network connection and reports updates via the observer mechanism. The applet has different views which display parts of the data. Every view is only interested in some parts of the data and registers itself as an Observer at the necessary Observables.
The views(extended JPanels) mostly consist of standard swing components (e.g. JLabels, JButton, ...). Some attributes of the components in the views depend on information from the underlying data model.
Example:
StatisticPanel::paintComponent(Graphics g) {
clearStatisticButton.setEnabled(stat.hasEntries());
minValueLabel.setText(stat.getMinValue());
super.paintComponent(g);
}
This logic is implemented in the paintComponent() method of the StatisticPanel and the update() methods just calls repaint(), because I didn't want the manipulate the components outside of the EDT.
Is this the intended way of updating swing components in a multi-threaded environment? Is it better to use a Runnable with SwingUtitlies.invokeLater()? Are there better approaches for this problem?
I second camickr's recommendations, but regarding this code snippet:
StatisticPanel::paintComponent(Graphics g) {
clearStatisticButton.setEnabled(stat.hasEntries());
minValueLabel.setText(stat.getMinValue());
super.paintComponent(g);
}
You have non-painting methods in your paintComponent method (the first two methods), and that shouldn't be as 1) you want this method to be as lean and fast as possible and thus have only painting-related code, and 2) you do not have aboslute control of when this method is called or even if it is called, and so non-painting related code and program logic does not belong in there. For these reasons, I strongly urge you to get them out of there, but instead should be called separate from paintComponent, but as with most Swing code, on the EDT.
EDIT 1
I'm not a professional, but how about if you gave your StaticPanel a method similar to this:
public void doMyUpdate() {
if (SwingUtilities.isEventDispatchThread()) {
clearStatisticButton.setEnabled(stat.hasEntries());
minValueLabel.setText(stat.getMinValue());
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
clearStatisticButton.setEnabled(stat.hasEntries());
minValueLabel.setText(stat.getMinValue());
}
});
}
repaint(); // if the paintComponent method has more than just a super call.
}
EDIT 2
Also, please have a look at this thread: check-if-thread-is-edt-is-necessary
repaint() is used to invoke the Swing RepaintManger which in turn will schedule the repainting of the component, so yes it is ok to just invoke repaint directly. The RepaintManager will make sure all repainting is done on the EDT.
Consider this code:
public class StateChartPanel extends JPanel {
private LightContext LC;
public StateChartPanel(LightContext lc){
LC=lc;
}
public void paintComponent( Graphics G ){
super.paintComponent( G );
LC.DrawStateChart((Graphics2D)G);
}
}
StateChartPanel is a panel to draw something (a state chart). It sends its Graphics object to LC which use it to draw shapes but whenever it draws something the PaintComponent event of StateChartPanel happens again and it causes my application to hang.
I think what's likely happening is an infinite loop: StateChartPanel.paintComponent is calling LC.DrawStateChart, which is then calling StateChartPanel.paintComponent. You probably have a StateChartPanel as a subcomponent of LC somehwere, and LC.DrawStateChart is calling its own paint() function. Try removing StateChartPanel.paintComponent's call to LC.DrawStateChart and see if that works.
Please learn proper Java Naming conventions. All conventions are typically followed in text books, tutorials or code we post in the forums. So follow them and don't make up your own:
a) variable names should not start with an upper case character
b) method names should not start with an upper case character
If you are looping then its probably because you invoke repaint() somewhere in your invisible code that you didn't post.
If you need more help post your SSCCE that shows the problem.