Java JFrame paint method display flickers - java

while using paint from JFrame, my display flickers a lot. I can't make it stop and I would love to know how to make it stop flickering. I also tried to add the swing.timer here, but its still doesn't do anything.
package Game;
public class Play extends JFrame implements MouseMotionListener, MouseListener, KeyListener, ActionListener {
private int _xPad,_yPad,_xdir,_ydir;
private Brick[][] _bricks;
private Ball _gameBall;
private Timer _timer;
public Play(int[][] board){
super("Elemental Brick Breaker");
_xdir = 0;
_ydir = 0;
_xPad = 350;
_yPad = 500;
_timer = new Timer(1000,this);
_timer.start();
_gameBall = new FireBall(_xPad,_yPad);
_bricks = new Brick[8][10];
int x = 75, y = 40;
for(int i = 0 ; i < 8 ; i++) {
for(int j = 0 ; j < 10 ; j++) {
_bricks[i][j] = new FireBrick(x,y);
x = x + 65;
}
y = y + 25;
x = 75;
}
}
public void paint(Graphics g) {
super.paint(g);
g.fillRect(_xPad, _yPad, 120, 10);
for(int i = 0 ; i < 8 ; i++) {
for(int j = 0 ; j < 10 ; j++) {
g.fillRect(_bricks[i][j].getX(), _bricks[i][j].getY() , 60, 20);
}
}
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
}

Do not override paint or update. Painting should be done using a lightweight component and overriding its paintComponent method. For example:
public class MyPanel extends JPanel{
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
//custom painting
}
}
//elsewhere in your code - add the JPanel to the JFrame
MyPanel myPanel = new MyPanel();
JFrame frame = new JFrame();
frame.add(myPanel );
If you wish to repaint upon some type of event, call repaint on the instance. For instance, within the implementation of an ActionListener:
#Override
public void actionPerformed(ActionEvent e){
myPanel.repaint();
}

You can try this approach:
have a separate image to draw things on:
im=createImage(getWidth(), getHeight());
in constructor.
Then in paint:
Graphics g2=im.getGraphics();
and apply all commands relagated to g on g2:
g2.fillRect etc
then draw the image on g:
g.drawImage(im, 0, 0, this);
in short:
public void paint(Graphics g) {
Graphics g2=im.getGraphics();
g2.fillRect(_xPad, _yPad, 120, 10);
for(int i = 0 ; i < 8 ; i++) {
for(int j = 0 ; j < 10 ; j++) {
g2.fillRect(_bricks[i][j].getX(), _bricks[i][j].getY() , 60, 20);
}
g.drawImage(im, 0, 0, this);
}
}
and have an update method:
public void update(Graphics g) { paint(g); }
--
I have simplified your class as follows: - it does not produce flicker - if I put it back as you have it it produces flicker.
class Play extends JFrame implements ActionListener {
private int _xPad,_yPad,_xdir,_ydir;
private Rectangle[][] _bricks;
// private Ball _gameBall;
private javax.swing.Timer _timer;
Image im;
public Play(){
super("Elemental Brick Breaker");
setSize(1000, 500);
_xdir = 0;
_ydir = 0;
_xPad = 350;
_yPad = 500;
_timer = new javax.swing.Timer(1000,this);
_timer.start();
// _gameBall = new FireBall(_xPad,_yPad);
_bricks = new Rectangle[8][10];
int x = 75, y = 40;
for(int i = 0 ; i < 8 ; i++) {
for(int j = 0 ; j < 10 ; j++) {
_bricks[i][j] = new Rectangle(x,y, 60, 20);
x = x + 65;
}
y = y + 25;
x = 75;
}
setVisible(true);
im=createImage(getWidth(), getHeight());
}
public void paint(Graphics g) {
Graphics g2=im.getGraphics();
g2.fillRect(_xPad, _yPad, 120, 10);
for(int i = 0 ; i < 8 ; i++) {
for(int j = 0 ; j < 10 ; j++) {
g2.fillRect(_bricks[i][j].x, _bricks[i][j].y , 60, 20);
}
}
g.drawImage(im, 0, 0, this);
}
public void update(Graphics g) { paint(g); }
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
repaint();
System.out.println("t");
}
}

In your paint method, do
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

Related

How do I use thread.sleep or swing timer to delay my algorithm visualizer?

