I'm trying to fill the entire canvas with fillRect. Whenever I include super.paint() in the beginning of the overridden paint method, super.paint() sometimes gets called after the fillRect, causing unpredictable behavior on whether super.paint() gets drawn first or drawRect.
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
public class DebugJFrameRace extends JFrame {
public DebugJFrameRace () {
super("Debug race");
this.setVisible(true);
this.setSize(600, 600);
this.repaint();
}
public void paint(Graphics g) {
super.paint(g);
// clear background
g.setColor(Color.black);
g.fillRect(0, 0, 600, 600);
System.out.println("Finished");
}
public static void main(String[] args) {
DebugJFrameRace app = new DebugJFrameRace ();
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I don't have problems with the posted code.
I'm not really sure what it is designed to demonstrate. A black background is always painted.
However, the posted code does not follow Swing guidelines:
All Swing components should be created and updated on the Event Dispach Thread (EDT). This is done by using SwingUtilities.invokeLater(...). Read the section from the Swing tutorial on Concurrency for more information and examples on how to better structure your code. Not executing the code on the EDT can cause random problems.
You should not override paint() in a JFrame. Custom painting is done by overriding paintComponent(...) on a JPanel and then you add the panel to the frame. Read the section from the Swing tutorial on Custom Painting for more information and working examples.
Related
I'm attempting to code a simple animation or physics example in a Java Swing application. I have the actual windows application open and working, but I can't figure out how to actually draw my shapes, and how I'd format the code for calculations between frames, that sort of stuff.
I've read some stuff about over riding a paint method, but I don't know what that means, and I don't believe I'm using it in the code I'm using right now. This is my code:
public class Physics extends JFrame{
public Physics() {
initUI();
}
private void initUI() {
JPanel panel = new JPanel();
getContentPane().add(panel);
panel.setLayout(null);
final JLabel label = new JLabel("Hi, press the button to do something");
label.setBounds(20, 0, 2000, 60);
final JButton submitButton = new JButton("Start");
submitButton.setBounds(20, 150, 80, 20);
submitButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//Put button code here later
}
});
panel.add(label);
panel.add(submitButton);
setTitle("Program");
setSize(300, 250);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Physics ex = new Physics();
ex.setVisible(true);
}
});
}
}
So I have some blank space above my button where I'd like to draw maybe a square or circle moving across the screen to start off with, once I get that down I can start getting into the more advanced stuff. Any hints on how to do that would be appriciated :D
Thanks!
"I've read some stuff about over riding a paint method, but I don't know what that means"
So you've overridden actionPerformed, so you know what an #Override is. As you'll notice from the ActionListener, you never actually explicitly call actionPerformed, but whatever you put in the there, still get's used. That's because the ActionListener implicitly call it for you.
The same is true with painting. In the Swing painting process, there is a paint chain that Swing uses to paint components. Along the way paint is called somewhere. So just like actionPerformed, you can override paint and it will get implicitly called for you.
#Override
public void paint(Graphics g) {
super.paint(g);
}
The Graphics object passed to the method is the graphics context that Swing will use for the painting. You can look at the Graphics API to see the methods you can use. You can use drawOval to draw a circle
#Override
public void paint(Graphics g) {
super.paint(g);
g.drawOval(x, y, width, height);
}
Now here's the thing. You don't actually want to override paint. In the tutorials linked above, some of the examples will use applets and override paint, but you shouldn'y paint on top level containers like JFrame or JApplet. Instead paint on a JPanel or JComponent and just add it the JFrame. When you do paint on JPanel or JComponent, you'll instead override paintComponent (which also gets called along the paint chain), instead of paint
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(x, y, width, height);
}
You see how I used variables for the drawOval method. The x is the x location from the top-let of the screen, and y and the y point. width and height are width and height of the circle. The great thing about using variables is that their values can be changed at runtime.
That's where the animation comes to play. As pointed out, you an use a javax.swing.Timer
The basic construct is
public Timer(int delay, ActionListener listener) {
}
The delay is the milliseconds to delay each call to the listener. The listener will have your actionPerformed call back that will do what's inside, every delay milliseconds. So what you can do, is just change the x from the drawOval and repaint(), and it will animate. Something like
Timer timer = new Timer(40, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
x += 5;
repaint();
}
});
timer.start();
The timer code you can just put in the constructor. That's probably simplest explanation I can give. Hope it helps.
Don't forget the to see Custom Painting and Grapics2D for more advance topics on graphics. Also see some example of timers and animation here and here and here and here and here
Also avoid using null layouts. See Laying out Components Within a Container to learn how to use layout managers, as should be done with Swing apps.
Take a look at the Swing tutorial on Custom Painting.
The example shows you how to do painting. If you want animation, then you would use a Swing Timer to schedule the animation. The tutorial also has a section on How to use a Swing Timer.
Put the two tutorial together and you have a solution.
There are any number of ways to achieve this.
Start by taking a look at:
Performing Custom Painting
2D Graphics
For details about how painting in Swing is done.
Animation is not as simple as just pausing a small period of time and then repainting, theres acceleration and deceleration and other concepts that need to be considered.
While you could write your own, that's not a small task, a better solution might be to use a pre-existing engine, for example...
Then take a look at:
Timing Framework
Trident
java-universal-tween-engine
Which are all examples of animation engines in Swing. While I prefer the Timing Framework as it provides me with a lower level API, this is a personal opinion. Both Trident and the JUWE seem to be geared more towards component/property based animation (which the Timing Framework can do if you want to build some of the feature sets up)
I created a simple animation with two rockets blasting off. The full eclipse project is here: https://github.com/CoachEd/JavaExamples/tree/master/RaceToSpace. Here's a screenshot:
I am trying to call the paint() method from another class, but it just doesn't work.
Here is the code:
Main.java:
public class Main extends JFrame {
private static final long serialVersionUID = 1L;
private int WIDTH = 600;
private int HEIGHT = 400;
private String NAME = "Dark Ages";
private String VERSION = "0.0.1 Pre-Alpha";
static boolean running = false;
private Image dbImage;
private Graphics dbg;
public Main() {
//Initialize window
JFrame frame = new JFrame();
frame.setTitle(NAME + " - " + VERSION);
frame.setSize(WIDTH, HEIGHT);
frame.setVisible(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
//Running
running = true;
}
public void paint(Graphics g) {
dbImage = createImage(getWidth(), getHeight());
dbg = dbImage.getGraphics();
paintComponent(dbg);
g.drawImage(dbImage, 0, 0, this);
}
public void paintComponent(Graphics g) {
// Draw Images
repaint();
}
public static void main(String args[]) {
new Main();
Player player = new Player();
}
}
Player.java:
public class Player {
public void paint(Graphics g) {
g.drawRect(100, 100, 100, 100);
}
}
How do I call the paint() method from another class in java?
In brief, you don't. And in fact, you shouldn't call it directly from the same class either. Instead you should change the state of the class (change its fields), and then call repaint() on it, which will suggest to the JVM that the component should be painted.
Other issues and suggestions:
An obvious issue I see immediately in your code is that your class has a Graphics field, something that is dangerous to do. I suggest that you get rid of that field so that you're not tempted to use it.
Do not draw directly within a JFrame, JDialog or other top-level window. These windows have too many roles to play behind the scenes that you don't really want to mess with how they render themselves or their children (unless you really know what you're doing and have definite need).
Better to draw in the paintComponent(...) method override of a JPanel or other JComponents.
In your paintComponent(...) override, don't forget to call the super's method: super.paintComponent(g);. This will allow Swing to do housekeeping drawing, such as erasing old out of date or deleted images, before you do your drawing.
Run, don't walk to your nearest Swing painting tutorial.
Your current Player class extends no Swing component and is added to no top level window, so its code will do nothing useful. Again, read the tutorials.
Never call repaint() from within paint(...) or paintComponent(...).
Please post modified code if possible.: please don't ask us to create code for you as that's your job. Please understand that the more code you create, the better a coder you'll be, and because of this, most of us don't feel that we should cheat you out of the opportunity to create your own code.
Useful Java Tutorials:
The Really Big Index: The main tutorial where you should start.
Using Swing Components: How to create Swing GUI's
Lesson: Performing Custom Painting: Introductory tutorial to Swing graphics
Painting in AWT and Swing: Advanced tutorial on Swing graphics
I have been playing around with Java's 2d painting tools and have hit a snag. I am attempting to move the objects. Here is the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Test extends JPanel{
private int[] location = new int[2];
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.fillArc(location[0], location[1], 100, 100, 45, 90);
g.setColor(Color.black);
g.fillArc((location[0]+50-10),(location[1]+50-10), 20, 20, 0, 360);
new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setLocation((location[0]+50),50);
repaint();
System.out.println("repainting");
}
}).start();
}
public void setLocation(int x, int y){
this.location[0] = x;
this.location[1] = y;
}
public static void main(String[] args){
JFrame jf=new JFrame();
jf.setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE);
jf.setPreferredSize(new Dimension(300,500));
jf.setLocation(100,100);
jf.add(new Test());
jf.pack();
jf.setVisible(true);
}
}
This only paints one of the two objects to the screen... it seems to be the second one as when I change the parameters of setLocation on [1] the one object it does paint moves. Any thoughts? Thanks
Edit: Edited above code to reflect what was said below.
You are adding two components to the JFrame in a default way. This will add the components BorderLayout.CENTER and so the second component will cover and obscure the first. You will want to read up on layout managers to fix this. Also read up on Swing Timers for simple animations, since your code, even if written correctly would do no animation.
If you want to move the drawing, then
Use only one Test JPanel
Override JPanel's paintComponent(...) method, not paint(...) method.
call the super.paintComponent(g) method first thing in your paintComponent method override.
Give the Test JPanel public methods to allow outside classes to change the location without having them directly futz with the field. Make the location field (name should begin with a lower-case letter) private just to be safe.
Use a Swing Timer to periodically call this method and change location, then call repaint() on the JPanel.
I am trying to draw an incredibly basic shape using swing in Java, however for some reason it does not seem to be working. This is code that I downloaded from my lecturer that he showed us in a lecture, but when I run it the window opens but nothing is drawn and I have no idea why.
package graphicsEx;
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class Lecture1Example extends JPanel{
// This is where the JPanel gets (re-)painted when the screen is refreshed.
public void paintComponent(Graphics g) {
// Cast to Graphics2D for more features.
Graphics2D g2D = (Graphics2D) g;
Rectangle2D rect = new Rectangle2D.Double(20,30,40,50);
g2D.setColor(Color.red);
g2D.draw(rect);
g2D.fill(rect);
}
public static void main(String args[]) {
JFrame frame = new JFrame("Playing with Graphics");
frame.setSize(500, 400);
frame.setVisible(true);
frame.setContentPane(new Lecture1Example());
}
}
I am using the Eclipse IDE.
Dear user1821475's lecturer:
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
"Subclasses of Swing components which have a UI delegate (vs. direct subclasses of JComponent), should invoke super.paintComponent() within their paintComponent override.
"As a convenience add and its variants, remove and setLayout have been overridden to forward to the contentPane as necessary."
The outermost Container should be setVisible() only after invoking pack() and other methods affecting geometry.
I have a JFrame on which I am using overriding the paint method to display some graphics. I also want to add a JPanel to it that will show up on top of the graphics. Right now, all I see is the graphics created from JFrame paint method.
Here's my JPanel:
public class NoteDraw extends JPanel {
public NoteDraw() {
setSize(200, 100);
setBackground(Color.BLACK);
}
#Override public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(0, 0, 100, 100);
}
}
Here's my JFrame:
public class ui extends JFrame {
public void paint(Graphics g) {
//do some drawing here...
}
}
Here's my main:
public class Main {
static ui main_gui = new ui();
public static void main(String[] args) {
NoteDraw note = new NoteDraw();
main_gui.getContentPane().add(note);
main_gui.setVisible(true);
}
}
You should NEVER override the paint() method of a JFrame (unless you really know what you are doing). This method is responsible for all the optimized painting behaviour of Swing.
Custom painting should always be done by overriding the paintComponent() method of a Swing component like you have done on your NoteDraw panel.
The reason the code in the paint method doesn't show is because the NoteDraw panel is opaque and therefore the panel paints over top of the Graphics code in your paint method.
So the solution is to move the Graphics painting code to the NoteDraw panel.
Or if you are trying to create some kind of background image for your frame then you can try using the Background Panel.
Or if you truly do need custom painting then you create a background panel and override the paintComponent() method. Then you set the layout to a BorderLayout and add this panel to the frame. Then you make your NoteDraw panel non-opaque and add it to the custom background panel. Now the background will show through on the NoteDraw panel.
Remember to call super.paint() when you override your paint() method.
This way, you still use the behavior defined in the parent class, and you can add your modifications safely.
Resources :
sun.com - Using the Keyword super