why is my java.awt.Window not repainting after I called the repaint() method?
public class Counter extends Window implements ActionListener {
private static final long serialVersionUID = 1L;
private Timer timer;
private int time;
public Counter() {
super(null);
setAlwaysOnTop(true);
setBounds(getGraphicsConfiguration().getBounds());
setBackground(new Color(0, true));
setVisible(true);
timer = new Timer(1000, this);
timer.start();
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.clearRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
g.drawString(String.valueOf(time), getWidth()/2, getHeight()/2);
}
#Override
public void update(Graphics g) {
super.update(g);
}
#Override
public void actionPerformed(ActionEvent e) {
time++;
repaint();
}
As you can see i created a timer with a delay of 1 second. After that i call repaint() to draw the counter's number on the screen. But it only draws a zero on my screen and stops drawing then (the zero stays on screen). First i thought that the paint method is only called once, but i tested a System.out.prinln() and prooved that the paint method is executed every second so it should actually repaint the window... So i don't know where i made a mistake.
And yes it is my intention to use the awt.Window and not a JFrame or Frame etc..
I finally got to run the code on Windows 7, and I could replicate the issue. For some reason, paint isn't being called; why, I don't know. Because I wouldn't do it this way, I've never had that issue.
Instead, I'd start by having the counter class extend from something JPanel or JComponent (just remember to make JPanel transparent) and then add it to a JWindow, something like this:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.Timer;
public class Counter extends JPanel implements ActionListener {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JWindow window = new JWindow();
window.add(new Counter());
window.pack();
window.setLocationRelativeTo(null);
window.setBackground(new Color(0, 0, 0, 0));
window.setVisible(true);
}
});
}
private static final long serialVersionUID = 1L;
private Timer timer;
private int time;
public Counter() {
setOpaque(false);
timer = new Timer(1000, this);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
System.out.println(time);
g.clearRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
g.drawString(String.valueOf(time), getWidth() / 2, getHeight() / 2);
}
#Override
public void actionPerformed(ActionEvent e) {
time++;
System.out.println("..." + time);
repaint();
}
}
Related
I am attempting to have a circle appear on the screen and follow the mouse around. (Eventually I'm going to turn it into a game with ray casting) I am using a MouseMotionListener and am trying to use the mouseMoved method to get accurate mouse location within my JPanel. The problem is though that the farther I move my mouse down the screen, the less accurate it becomes. By the time my mouse gets to the bottom, it is drawing circles about 20 pixels above. It is not a lagging thing because it never catches up, it is always a few pixels above where it should be.
I've tried using different methods that call from MouseEvents and have tried using MousePointerInfo but none work correctly. It does seem to work when I have the JFrame set to undecorated, but obviously that does not look good for a program and thus I want to avoid that.
public class Driver {
public static void main(String[] args) {
JFrame frame = new JFrame("Moonlight");
frame.setSize(700, 700);
frame.setLocation(350, 50);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new MoonlightPanel());
frame.setVisible(true);
}
}
public class Panel extends JPanel {
private BufferedImage myImage;
private Graphics myBuffer;
private Timer t;
public Panel () {
myImage = new BufferedImage(700, 700, BufferedImage.TYPE_INT_RGB);
myBuffer = myImage.getGraphics();
t = new Timer(0, new Listener());
t.start();
addMouseMotionListener(new Mouse());
}
private class Listener implements ActionListener {
public void actionPerformed(ActionEvent e) {
drawBackground();
/*try {
Point pos = getMousePosition();
myBuffer.setColor(Color.WHITE);
myBuffer.fillOval(pos.x - 10, pos.y - 10, 20, 20);
}
catch(NullPointerException en) {}*/
repaint();
}
}
private class Mouse implements MouseMotionListener {
public void mouseMoved(MouseEvent e) {
Point pos = new Point(e.getX(), e.getY());
System.out.println(pos);
myBuffer.setColor(Color.BLUE);
myBuffer.fillOval(pos.x - 10, pos.y - 10, 20, 20);
}
public void mouseDragged(MouseEvent e) {}
}
public void drawBackground() {
setBackground(Color.BLACK);
}
public void paintComponent(Graphics g) {
g.drawImage(myImage, 0, 0, getWidth(), getHeight(), null);
}
}
Your code is much more complex than it needs to be. The Panel class members are unnecessary. All you need to do is save the mouse location in the mouseMoved() method - in a class member variable - and reference it in the paintComponent() method to draw your blue circle. The below code is a stripped-down version which displays a blue circle that follows the mouse pointer around on the screen.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MoonLite extends JPanel implements MouseMotionListener {
private Point pt;
public MoonLite() {
setBackground(Color.BLACK);
setForeground(Color.BLUE);
addMouseMotionListener(this);
}
public void mouseMoved(MouseEvent e) {
pt = e.getPoint();
repaint();
}
public void mouseDragged(MouseEvent e) {
// Do nothing.
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (pt != null) {
g.fillOval(pt.x - 10, pt.y - 10, 20, 20);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Moonlight");
frame.setSize(700, 700);
frame.setLocation(350, 50);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new MoonLite());
frame.setVisible(true);
}
});
}
}
I hope that I used the question function correctly this time. I have been confused by a question from yesterday to now. I used Google search to ask my java teacher and did not solve my problem.
When I use repaint, the child components in the shaped JPanel will exceed their display area. As in the following figures,
This is the effect I want
But when I use repaint somethings changes.
The button doesn’t seem right at first.
But sometimes the button will return to normal
these are my code. I use repaint because the information I checked tells me that I can use it. Repaint to achieve animation effects.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.RoundRectangle2D;
class GPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.clip(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), getWidth(), getHeight()));
g2d.setPaint(Color.BLUE);
g2d.fillRect(0, 0, getWidth(), getHeight());
}
}
public class MainComponentOverflow {
public static void main(String[] args) {
JFrame frame = new JFrame();
// This is a panel with a shape
GPanel panel = new GPanel();
// This one is the effect I am looking for, the rectangle is displayed in the Panel.
//panel.add(new Normal());
// The following two will have problems, the rectangle will be displayed outside the Panel
//panel.add(new Problem1());
panel.add(new Problem2());
//panel.add(new JButton("This will also cause problems, but it may also display properly when I resize the window."));
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
class Normal extends JPanel {
public Normal() {
setPreferredSize(new Dimension(500, 500));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
class Problem1 extends JPanel implements ActionListener {
public Problem1() {
Timer timer = new Timer(16, this);
timer.start();
setPreferredSize(new Dimension(500, 500));
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
class Problem2 extends JPanel implements ActionListener {
public Problem2() {
Timer timer = new Timer(16, this);
timer.start();
setPreferredSize(new Dimension(500, 500));
}
#Override
public void actionPerformed(ActionEvent e) {
setBackground(new Color((float) Math.random(), (float)Math.random(), (float)Math.random()));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
}
When the frame is painted first, the clip will be set in GPanel, and then the children will be painted in Problem1 with the same clip, so it will work.
However, when you repaint Problem1, GPanel will not be repainted first, so the clip isn’t set, and there is no clip to restrict Problem1.
If instead of repainting Problem1 you repaint the parent, GPanel, it will resolve your problem.
Another solution would be to put the clip in Problem1 too.
Note that you can replace your RoundRect2D with an Ellipse2D, as you use it to paint an ellipse.
I have to draw an archery target with two black lines in the innermost circle that forms a cross, but every time i adjust the lines so that the lines are closer to the centre it goes behind the image instead of appearing on top. How can I stop this? Does it need to have a separate set of instructions entirely?
This is my code:
package sumshapes;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*;
public class SumShapes extends JFrame
implements ActionListener {
private JPanel panel;
public void paint(Graphics g)
{
g.setColor(Color.BLACK);
g.drawLine(250, 200, 250, 200);
g.drawOval(140,90,200,200);
g.setColor(Color.BLACK);
g.fillOval(140,90,200,200);
g.drawOval(162,109,155,155);
g.setColor(Color.BLUE);
g.fillOval(162,109,155,155);
g.drawOval(183,129,112,112);
g.setColor(Color.RED);
g.fillOval(183, 129, 112, 112);
g.drawOval(210,153,60,60);
g.setColor(Color.YELLOW);
g.fillOval(210, 153, 60, 60);
g.setColor(Color.BLACK);
}
public static void main(String[] args) {
SumShapes frame = new SumShapes();
frame.setSize(500,400);
frame.setBackground(Color.yellow);
frame.createGUI();
frame.setVisible(true);
}
private void createGUI(){
setDefaultCloseOperation(EXIT_ON_CLOSE);
Container window = getContentPane();
window.setLayout (new FlowLayout());
}
public void actionPerformed(ActionEvent event) {
Graphics paper = panel.getGraphics();
paper.drawLine(20,80,120,80);
}
}
All your drawing should go into the paintComponent method of a lightweight component, such as a JPanel.
There should never be a need to call getGraphics. If you wish to change the drawing upon a particular action you should a) program the logic into paintComponent b) alter the logic in the Action c) call repaint on the Component
For example:
private JPanel panel = new JPanel(){
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);//call parent method first thing
//paint here
}
#Override
public Dimension getPreferredSize(){//provided so you can size this component as necessary
return new Dimension(500,400);
}
};
....
frame.add(panel);
frame.pack();
As an aside, I'd recommend placing all calls to to Swing components on the EDT - this means wrapping your Swing calls in the main method with SwingUtilities. eg
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable(){
#Override
public void run() {
SumShapes frame = new SumShapes();
....
}
});
}
I am trying to learn how to make a graphics program, but some of the methods in java AWT are giving me unexpected results.
I have made a window, and I place a rectangle and that works. I want another figure, a circle, to appear after 1 second. I have tried the wait(x) method, which just places the circle immediately, and now I have tried the Thread.sleep(x) method, which does work, however I get the following behaviour:
After one second, the circle is displayed on the screen, but after a split second it disappears again, and another split second later it reappears and stays on the screen. I don't want it to temporarily disappear. What am I doing wrong?
import java.awt.*;
class Example extends Canvas{
public static void main(String[] args){
Example graphicProgram = new Example();
Frame graphics = new Frame();
graphics.setSize(300, 300);
graphics.add(graphicProgram);
graphics.setVisible(true);
}
public Example(){
setSize(200, 200);
setBackground(Color.white);
}
public void paint(Graphics g){
g.fillRect(20, 150, 100, 100);
try{
Thread.sleep(1000);
} catch (Exception ex){
}
g.fillOval(150, 20, 100, 100);
}
}
Never call Thread.sleep from within a paint type of method. Doing this will make your GUI completely unresponsive.
Yes, do call the super painting method from within your painting method (as per muhammad's answer).
You should not call Thread.sleep(...) from the event thread either as this too will freeze your application.
You should skip doing AWT and move to Swing.
When you do so, do your drawing in the paintComponent(Graphics g) method of a JComponent or JPanel object.
Call the super's paintComponent(g) within your paintComponent method override.
Use a Swing Timer to do any delay or animation.
e.g.,
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class DrawFoo extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final Stroke BASIC_STROKE = new BasicStroke(3f);
private static final Color RECT_COLOR = Color.blue;
private static final Color OVAL_COLOR = Color.red;
private boolean drawCircle = false;
private int rectX = 20;
private int rectY = 150;
private int rectWidth = 100;
public DrawFoo() {
int delay = 1000;
Timer timer = new Timer(delay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
drawCircle = true;
repaint();
}
});
timer.setRepeats(false);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(BASIC_STROKE);
g2.setColor(RECT_COLOR);
g.fillRect(rectX, rectY, rectWidth, rectWidth);
if (drawCircle) {
g2.setColor(OVAL_COLOR);
g.fillOval(rectY, rectX, rectWidth, rectWidth);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("DrawFoo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawFoo());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
put this line as the first statment in your paint method super.paint(g);
it will be good to place also Graphics2D g2 = (Graphics2D)g; after above statment to use the improved performance and extra methods offered by Graphics2D
public class Graphics2DTest extends JPanel implements ActionListener{
private Timer time = new Timer(5,(ActionListener) this);
int x = 0,y = 0;
public void paintComponent(Graphics g){
Graphics2D gui = (Graphics2D) g;
Rectangle2D rectangle = new Rectangle2D.Double(x,y,100,150);
gui.setPaint(Color.GREEN);
gui.fill(rectangle);
time.start();
}
public void actionPerformed(ActionEvent arg0) {
x++;
y++;
repaint();
}
}
The problem is repaint() is supposed to clear the frame and draw the rectangle in the position, but the previously painted rectangle remains. So, how to do it? Please explain your answers.
Have you tried calling super.paintComponent(g) in your paintComponent method? This will clear prior images drawn in your JPanel:
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D gui = (Graphics2D) g;
Rectangle2D rectangle = new Rectangle2D.Double(x,y,100,150);
gui.setPaint(Color.GREEN);
gui.fill(rectangle);
//time.start();
}
Also, don't start a timer or do any program logic within the paintComponent method. First of all you cannot absolutely control when or if the method will be called, and secondly, this method must be concerned only with painting and nothing else, and needs to be as fast as possible.
For instance:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
public class Graphics2DTest extends JPanel implements ActionListener {
private Timer time = new Timer(5, (ActionListener) this);
int x = 0, y = 0;
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gui = (Graphics2D) g;
Rectangle2D rectangle = new Rectangle2D.Double(x, y, 100, 150);
gui.setPaint(Color.GREEN);
gui.fill(rectangle);
//time.start();
}
public void actionPerformed(ActionEvent arg0) {
x++;
y++;
repaint();
}
public Graphics2DTest() {
setPreferredSize(new Dimension(700, 500));
time.start();
}
private static void createAndShowUI() {
JFrame frame = new JFrame("Graphics2DTest");
frame.getContentPane().add(new Graphics2DTest());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
You need to repaint the background each time as well. Add code to paint the background before you paint the rectangle.
You need to clear the background first.
A resource is this:
http://java.sun.com/products/jfc/tsc/articles/painting/