Whenever I use thread.sleep on my algorithm visualizer, my screen turns white. I have tried using a swing timer, but I couldn't do it since I am also trying to run the sorting loop. Other times when I tried to use swing timer, it ran the sorter completely and just infinitely called repaint(). I have also tried creating a new thread and putting thread.sleep onto there, but my screen turned white again. I'm not sure if a swing timer could work in this scenario.
public void drawPanel() {
ActionListener action = new ActionListener() {
public void actionPerformed(ActionEvent e) {
repaint();
System.out.println("repained");
}
};
timer = new Timer(1000, action);
timer.setRepeats(false);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.darkGray);
Graphics2D g2d = (Graphics2D) g;
int width = 10;
int x = 0;
Algorithims.HeapSort(listArr);
for (int i = 0; i < (screenWidth - 15) / width; i++) {
g2d.setPaint(Color.white);
g2d.fillRect(x, (screenHeight - listArr[i]) - 40, width, listArr[i]);
g2d.setPaint(Color.black);
g2d.drawRect(x, (screenHeight - listArr[i]) - 40, width, listArr[i]);
x += width;
}
}
public void swap(int i, int j) {
int temp = listArr[i];
listArr[i] = listArr[j];
listArr[j] = temp;
GUI gui = new GUI();
gui.drawPanel();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
listArr = shuffleRectangles();
JFrame frame = new JFrame();
GUI gui = new GUI();
frame.setTitle("Algorithim Visualizer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(screenWidth, screenHeight);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.add(gui);
}
Here is the the sorter from a different class:
public static void HeapSort(int[] array) {
GUI gui = new GUI();
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
if (array[i] < array[j]) {
//swapping i and j
gui.swap(i, j);
}
}
}
}

Changing the frame's display after every interval, like an animation

How can I use repaint here, that it changes the display after some interval, like an animation?
Ouput:
(Like initially and randomly it is) this-1 (after some short interval and randomly it changes to) this-2 and continued...
import java.awt.*;
import java.util.Random;
import javax.swing.*;
class MatrixPanel extends JPanel {
private final int sqW = 15;
private final int sqH = 15;
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
#Override
public void paintComponent(Graphics g) {
Random rand = new Random();
for (int i = 0; i < 300; i += this.sqW) {
for (int j = 0; j < 300; j += this.sqH) {
if (rand.nextInt(2) == 1) {
g.setColor(Color.BLACK);
} else {
g.setColor(Color.GRAY);
}
g.fillRect(i, j, this.sqW, this.sqH);
g.setColor(Color.BLACK);
g.drawRect(i, j, this.sqW, this.sqH);
}
}
}
}
class GameOfLifeGUI extends JFrame {
public GameOfLifeGUI() {
JSplitPane splitPane = new JSplitPane();
/*Panels*/
JPanel topPanel = new JPanel();
JPanel bottomPanel = new JPanel();
/*Label - 1*/
JLabel generationLabel = new JLabel("GenerationLabel");
generationLabel.setText("Generation #");
generationLabel.setFont(new Font("Comic Sans MS", Font.PLAIN, 14));
generationLabel.setBounds(10, 5, 300, 20);
/*Label - 2*/
JLabel aliveLabel = new JLabel("AliveLabel");
aliveLabel.setText("Alive: ");
aliveLabel.setFont(new Font("Comic Sans MS", Font.PLAIN, 14));
aliveLabel.setBounds(10, 25, 300, 20);
topPanel.setLayout(null);
topPanel.add(generationLabel);
topPanel.add(aliveLabel);
bottomPanel.add(new MatrixPanel());
setPreferredSize(new Dimension(315, 405));
getContentPane().setLayout(new GridLayout());
getContentPane().add(splitPane);
splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
splitPane.setDividerLocation(50);
splitPane.setTopComponent(topPanel);
splitPane.setBottomComponent(bottomPanel);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
pack();
}
public static void main(String[] args) {
new GameOfLifeGUI().setVisible(true);
}
}
Specifically not repaint, if there are other components that I can use and meet my requirements will be most welcome.
Thank you.
You don't want to do the following:
public void paintComponent(Graphics g) {
Random rand = new Random();
for (int i = 0; i < 300; i += this.sqW) {
for (int j = 0; j < 300; j += this.sqH) {
if (rand.nextInt(2) == 1) {
g.setColor(Color.BLACK);
} else {
g.setColor(Color.GRAY);
}
g.fillRect(i, j, this.sqW, this.sqH);
g.setColor(Color.BLACK);
g.drawRect(i, j, this.sqW, this.sqH);
}
}
}
Since painting is done on the EDT, you need to keep your processing to a minimum, otherwise performance will suffer and you may even lock up your application.
Use a Swing timer and create the image in a BufferedImage object. BufferedImages provide a graphics context so you can use the same methods you are using in paintComponent.
You can set the timer interval to whatever works for you and then issue a repaint when the image is created. So it could look similar to the following:
final BufferedImage image = new BufferedImage(300,300,BufferedImage.TYPE_INT_RGB);
...
Timer timer = new Timer(0, ae->createImage());
timer.setDelay(300);
timer.start();
...
public void createImage(BufferedImage image) {
Random rand = new Random();
Graphics g = image.getGraphics();
for (int i = 0; i < 300; i += this.sqW) {
for (int j = 0; j < 300; j += this.sqH) {
if (rand.nextInt(2) == 1) {
g.setColor(Color.BLACK);
} else {
g.setColor(Color.GRAY);
}
g.fillRect(i, j, this.sqW, this.sqH);
g.setColor(Color.BLACK);
g.drawRect(i, j, this.sqW, this.sqH);
}
}
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Image observer not required so it can be set to null.
g.drawImage(image, 0,0, null);
}
Finally, you need to set your MatrixPanel size large enough to hold the image. Probably 300 x 300.

