Using repaint() to draw on JPanel which is added to JFrame - java

Attempting to draw my rectangle across the screen horizontally in realtime. When I run this I get nothing but the JFrame. I'm not sure what I am missing aside from maybe some type of threading freeze to redraw the shape maybe?
public class ScreenTest extends JFrame {
int rectY = 50;
public ScreenTest()
{
setSize(300,200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
private class DrawPanel extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(80, rectY, 50, 50);
}
}
public void Draw()
{
DrawPanel test = new DrawPanel();
add(test);
while (rectY < 200)
{
rectY = rectY + 10;
test.repaint();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ScreenTest myWindow = new ScreenTest();
myWindow.Draw();
}
}

Swing is single threaded and not thread safe.
What this means is, you should not perform any kind of long running or blocking operations within the "Event Dispatching Thread", as this will stop the UI from been painted or responding to new events.
It also means you should not update the UI, or any state the UI relies on, from outside the context of the Event Dispatching Thread.
Your code "is" working, but because the while-loop can run so fast, it's completing before the window is realised on the screen (visible and updatable). Swing is also optimised, so all the repaint calls are likely been consolidated into a single repaint pass.
A better solution might to start with Swing `Timer, which acts as a pseudo repeating loop, but which is called on within the context of the Event Dispatching Thread.
Start by taking a look at Concurrency in Swing and How to Use Swing Timers for more details.
Runnable Example
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class ScreenTest extends JFrame {
public ScreenTest() {
setSize(300, 200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
private class DrawPanel extends JPanel {
int rectY = 50;
private Timer timer;
// This is just convince
#Override
public void addNotify() {
super.addNotify();
timer = new Timer(25, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
rectY += 1;
repaint();
}
});
// Otherwise it disappears to fast
timer.setInitialDelay(1000);
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
timer.stop();
timer = null;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(80, rectY, 50, 50);
}
}
public void Draw() {
DrawPanel test = new DrawPanel();
add(test);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ScreenTest myWindow = new ScreenTest();
myWindow.Draw();
}
});
}
}

it is working but is so fast that you can't see it, you need to make the loop which changes the Y coordinate slower with a delay. to solve it i used Thread.sleep() in the while loop:
package paquete;
import javax.swing.*;
import java.awt.*;
public class ScreenTest extends JFrame {
int rectY = 50;
public ScreenTest()
{
setSize(300,200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
private class DrawPanel extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(80, rectY, 50, 50);
}
}
public void Draw() throws InterruptedException {
DrawPanel test = new DrawPanel();
add(test);
while (rectY < 200)
{
rectY = rectY + 10;
Thread.sleep(100);
test.repaint();
}
}
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ScreenTest myWindow = new ScreenTest();
myWindow.Draw();
}
}
i hope this helps you, you can change the duration changing the number inside the argument of Thread.sleep()

Related

Why is the repaint() method not calling my paintComponent() method?

I was trying to copy a repaint() and a paintComponent() method from a tutorial. After I copied the two methods my paintComponent did not get called and so the rectangle is not being showed. Here is my code:
public class Main {
GameWindow gw;
Main() {
gw = new GameWindow();
}
void start() {
gw.setWindow();
}
public static void main(String[] args) {
new Main().start();
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameWindow extends JPanel implements Runnable {
final int ORIGINAL_TILE_SIZE = 16;
final int SCALE = 3;
final int TILE_SIZE = ORIGINAL_TILE_SIZE * SCALE;
final int MAX_SCREEN_COLUMNS = 16;
final int MAX_SCREEN_ROWS = 12;
final int SCREEN_WIDTH = TILE_SIZE * MAX_SCREEN_COLUMNS;
final int SCREEN_HEIGHT = TILE_SIZE * MAX_SCREEN_ROWS;
Thread animation;
void setWindow() {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.setTitle("Avontuur");
window.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
window.getContentPane().setBackground(Color.black);
ImageIcon icon = new ImageIcon("C:\\Users\\Rick\\Desktop\\Star.png");
window.setIconImage(icon.getImage());
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
startAnimation();
}
void startAnimation() {
animation = new Thread(this);
animation.start();
}
#Override
public void run() {
while (animation != null) {
update();
repaint();
}
}
public void update() {
}
public void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.white);
g2.fillRect(100, 100, TILE_SIZE, TILE_SIZE);
g2.dispose();
}
}
I already tried some solutions from stackOverflow, but they did not work or they were not relevant to my problem. Now the code above is what I tried myself using the video, but after using a println in the method I saw it was not getting called. I expected it to work after watching the tutorial, but it didn't. Does anyone know how I can fix this? Thanks in advance!
Swing is single threaded - never block the Event Dispatching Thread with long running or blocking operations
Swing is NOT thread safe - never update the UI or any state the UI relies on from outside the context of the Event Dispatching Thread.
See Concurrency in Swing for more details.
Swing makes use of a passive rendering engine - you don't control the painting process and you need to work within in it's intended design/workflow, see Painting in AWT and Swing and Performing Custom Painting for more details.
So, what's the solution? In it's simplest form, use a Swing Timer, for example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new GamePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GamePane extends JPanel {
final int ORIGINAL_TILE_SIZE = 16;
final int SCALE = 3;
final int TILE_SIZE = ORIGINAL_TILE_SIZE * SCALE;
final int MAX_SCREEN_COLUMNS = 16;
final int MAX_SCREEN_ROWS = 12;
final int SCREEN_WIDTH = TILE_SIZE * MAX_SCREEN_COLUMNS;
final int SCREEN_HEIGHT = TILE_SIZE * MAX_SCREEN_ROWS;
private Timer timer;
#Override
public Dimension getPreferredSize() {
return new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT);
}
#Override
public void addNotify() {
super.addNotify();
if (timer != null) {
timer.stop();
}
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
update();
}
});
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
if (timer != null) {
timer.stop();
}
}
public void update() {
System.out.println("Updatey update");
repaint();
}
public void paintComponent(final Graphics g) {
super.paintComponent(g);
System.out.println("Painty paint paint");
Graphics2D g2 = (Graphics2D) g.create();
g2.setColor(Color.white);
g2.fillRect(100, 100, TILE_SIZE, TILE_SIZE);
g2.dispose();
}
}
}
You should now see a bunch of text been printed to the console.
Also - you might not, I've made some structural changes to your code, there is not reason for a JPanel based class to create it's own window (or if you really wanted to do this, I'd create a static method to do it, but then I'd be questioning why).
Oh, and also what DontKnowMuchBut Getting Better said in the comments!
Here is the specific rectangle problem with your code, I will not mention other things because is not in the question, you are not adding GameWindow itself as component of your JFrame, and you are not calling your paintComponent method anywhere, I put it on update and it worked fine, but I'm afraid that your code will block and you will not able to move the window and interact with it since you are always calling update/repaint over and over again inside your while statement.
edit.: As the guy in the comment said, we must not call paintcomponent directly, so I fixed.
void setWindow() {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.setTitle("Avontuur");
window.add(GameWindow.this);
window.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT));
window.getContentPane().setBackground(Color.black);
ImageIcon icon = new ImageIcon("C:\\Users\\Rick\\Desktop\\Star.png");
window.setIconImage(icon.getImage());
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
startAnimation();
}

Repainting-Thread doesn't repaint Inner-Class JPanel

I want to make a little rain program in swing, but for some reason I cannot repaint the panel from another class. I tried using an inner class for the panel this time, but it doesn't seem to work with repainting it from another class/thread. Does someone know why?
sscce:
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
public class UI extends JFrame {
public static void main(String[] args) {
UI myProgram = new UI();
myProgram.setVisible(true);
}
public UI() {
this.setSize(new Dimension(500,300));
this.setBackground(Color.WHITE);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
UserPanel p = new UserPanel(this);
}
public class UserPanel extends JPanel implements ActionListener {
private Timer time = new Timer(1, this);
private UI myFrame;
public UserPanel(UI myFrame) {
this.myFrame = myFrame;
this.setSize(myFrame.getSize());
time.start();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("painting");
g.setColor(Color.BLACK);
g.fillRect(this.getWidth()/2, this.getHeight()/2, 50,50);
}
}
}
UI Class (with inner class JPanel):
package Rain;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
public class UI extends JFrame {
public UI() {
this.setSize(new Dimension(500,300));
this.setBackground(Color.WHITE);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
UserPanel p = new UserPanel(this);
}
private class UserPanel extends JPanel implements ActionListener {
private Timer time = new Timer(1, this);
private UI myFrame;
private ArrayList<Raindrop> rain = new ArrayList<Raindrop>();
private static final int AMOUNT = 50;
private Random rand = new Random();
public UserPanel(UI myFrame) {
this.myFrame = myFrame;
this.setSize(myFrame.getSize());
for(int i = 0; i < AMOUNT; i++) {
createRain();
}
new Painter(this);
time.start();
}
public void createRain() {
float distance = rand.nextFloat() * 90 + 10;
int x = rand.nextInt(this.getWidth());
int y = 100;
rain.add(new Raindrop(distance,x,y));
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("tick");
for(Raindrop r : rain) {
r.fall();
}
}
public void paintComponent(Graphics g) {
System.out.println("painting");
g.setColor(this.getBackground());
g.fillRect(0,0,this.getWidth(),this.getHeight());
for(Raindrop r : rain) {
r.draw(g);
}
}
}
}
Painter:
package Rain;
import javax.swing.JPanel;
public class Painter extends Thread {
private JPanel p;
public Painter(JPanel p) {
this.p = p;
this.start();
}
public void run() {
while(true) {
System.out.println("trying to paint..");
p.repaint();
}
}
}
Console Output:
trying to paint..
tick
trying to paint..
tick
...
Expected Output:
trying to paint..
painting
tick
trying to paint..
...
The thread does work but it never calls the paintComponent(Graphics g) function in the panel
All Swing applications must run on their own thread, called EDT. (Hopefully, you start your application by calling SwingUtilities#invokelater method). So, repainting a component outside of Event Dispatch Thread is really bad bad (bad) idea. Instead of creating new Thread, repaint the component inside javax.swing.Timer's action listener since it will run in EDT.
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("tick");
for(Raindrop r : rain) {
r.fall();
}
repaint(); //repaint in EDT
}
Also, when you #Override paintComponent method, always start by calling super.paintComponent(g);
public void paintComponent(Graphics g) {
super.paintComponent(g);//let component get painted normally
System.out.println("painting");
g.setColor(this.getBackground());
g.fillRect(0,0,this.getWidth(),this.getHeight());
for(Raindrop r : rain) {
r.draw(g);
}
}
UPDATE after your SSCCE
In order a component to get painted, it must have a parent. You UserPanel p = new UserPanel(this); but you never add it to the frame:
UserPanel p = new UserPanel(this);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(p);
The complete SSCCE:
public class UI extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> { //Run in EDT
UI myProgram = new UI();
myProgram.setVisible(true);
});
}
public UI() {
super("title");//call super for frame
this.setSize(new Dimension(500, 300));
this.setBackground(Color.WHITE);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
UserPanel p = new UserPanel(this);
//Use border layout to make p fit the whole frame
getContentPane().setLayout(new BorderLayout());
getContentPane().add(p, BorderLayout.CENTER);
}
public class UserPanel extends JPanel implements ActionListener {
private Timer time = new Timer(1, this);
private UI myFrame;
public UserPanel(UI myFrame) {
this.myFrame = myFrame;
this.setSize(myFrame.getSize());
time.start();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("painting");
g.setColor(Color.BLACK);
g.fillRect(this.getWidth() / 2, this.getHeight() / 2, 50, 50);
}
}
}
Don't ignore the SwingUtilities.invokeLater.

java simple bouncing ball flickers

This is just a simple red ball going up and down and i see it flickering. I already saw few subjects about that but did not find any answer that helped me.
Thank you :)
The Window class with the go method that makes the ball goes up and down.
The panel that also contains the ball positions and that just repaints.
Window.java
import java.awt.Dimension;
import javax.swing.JFrame;
public class Window extends JFrame
{
public static void main(String[] args)
{
new Window();
}
public Panel pan = new Panel();
public Window()
{
this.setSize(600, 600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setContentPane(pan);
this.setVisible(true);
go();
}
private void go()
{
int vecY = 1;
while (true)
{
if (pan.y <= 100)
{
vecY = 1;
}
else if (pan.y >= 400)
{
vecY = -1;
}
pan.y += vecY;
pan.repaint();
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
Panel.java
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panel extends JPanel
{
public int x = 300;
public int y = 300;
public void paintComponent(Graphics g)
{
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.red);
g.fillOval(x, y, 50, 50);
}
}
There are a number of possible issues. The primary issue is likely to be a thread race condition between your while-loop and the paintComponent method.
Your while-loop is capable of change the state of the y position before the paintComponent has a chance to paint it's state. Painting is done at the leisure of the paint sub system, so calling repaint simply makes a request to the RepaintManager which decides what and when an actual paint cycle might take place, this means that you could be dropping frames.
For most animations in Swing, a Swing Timer is more the capable. It's safe to update the UI from within, as the ActionListener is called within the context of the EDT but won't block the EDT
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Window extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Window();
}
});
}
public Panel pan = new Panel();
public Window() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setContentPane(pan);
pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
go();
}
private void go() {
Timer timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pan.updateAnmationState();
}
});
timer.start();
}
public class Panel extends JPanel {
private int x = 300;
private int y = 300;
private int vecY = 1;
public void updateAnmationState() {
if (y <= 100) {
vecY = 1;
} else if (y >= 400) {
vecY = -1;
}
y += vecY;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.red);
g.fillOval(x, y, 50, 50);
}
}
}
This example worked fine for me on MiniMac

i can't see circle moving

While using Swing in java, I am trying to move a circle slowly from a starting position to an end position when clicking a button. However, I can't see the circle moving. It just moves from start to end in an instant.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MyApp {
private int x = 10;
private int y = 10;
private JFrame f;
private MyDraw m;
private JButton b;
public void go() {
f = new JFrame("Moving circle");
b = new JButton("click me to move circle");
m = new MyDraw();
f.add(BorderLayout.SOUTH, b);
f.add(BorderLayout.CENTER, m);
f.setSize(500, 500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
b.addActionListener(new Bute());
}
public static void main(String[] args) {
MyApp m = new MyApp();
m.go();
}
private class Bute implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < 150; i++) {
++x;
++y;
m.repaint();
Thread.sleep(50);
}
}
}
private class MyDraw extends JPanel {
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, 500, 500);
g.setColor(Color.red);
g.fillOval(x, y, 40, 40);
}
}
}
I think the problem is with the action listener because when I'm doing it without using button it is working. Any suggestions?
As Andrew Thompson said, calling Thread.sleep() without defining a second thread freezes everything, so the solution is to define and run another thread like so:
class Bute implements ActionListener, Runnable {
//let class implement Runnable interface
Thread t; // define 2nd thread
public void actionPerformed(ActionEvent e) {
t = new Thread(this); //start a new thread
t.start();
}
#Override //override our thread's run() method to do what we want
public void run() { //this is after some java-internal init stuff called by start()
//b.setEnabled(false);
for (int i = 0; i < 150; i++) {
x++;
y++;
m.repaint();
try {
Thread.sleep(50); //let the 2nd thread sleep
} catch (InterruptedException iEx) {
iEx.printStackTrace();
}
}
//b.setEnabled(true);
}
}
The only problem with this solution is that pressing the button multiple times will speed up the circle, but this can be fixed by making the button unclickable during the animation via b.setEnabled(true/false). Not the best solution but it works.
As said in the comments and another answer, don't block the EDT. Thead.sleep(...) will block it, so you have two options:
Create and manage your own (new) thread.
Use a Swing Timer
In this answer I'll be using a Swing Timer, since it's easier to use. I also changed the paintComponent method to use the Shape API and change the button text to start and stop accordingly as well as reusing the same ActionListener for the button and the timer:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MovingCircle {
private JFrame frame;
private CustomCircle circle;
private Timer timer;
private JButton button;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingCircle()::createAndShowGui);
}
private void createAndShowGui() {
frame = new JFrame(this.getClass().getSimpleName());
circle = new CustomCircle(Color.RED);
timer = new Timer(100, listener);
button = new JButton("Start");
button.addActionListener(listener);
circle.setBackground(Color.WHITE);
frame.add(circle);
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private ActionListener listener = (e -> {
if (!timer.isRunning()) {
timer.start();
button.setText("Stop");
} else {
if (e.getSource().equals(button)) {
timer.stop();
button.setText("Start");
}
}
circle.move(1, 1);
});
#SuppressWarnings("serial")
class CustomCircle extends JPanel {
private Color color;
private int circleX;
private int circleY;
public CustomCircle(Color color) {
this.color = color;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(color);
g2d.fill(new Ellipse2D.Double(circleX, circleY, 50, 50));
}
#Override
public Dimension preferredSize() {
return new Dimension(100, 100);
}
public void move(int xGap, int yGap) {
circleX += xGap;
circleY += yGap;
revalidate();
repaint();
}
public int getCircleX() {
return circleX;
}
public void setCircleX(int circleX) {
this.circleX = circleX;
}
public int getCircleY() {
return circleY;
}
public void setCircleY(int circleY) {
this.circleY = circleY;
}
}
}
I'm sorry, I can't post a GIF as I wanted but this example runs as expected.

How does repaint() method behave inside an infinite for loop?

I have this code,
import java.awt.*;
import java.applet.*;
public class FirstApplet extends Applet
{
int len;
char ch;
String msg="Hello World ";
public void init()
{
setBackground(Color.CYAN);
setForeground(Color.WHITE);
}
public void start()
{
System.out.println("Inside Start");
repaint();
}
public void paint(Graphics g)
{
System.out.println("Inside paint");
g.drawString(msg,200,200);
}
}
It outputs a CYAN coloured background with Hello World on it.And on the console(cmd),it outputs-
Inside Start
Inside paint
Now if I modify the code to this-
import java.awt.*;
import java.applet.*;
public class FirstApplet extends Applet
{
String msg="Hello World ";
int len;
char ch;
public void init()
{
setBackground(Color.CYAN);
setForeground(Color.WHITE);
}
public void start()
{
System.out.println("Inside Start");
for(;;)
{
repaint();
}
}
public void paint(Graphics g)
{
System.out.println("Inside paint");
g.drawString(msg,200,200);
}
}
It outputs a white coloured screen with no text on it,and on the console it just outputs-
Inside Start
I am unable to understand the output of second program,Although I am calling the repaint() inside the for loop every time yet why the colour of the applet window is not changing to CYAN colour and why its not printing "Inside paint" on the console?Can somebody please help me out.
You're tying up the GUI's event thread with your infinite loop, so that although repaint() is being called, the GUI's event thread is unable to act on it. Consider using a Swing Timer or a background thread instead.
For example, and continuing with your 1890's Applet example:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.*;
public class PaintEg extends Applet {
String msg = "Hello World ";
int len;
char ch;
public void init() {
setBackground(Color.CYAN);
setForeground(Color.WHITE);
}
public void start() {
System.out.println("Inside Start");
new Thread(new Runnable() {
public void run() {
for (;;) {
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
public void paint(Graphics g) {
System.out.println("Inside paint");
g.drawString(msg, 10, 20);
}
}
Better maybe is a Swing example that uses a Swing Timer and performs basic animation:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class PaintEg2 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 30;
private String msg = "Hello World ";
private int msgX = 0;
private int msgY = 0;
public PaintEg2() {
setBackground(Color.CYAN);
setForeground(Color.WHITE);
setFont(new Font(Font.SANS_SERIF, Font.BOLD, 20));
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString(msg, msgX, msgY);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
msgX++;
msgY++;
repaint();
}
}
private static void createAndShowGui() {
PaintEg2 mainPanel = new PaintEg2();
JFrame frame = new JFrame("PaintEg2");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note that in your code repaint() is being called and is being executed, but the paint manager is unable to act on this because it does so on the GUI's event thread. If the GUI's event thread is tied up, no painting can be done.
For more on how painting is done in Swing and AWT, please read Painting in AWT and Swing

Categories