Detecting when multiple Ellipse objects are being clicked

I am working on a version of the Squares game. For it I need to detect when my Ellipses are being clicked. But the problem is my method is using one Ellipse object. How can I detect which Ellipse is being clicked? Here is my code.
Main Squares class
public static boolean running = false;
public Squares() {
this.setSize(600, 600);
this.setTitle("Squares");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setContentPane(new SquarePane());
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public static void main(String[] args) {
try {
new Squares();
} catch (Exception e) {
e.printStackTrace();
System.out.println("Crashed");
System.exit(-1);
}
running = true;
}
}
SquaresPanel Class
public static int x = 100;
public static int y = 100;
public static Color randomColor;
public static float r;
public static float g;
public static float b;
public void paintComponent(Graphics gra) {
Graphics2D g2d = (Graphics2D) gra;
gra.setColor(Color.black);
gra.fillRect(0, 0, 600, 600);
Random rand = new Random();
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
Ellipse2D oval = new Ellipse2D.Double(x, y, 10, 10);
r = rand.nextFloat();
g = rand.nextFloat();
b = rand.nextFloat();
randomColor = new Color(r, g, b);
g2d.setColor(randomColor);
g2d.fill(oval);
x += 50;
}
x = 100;
y += 50;
}
}
Thanks guys!
Will
Without looking too much at your code (as I see it's lacking alot) I will just explain how your requirement can be achieve.
1st : You need a MouseListener and implement the mousePressed. From the MouseEvent object, you can obtain the point clicked. See How to Write MouseListener if you are unsure.
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
}
2nd: Keep a List of your ellipses
List<Ellipse2D> ellipses;
3rd: Keep a selectedEllipse variable to hold the select one.
Ellipse2D selectedEllipse;
4th: After clicking the point, you loop through the list, checking if each Ellipse2D.contains the point. Then do something with the selected Ellipse
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
for (Ellipse2D ellipse : ellipses) {
if (ellipse.contains(p) {
selectedEllipse = ellipse;
// do something with selectedEllipse
break;
} else {
selectedEllipse = null;
}
}
}
5th: Loop through your ellipses to paint the in the paintComponent method
protected void paintComponent(Grapchics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
for (Ellipse2D ellipse : ellipses) {
g2.fill(ellipse):
}
}
Side Notes
You must call super.paintComponent in your paintComponent method
protected void paintComponent(Graphics g) {
super.paintComponent(g);
}
UPDATE
After taking a closer look at your code, I see more of what you are trying to achieve. Looks like you want am 8 by 8 grid of your ellipses. Another option is just to create 64 panels. and paint each of them.
First have a panel class where you can set the color
public class EllipsePanel extends JPanel {
private Color color;
public EllipsePanel(Color color) {
this.color = color;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillOval(0, 0, getWidth(), getHeight());
}
}
Then you can use a panel to hold all those panel and use a GridLayout, but also keeping a JPanel[][] so you can easily refer to each panel. You can also a add a mouselistener to each panel
JPanel gridPanel = new JPanel(new GridLayout(8, 8));
EllipsePanel[][] panels = new EllipsePanel[8][8];
EllipsePanel selectedPanel = null;
int currentRow;
int currentCol;
...
for (int i = 0; i < 8; i++) {
for (int j = 0; i < 8; j++) {
final EllipPanel panel = new EllipsePanel(getRendomColor);
panel.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e) {
selectedPanel = panel;
// do something with selected panel;
}
});
gridPanel.add(panel);
}
}
You should implement a mouse listener on your JPannel, and then use the position clicked retrieved from the listener to work out which ellipse was clicked

Reference of graphics 2d object doesn't work in orphan Thread

I am trying to design a simple game using Graphics2D in a JPanel. I am able to draw normal objects by overriding the paintComponent() method. But when I reference the Graphics2D object inside a orphan Thread, it does not work. Where am I going wrong?
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.drawString("sample",60,100); //Works fine
if(<Certain Condition>){
new Thread(new Runnable(){
//Some Code Here
public void run() {
try{
g2d.drawString("sample2",60,100); //Does not work.. :(
System.out.println("Test Print"); //Shows Output
}
catch (Exception e)
{
}
}
}).start();
}
}
Here is the complete code for reference. This is essentially a 'ping pong ball' game. Its working well but I am not able to highlight an increase in score when the ball hits the striker. The important part of code is highlighted. It's SSCCE.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
public class MovingBall extends JPanel {
int XPos, YPos;
int speedX, speedY;
int diameter;
private JButton jButton1 = new JButton();
private JButton jButton2 = new JButton();
private JLabel jLabel1 = new JLabel();
private static Timer timer;
private static MovingBall movingball;
private int w,h;
private int strikerHeight;
private int strikerWidth;
private int score;
private boolean isBallMoving;
int strikerYPos;
Graphics2D g2d;
public MovingBall() {
//Striker Properties
strikerHeight = 100;
strikerWidth = 20;
strikerYPos = strikerHeight/2;
//Ball Properties
isBallMoving = false;
XPos = strikerWidth + 5;
YPos = 0;
Random r = new Random();
speedX = 2+ Math.abs(r.nextInt()) % 5;
speedY = 2+ Math.abs(r.nextInt()) % 5;
diameter = 50;
//UI Objects
try {
jbInit();
} catch (Exception e) {
e.printStackTrace();
}
movingball = this; //Helps to access the current class object in inner classes
//Create a timer for animation
timer = new Timer(1, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
movingball.repaint();
}
});
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
Dimension size = getSize();
Insets insets = getInsets();
w = size.width - insets.left - insets.right;
h = size.height - insets.top - insets.bottom;
//Paint the striker
g2d.setColor(Color.DARK_GRAY);
if(strikerYPos < strikerHeight/2) //Top End
g2d.fillRect(0,0, strikerWidth, strikerHeight);
else if(strikerYPos > (h-strikerHeight/2)) //Bottom End
g2d.fillRect(0,h-strikerHeight, strikerWidth, strikerHeight);
else //Anywhere in the middle
g2d.fillRect(0,strikerYPos - (strikerHeight/2), strikerWidth, strikerHeight);
//Paint the ball
if (isBallMoving) {
XPos += speedX;
YPos += speedY;
g2d.drawOval(XPos, YPos, diameter,diameter);
if((XPos+diameter) >= w)
{
//speedX *= -1;
speedX = ((int)Math.signum((double)speedX))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
XPos = w-diameter-1;
}
if(XPos <= strikerWidth)
{
if((YPos+diameter/2) >= (strikerYPos-strikerHeight/2) && (YPos+diameter/2) <= (strikerYPos+strikerHeight/2))
{
score++;
//////////////////////////////////////////////////////////////////////
/////THIS IS THE PART TO FOCUS ON///////////////////////////////////////
/////WHEN THE BALL HITS THE STRIKER, I SHOW A '+1' TEXT FADING UPWARDS FROM THE POINT OF HIT
/////(THIS IS TO HIGHLIGHT A +1 INCREASE IN SCORE)///////////////////
//////NOW SINCE THE BALL MAY HIT THE STRIKER AGAIN BEFORE THE PREVIOUS +1 HAS COMPLETELY FADED,
//////I HAVE MADE THIS SIMPLE THREAD TO CREATE A +1 EVERY TIME THERE IS A HIT. SO THERE CAN BE MULTIPLE
//////+1 ON THE SCREEN.
//-------------------------------SADLY, SOMETHING IS WRONG-------------------
//Print a '+1' to show score increase
new Thread(new Runnable(){
int yStart = strikerYPos;
int fadeLength = 0;
Timer pointTimer;
int MAX_FADE_LEN = 50;
public void run() {
try
{
pointTimer = new Timer(1, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if(fadeLength >= MAX_FADE_LEN)
pointTimer.stop();
g2d.setColor(new Color(0,0,0,255));
g2d.setFont(new Font("Times",Font.BOLD,20));
g2d.drawString("+1",60,yStart - fadeLength);
g2d.drawOval(100,100,50,50);
System.out.println("Drawn +1 at x = " + 60 + " y = " + (yStart - fadeLength));
fadeLength++;
}
});
pointTimer.start();
}
catch (Exception e)
{
}
}
}).start();
////////////////THREAD ENDS HERE//////////////////////
}
else
{
score--;
}
//SHOW THE SCORE ON THE LABEL
jLabel1.setText("Score: " + score);
speedX = ((int)Math.signum((double)speedX))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
XPos = strikerWidth+1;
}
if(YPos <= 0)
{
speedY = ((int)Math.signum((double)speedY))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
YPos = 0;
}
if((YPos+diameter) >= h)
{
speedY = ((int)Math.signum((double)speedY))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
YPos = h-diameter;
}
} else {
g2d.drawOval(XPos,YPos,diameter,diameter);
return;
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Magic Ball");
movingball = new MovingBall();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(movingball);
frame.setSize(450, 700);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void jbInit() throws Exception {
jButton1.setText("Start");
jButton1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton1_actionPerformed(e);
}
});
jButton2.setText("Stop");
jButton2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton2_actionPerformed(e);
}
});
jLabel1.setText("Score:0");
this.add(jButton1, null);
this.add(jButton2, null);
this.add(jLabel1, null);
this.setBackground(Color.white);
this.addMouseMotionListener(new MouseMotionListener() {
public void mouseMoved(MouseEvent e) {
int coordX = e.getX();
if(coordX < 200)
strikerYPos = e.getY();
}
public void mouseDragged(MouseEvent e) {
}
});
}
private void jButton1_actionPerformed(ActionEvent e) {
if(!isBallMoving)
isBallMoving = true;
}
private void jButton2_actionPerformed(ActionEvent e) {
isBallMoving = false;
}
}
everything inside paintComponent is repainted (automatically) on every mouse, key and internall methods implemented in API, then you thread probably never ended, there can be bunch of concurently Threads, nothing is repainted, displayed
output to the Swing GUI must be done on EDT
use Swing Timer instead of new Thread(new Runnable(){
call repaint()
I don't think many people would consider almost 250 LOC to be 'short' (though I must admit I was deliberately vague when writing the SSCCE document). OTOH I adapted my shorter source seen here to an animated example that shows a 'fade effect' on mouse clicks. Adapting it to your needs is left as an exercise for ..you.
This source shows how to change the drawn string over a period of 5 seconds. It uses the same Thread (the EDT) for both the main (bouncing ball) and fade animation.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.*;
class ShapeCollision {
private BufferedImage img;
private Area walls;
int x;
int y;
int xDelta = 3;
int yDelta = 2;
ArrayList<Strike> strikes;
/**
* A method to determine if two instances of Area intersect
*/
public boolean doAreasCollide(Area area1, Area area2) {
boolean collide = false;
Area collide1 = new Area(area1);
collide1.subtract(area2);
if (!collide1.equals(area1)) {
collide = true;
}
Area collide2 = new Area(area2);
collide2.subtract(area1);
if (!collide2.equals(area2)) {
collide = true;
}
return collide;
}
ShapeCollision() {
int w = 400;
int h = 200;
img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
final JLabel imageLabel = new JLabel(new ImageIcon(img));
x = w / 2;
y = h / 2;
strikes = new ArrayList<Strike>();
MouseListener strikeListener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
Strike s = new Strike(e.getPoint(),System.currentTimeMillis());
strikes.add(s);
}
};
imageLabel.addMouseListener(strikeListener);
walls = new Area(new Rectangle(0, 0, w, h));
ActionListener animate = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
animate();
imageLabel.repaint();
}
};
Timer timer = new Timer(50, animate);
timer.start();
JOptionPane.showMessageDialog(null, imageLabel);
timer.stop();
}
public void animate() {
Graphics2D g = img.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLACK);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
x += xDelta;
y += yDelta;
int s = 15;
Area player = new Area(new Ellipse2D.Double(x, y, s, s));
// Acid test of edge collision;
if (doAreasCollide(player, walls)) {
if (x + s > img.getWidth() || x < 0) {
xDelta *= -1;
}
if (y + s > img.getHeight() || y < 0) {
yDelta *= -1;
}
}
g.setColor(Color.ORANGE);
g.setColor(Color.YELLOW);
g.fill(player);
for (Strike strike : strikes) {
strike.draw(g);
}
g.dispose();
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new ShapeCollision();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
class Strike {
private Point point;
private long started;
private final long DURATION = 5000;
private boolean expired = false;
Strike(Point point, long time) {
this.point = point;
started = time;
}
public void draw(Graphics g) {
long now = System.currentTimeMillis();
long age = now - started;
if (age>DURATION) {
expired = true;
return;
}
double fraction = 1d-((double)age/(double)DURATION);
int alpha = (int)(fraction*255d);
Color c = new Color(255,255,255,alpha);
g.setColor(c);
String s = point.x + "," + point.y;
g.drawString( s, point.x, point.y );
}
public boolean isExpired() {
return expired;
}
}
As i understand - you save the Graphics2D object into g2d variable and trying to paint something onto it from a separate thread later? If so - don't do it. It is a really bad thing to do. Really.
If you want to modify (animate/change) whatever is painted on your component - simply change the data/model which affects the painting and than repaint the whole component or its modified part (any rectangle within the component bounds).
For example in your example case - keep painted string coordinates outside the paint method and modify them in a separate thread and then just call repaint on the component each time you change them. With each repaint string will be painted at the updated coordinates.
Also note that repaint might be called outside of the EDT (Event Dispatch Thread) as it will perform the actual repaint in EDT anyway.
Here is some random example of animation:
public class AnimationTest
{
private static List<Point> locationData = new ArrayList<Point> ();
private static List<Boolean> directionData = new ArrayList<Boolean> ();
public static void main ( String[] args )
{
locationData.add ( new Point ( 5, 25 ) );
directionData.add ( true );
final JComponent canvas = new JComponent ()
{
protected void paintComponent ( Graphics g )
{
super.paintComponent ( g );
Graphics2D g2d = ( Graphics2D ) g;
for ( int i = 0; i < locationData.size (); i++ )
{
Point p = locationData.get ( i );
g2d.drawString ( "Some string #" + i, p.x, p.y );
}
}
};
canvas.addMouseListener ( new MouseAdapter ()
{
public void mousePressed ( MouseEvent e )
{
locationData.add ( e.getPoint () );
directionData.add ( true );
canvas.repaint ();
}
} );
JFrame frame = new JFrame ();
frame.getContentPane ().setLayout ( new BorderLayout () );
frame.getContentPane ().add ( canvas );
frame.setSize ( 500, 500 );
frame.setLocationRelativeTo ( null );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setVisible ( true );
Timer timer = new Timer ( 1000 / 48, new ActionListener ()
{
public void actionPerformed ( ActionEvent e )
{
for ( int i = 0; i < locationData.size (); i++ )
{
Point p = locationData.get ( i );
if ( directionData.get ( i ) )
{
if ( p.y < canvas.getHeight () - 1 )
{
p.y += 1;
}
else
{
directionData.set ( i, false );
}
}
else
{
if ( p.y > 20 )
{
p.y -= 1;
}
else
{
directionData.set ( i, true );
}
}
}
canvas.repaint ();
}
} );
timer.start ();
}
}
You can find here:
Data lists on which painting and animation are based with single initial element
Data modification through mouse interaction
Proper canvas update on any data changes
Example is not too optimized, but should be enough to understand the concept.
Print the graphicsobject, if it changes between calls in paintComponent it means the one you are passing to the orphan thread is a dead object which swing doesn't use anymore for painting on the current component.
Swing is a platform-independent, Model-View-Controller GUI framework for Java, which follows a single-threaded programming model.
You should use dispatcher. http://en.wikipedia.org/wiki/Event_Dispatch_Thread

How to move the image inside the JApplet in vertical line?

I have displayed an image(ball) inside the JApplet, now I want the image to move in a vertical way (up and down). The problem is I don't know how to do it.
Could someone has an idea about this matter?
You need to set the position of that image to some calculated value (means you caculate the vertical position using time, speed and maybe other restrictions).
How you'd set that position depends on how you draw the image.
Example, based on drawing in the applet's (or a nested component's) paint(Graphics g) method:
//first calculate the y-position
int yPos += timeSinceLastPaint * speed; //increment the position
if( (speed > 0 && yPos > someMaxY) || (speed < 0 && yPos <0 ) ) {
speed *= -1; //if the position has reached the bottom (max y) or the top invert the direction
}
//in your paint(Graphics g) method:
g.drawImage(image, yPos, x, null);
Then you'd have to constantly repaint the applet.
More information on animations in applets can be found here: http://download.oracle.com/javase/tutorial/uiswing/components/applet.html
another example for javax.swing.Timer with moving Ojbects created by paintComponent(Graphics g), and I have lots of Start, not some blurred Mikado :-)
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class AnimationBackground {
private Random random = new Random();
private JFrame frame = new JFrame("Animation Background");
private final MyJPanel panel = new MyJPanel();
private JLabel label = new JLabel("This is a Starry background.", JLabel.CENTER);
private JPanel stopPanel = new JPanel();
private JPanel startPanel = new JPanel();
public AnimationBackground() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
panel.setBackground(Color.BLACK);
for (int i = 0; i < 50; i++) {
Star star = new Star(new Point(random.nextInt(490), random.nextInt(490)));
star.setColor(new Color(100 + random.nextInt(155), 100 + random.nextInt(155), 100 + random.nextInt(155)));
star.setxIncr(-3 + random.nextInt(7));
star.setyIncr(-3 + random.nextInt(7));
panel.add(star);
}
panel.setLayout(new GridLayout(10, 1));
label.setForeground(Color.WHITE);
panel.add(label);
stopPanel.setOpaque(false);
stopPanel.add(new JButton(new AbstractAction("Stop this madness!!") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
panel.stopAnimation();
}
}));
panel.add(stopPanel);
startPanel.setOpaque(false);
startPanel.add(new JButton(new AbstractAction("Start moving...") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
panel.startAnimation();
}
}));
panel.add(startPanel);
frame.add(panel);
frame.pack();
frame.setLocation(150, 150);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AnimationBackground aBg = new AnimationBackground();
}
});
}
private class Star extends Polygon {
private static final long serialVersionUID = 1L;
private Point location = null;
private Color color = Color.YELLOW;
private int xIncr, yIncr;
static final int WIDTH = 500, HEIGHT = 500;
Star(Point location) {
int x = location.x;
int y = location.y;
this.location = location;
this.addPoint(x, y + 8);
this.addPoint(x + 8, y + 8);
this.addPoint(x + 11, y);
this.addPoint(x + 14, y + 8);
this.addPoint(x + 22, y + 8);
this.addPoint(x + 17, y + 12);
this.addPoint(x + 21, y + 20);
this.addPoint(x + 11, y + 14);
this.addPoint(x + 3, y + 20);
this.addPoint(x + 6, y + 12);
}
public void setColor(Color color) {
this.color = color;
}
public void move() {
if (location.x < 0 || location.x > WIDTH) {
xIncr = -xIncr;
}
if (location.y < 0 || location.y > WIDTH) {
yIncr = -yIncr;
}
translate(xIncr, yIncr);
location.setLocation(location.x + xIncr, location.y + yIncr);
}
public void setxIncr(int xIncr) {
this.xIncr = xIncr;
}
public void setyIncr(int yIncr) {
this.yIncr = yIncr;
}
public Color getColor() {
return color;
}
}
private class MyJPanel extends JPanel {
private static final long serialVersionUID = 1L;
private ArrayList<Star> stars = new ArrayList<Star>();
private Timer timer = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Star star : stars) {
star.move();
}
repaint();
}
});
public void stopAnimation() {
if (timer.isRunning()) {
timer.stop();
}
}
public void startAnimation() {
if (!timer.isRunning()) {
timer.start();
}
}
#Override
public void addNotify() {
super.addNotify();
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
timer.stop();
}
MyJPanel() {
this.setPreferredSize(new Dimension(512, 512));
}
public void add(Star star) {
stars.add(star);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Star star : stars) {
g.setColor(star.getColor());
g.fillPolygon(star);
}
}
}
}
How to move the image inside the JApplet ..?
Pretty much exactly the same way you might do it in a JFrame, JComponent or JPanel or...
Or to put that another way, nothing to do with applets and everything to do with Graphics2D. For more details, see the 2D Graphics Trail of the Java Tutorial.
When you've figured how to move an image and paint it to a Graphics2D, implement that logic in a JComponent or JPanel's paintComponent(Graphics) method and drop the component with moving image into a JApplet or JFrame (or a JPanel etc.).
For the animation side of it, use a javax.swing.Timer as seen in this example. This example does not extend any component. Instead, it creates a BufferedImage and adds it to a JLabel that is displayed to the user. When the timer fires, the code grabs the Graphics object of the image, and proceeds from there to draw the bouncing lines.
import java.awt.image.BufferedImage;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.*;
import javax.swing.*;
import java.util.Random;
class LineAnimator {
public static void main(String[] args) {
final int w = 640;
final int h = 480;
final RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
);
hints.put(
RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY
);
final BufferedImage bi = new BufferedImage(w,h, BufferedImage.TYPE_INT_ARGB);
final JLabel l = new JLabel(new ImageIcon(bi));
final BouncingLine[] lines = new BouncingLine[100];
int factor = 1;
for (int ii=0; ii<lines.length; ii++) {
lines[ii] = new BouncingLine(w*factor,h*factor);
}
final Font font = new Font("Arial", Font.BOLD, 30);
ActionListener al = new ActionListener() {
int count = 0;
long lastTime;
String fps = "";
private final BasicStroke stroke = new BasicStroke(6);
public void actionPerformed(ActionEvent ae) {
count++;
Graphics2D g = bi.createGraphics();
g.setRenderingHints(hints);
g.setColor(new Color(55,12,59));
g.fillRect(0,0,w,h);
g.setStroke(stroke);
for (int ii=0; ii<lines.length; ii++) {
lines[ii].move();
lines[ii].paint(g);
}
if ( System.currentTimeMillis()-lastTime>1000 ) {
lastTime = System.currentTimeMillis();
fps = count + " FPS";
count = 0;
}
g.setColor(Color.YELLOW);
g.setFont(font);
g.drawString(fps,5,h-5);
l.repaint();
g.dispose();
}
};
Timer timer = new Timer(25,al);
timer.start();
JOptionPane.showMessageDialog(null, l);
//System.exit(0);
timer.stop();
}
}
class BouncingLine {
private final Color color;
private static final Random random = new Random();
Line2D line;
int w;
int h;
int x1;
int y1;
int x2;
int y2;
BouncingLine(int w, int h) {
line = new Line2D.Double(random.nextInt(w),random.nextInt(h),random.nextInt(w),random.nextInt(h));
this.w = w;
this.h = h;
this.color = new Color(
random.nextInt(255)
,random.nextInt(255)
,random.nextInt(255)
,64+random.nextInt(128)
);
x1 = (random.nextBoolean() ? 1 : -1);
y1 = (random.nextBoolean() ? 1 : -1);
x2 = -x1;
y2 = -y1;
}
public void move() {
int tx1 = 0;
if (line.getX1()+x1>0 && line.getX1()+x1<w) {
tx1 = (int)line.getX1()+x1;
} else {
x1 = -x1;
tx1 = (int)line.getX1()+x1;
}
int ty1 = 0;
if (line.getY1()+y1>0 && line.getY1()+y1<h) {
ty1 = (int)line.getY1()+y1;
} else {
y1 = -y1;
ty1 = (int)line.getY1()+y1;
}
int tx2 = 0;
if (line.getX2()+x2>0 && line.getX2()+x2<w) {
tx2 = (int)line.getX2()+x2;
} else {
x2 = -x2;
tx2 = (int)line.getX2()+x2;
}
int ty2 = 0;
if (line.getY2()+y2>0 && line.getY2()+y2<h) {
ty2 = (int)line.getY2()+y2;
} else {
y2 = -y2;
ty2 = (int)line.getY2()+y2;
}
line.setLine(tx1,ty1,tx2,ty2);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setColor(color);
//line.set
g2.draw(line);
}
}
Update 1
I want to do it in JApplet(1) using the image(2), is it possible(3)?
The examples by mKorbel and myself feature either an image in a JLabel or custom rendering in a JPanel. In our case, we added the components to a JOptionPane & a JFrame. Either example could be just as easily added to a JApplet, or a JDialog, or as part of another panel, or.. See the Laying Out Components Within a Container lesson & Using Top-Level Containers in the Java Tutorial for more details.
Instead of the stars or lines in our examples, ..paint your image. My example goes so far as to demonstrate how to get the position to bounce around within the bounds of the container.
Sure it is possible, but "Batteries not included". Our intention is to give you some ideas that you can then adapt to your bouncing ball applet. I doubt anyone is going to create an example for you, using balls, in an applet. Though if you post an SSCCE that shows your intent and what you tried, I (and others) would often run with that source. If you want more specific answers, ask a more specific SSCCE. ;)
I want to do it in JApplet.
Why not both? You can have a hybrid application/applet as shown in this animation.

Categories

Resources