timer.schedule() in not working at all (in Java) - java

I made a simple program in Java which draws a rectangle on a canvas. And then the rectangle starts moving along X-axis, from left to right.
But the timer.schedule() function is not working. Following is the code:-
package firstanimation;
import java.awt.*;
import java.util.Timer;
public class FirstAnimation {
public static void main(String[] args) {
Frame frame = new Frame("SomeRandomName");
frame.setBounds(50, 50, 700, 500);
frame.setBackground(Color.red);
MyCanvas canvas = new MyCanvas();
frame.add(canvas);
frame.setVisible(true);
Graphics graph = frame.getGraphics();
Timer timer = new Timer();
Task task = new Task(canvas, graph);
timer.schedule(task, 1000,1000);
}
}
package firstanimation;
import java.awt.*;
public class MyCanvas extends Canvas{
public int x,y,width,height;
public MyCanvas()
{
x = 0;
y = 0;
width = 50;
height = 50;
}
#Override
public void paint(Graphics g) {
g.setColor(Color.LIGHT_GRAY);
g.fillRect(x, y, width, height);
}
#Override
public void update(Graphics g) {
x+=10;
g.fillRect(x, y, width, height);
}
}
package firstanimation;
import java.util.TimerTask;
import java.awt.Graphics;
public class Task extends TimerTask{
private MyCanvas canvas;
private Graphics graphics;
public Task(MyCanvas can, Graphics g)
{
super();
canvas = can;
graphics = g;
canvas.paint(g);
}
#Override
public void run() {
canvas.update(graphics);
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
But the strange thing is... Every time i'm maximizing and restoring the frame, the box is moving.
Why is this happening?

"But the strange thing is... Every time i'm maximizing and restoring the frame, the box is moving. Why is this happening?"
Because repaint() is called when you resize which update the graphics, which you should be doing, instead of trying to call paint.
But...
Still many things wrong.
Seeing as this is your first animation (package firstanimation;), let me get you started in the right direction.
Don't use Canvas. Use JPanel or JComponent instead. When you do, don't override paint but paintComponent instead. Also make sure you call super.paintComponent so you aren't let with any paint artifact during the animation.
public class MyCanvas extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//painting code
}
}
Never use getGraphics on a component to do any painting.
You shouldn't ever have to explicitly call paint. The paint[Component] method will be implicitly called for you. A simple call to repaint() will repaint the component.
I just realized you're using all AWT components. Don't use them, they're out-dated. Instead use Swing component. The majority of them are just prefixed with a J, like Frame -> JFrame. They are in the javax.swing.* package.
For animation use a javax.swing.Timer. You can see more at How to Use Timers. The basic construct is
Timer ( int delayInMillis, ActionListener listener )
where delayInMillis is the time to delay between ticks(in this case animations) and the ActionListener listens for "ticks". Each tick, the actionPerformed of the ActionListener is called. There, you can put the code to update any variables you use for animation.
I suggest you read the tutorials Performing Custom Painting to see the proper way to paint.
Here's a simple example with all the points above mentioned
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.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class AnimateBall extends JPanel {
private static final int D_W = 500;
private static final int D_H = 300;
private Ball ball;
public AnimateBall() {
Random rand = new Random();
int randX = rand.nextInt(D_W);
int randY = rand.nextInt(D_H);
ball = new Ball(randX, randY);
Timer timer = new Timer(15, new ActionListener() {
public void actionPerformed(ActionEvent e) {
ball.animate();
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
ball.drawBall(g);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public class Ball {
int x = 0;
int y = 0; // Current ball position
int dx = 4; // Increment on ball's x-coordinate
int dy = 4; // Increment on ball's y-coordinate
int radius = 15; // Ball radius
public Ball(int x, int y) {
this.x = x;
this.y = y;
}
Color color = new Color((int) (Math.random() * 256),
(int) (Math.random() * 256), (int) (Math.random() * 256));
public void drawBall(Graphics g) {
g.setColor(color);
g.fillOval(x - radius, y - radius, radius * 2, radius * 2);
}
public void animate() {
if (x < 0 || x > getWidth()) {
dx = -dx;
}
if (y < 0 || y > getHeight()) {
dy = -dy;
}
// Adjust ball position
x += dx;
y += dy;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new AnimateBall());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Related

How to draw a rectangle moving in a circle around the center of the canvas?

The code is meant to draw a rectangle, which moves in a circle around the center of the canvas one time. The code I currently have is
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import javax.swing.Timer;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class Q3_Circular extends JComponent {
protected int degree = 0;
protected double xStart;
protected double yStart;
protected Timer timer;
public Q3_Circular() {
timer = new Timer(1000, new TimerCallback()); //creates new times that refreshes every 100 ms, and called the TimerCallback class
timer.start();
}
protected class TimerCallback implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (degree < (2 * Math.PI)){
xStart = getWidth()/2 * Math.cos(degree+1);
yStart = getHeight()/2 * Math.sin(degree+1);
degree+= 1;
repaint();
}
else {
degree += 0;
repaint();
}
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("AnimatedSquare");
Q3_Circular canvas = new Q3_Circular();
frame.add(canvas);
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public void paintComponent(Graphics g){
xStart = (double)(getWidth())/2.0 * Math.cos(degree);
yStart = (double)(getHeight())/2.0 * Math.sin(degree);
Graphics2D g2 = (Graphics2D) g;
g2.draw(new Rectangle2D.Double(xStart,yStart, 25,25));
repaint();
}
}
This code appears to draw the rectangle very quickly around the point (0,0). I'm not sure where the code is wrong.
Your code was confusing. Here's the GUI I created.
When creating a Swing GUI, use the model / view / controller pattern. Create a GUI model, and GUI view, and one or more controllers to modify the model and repaint the view.
Here are the changes I made to your code.
I created a DrawingRectangle class to hold the information about the drawing rectangle. This class is a plain old Java object with getters and setters. This class is the GUI model.
I moved everything out of the main method except for the call to the SwingUtilities invokeLater method. The invokeLater method puts the creation and use of the Swing components on the Event Dispatch thread. Oracle and I insist that all Swing applications start on the Event Dispatch thread.
I create the drawing rectangle in the constructor of the Q3_Circular class. Generally, you create the GUI model, then the GUI view.
I rearranged the JFrame code in the run method to be in the proper order. I removed the setSize method and replaced it with the pack method. We don't care how big the JFrame is. We care how big the drawing panel is.
I created a drawing panel from a JPanel. Here, we set the preferred size of the drawing panel. We extend a JPanel so we can override the paintComponent method.
The paintComponent method does nothing but paint the drawing rectangle. No calculations or anything but painting is done in the paintComponent method. I added a call to the super paintComponent method to maintain the Swing paint chain and clear the drawing panel before I paint the drawing rectangle. I draw the rectangle using the x and y coordinates as the center of the rectangle, rather than the upper left corner. This is the one transformation I do in the drawing code.
I created a drawing animation from a Runnable. You can use a Swing Timer if you want. I find it easier to create my own animation code. This is the GUI controller. Here is where we do the calculations, update the model, and repaint the drawing panel. In the repaint method, I use the SwingUtilities invokeLater method to do the painting on the Event Dispatch thread. I do this because the animation thread is a separate thread.
Here's the code. I put all the classes together so I could paste the code easier. You should separate the classes into different files.
package com.ggl.testing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Q3_Circular implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Q3_Circular());
}
private static final int DRAWING_WIDTH = 300;
private static final int DRAWING_HEIGHT = DRAWING_WIDTH;
private DrawingRectangle drawingRectangle;
public Q3_Circular() {
int center = DRAWING_WIDTH / 2;
Rectangle2D rectangle = new Rectangle2D.Double(center, center, 32D, 32D);
drawingRectangle = new DrawingRectangle(Color.RED, rectangle);
}
#Override
public void run() {
JFrame frame = new JFrame("Animated Square");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawingPanel drawingPanel = new DrawingPanel(DRAWING_WIDTH,
DRAWING_HEIGHT, drawingRectangle);
frame.add(drawingPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
new Thread(new DrawingAnimation(drawingPanel, drawingRectangle))
.start();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 8226587438110549806L;
private DrawingRectangle drawingRectangle;
public DrawingPanel(int width, int height,
DrawingRectangle drawingRectangle) {
this.setPreferredSize(new Dimension(width, height));
this.drawingRectangle = drawingRectangle;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(drawingRectangle.getColor());
Rectangle2D rectangle = drawingRectangle.getRectangle();
int x = (int) Math.round(rectangle.getX());
int y = (int) Math.round(rectangle.getY());
int width = (int) Math.round(rectangle.getWidth());
int height = (int) Math.round(rectangle.getHeight());
g.fillRect(x - width / 2, y - height / 2, width, height);
}
}
public class DrawingAnimation implements Runnable {
private DrawingPanel drawingPanel;
private DrawingRectangle drawingRectangle;
public DrawingAnimation(DrawingPanel drawingPanel,
DrawingRectangle drawingRectangle) {
this.drawingPanel = drawingPanel;
this.drawingRectangle = drawingRectangle;
}
#Override
public void run() {
int xCenter = drawingPanel.getWidth() / 2;
int yCenter = drawingPanel.getHeight() / 2;
double radius = drawingPanel.getWidth() / 3;
for (int degree = 0; degree < 360; degree++) {
double radians = Math.toRadians((double) degree);
double x = radius * Math.cos(radians) + xCenter;
double y = radius * Math.sin(radians) + yCenter;
drawingRectangle.setRectangleOrigin(x, y);
repaint();
sleep(100L);
}
}
private void sleep(long interval) {
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
}
}
private void repaint() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.repaint();
}
});
}
}
public class DrawingRectangle {
private final Color color;
private Rectangle2D rectangle;
public DrawingRectangle(Color color, Rectangle2D rectangle) {
this.color = color;
this.rectangle = rectangle;
}
public void setRectangleOrigin(double x, double y) {
rectangle
.setRect(x, y, rectangle.getWidth(), rectangle.getHeight());
}
public Color getColor() {
return color;
}
public Rectangle2D getRectangle() {
return rectangle;
}
}
}

Transparent Panels

I am making a game where a user has to draw lines so as to make a ball bounce into a target. I'm having trouble getting both the ball and the line to show up concurrently, and I can get only one or the other to appear. It seems to me that the panels block each other out, even though I made them transparent. I would like for them both to appear on the same frame. As of this post, the line panel covers the ball panel.
import javax.swing.Timer;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.Graphics;
import java.awt.Color;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class Game
{
public static void main(String args[]) throws Exception
{
JFrame f = new JFrame("Let's Play");
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setSize(1280, 720);
f.setLocation(300, 300);
f.setResizable(false);
//this part draws a ball that bounces around the screen
BallPanel ballPanel = new BallPanel()
{
// draw rectangles and arcs
public void paintComponent(Graphics g)
{
super.paintComponent(g); // call superclass's paintComponent
g.setColor(Color.red);
// check for boundaries
if (x < radius) dx = Math.abs(dx);
if (x > getWidth() - radius) dx = -Math.abs(dx);
if (y < radius) dy = Math.abs(dy);
if (y > getHeight() - radius) dy = -Math.abs(dy);
// adjust ball position
x += dx;
y += dy;
g.fillOval(x - radius, y - radius, radius*2, radius*2);
}
};
ballPanel.setOpaque(false);
f.add(ballPanel);
//this part allows you to draw lines on the frame with your mouse
JPanel lineP = new JPanel()
{
Point pointStart = null;
Point pointEnd = null;
{
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent me)
{
pointStart = me.getPoint();
}
public void mouseReleased(MouseEvent me)
{
pointStart = null;
}
});
addMouseMotionListener(new MouseMotionAdapter()
{
public void mouseMoved(MouseEvent me)
{
pointEnd = me.getPoint();
}
public void mouseDragged(MouseEvent me)
{
pointEnd = me.getPoint();
repaint();
}
});
}
public void paint(Graphics dline)
{
super.paint(dline);
if (pointStart != null)
{
dline.setColor(Color.RED);
dline.drawLine(pointStart.x, pointStart.y, pointEnd.x, pointEnd.y);
}
}
};
lineP.setOpaque(false); //attempted to enable to see ball panel here
f.add(lineP);
f.setVisible(true);
}
}
class BallPanel extends JPanel implements ActionListener
{
private int delay = 10;
protected Timer timer;
public int x = 30; // x position
public int y = 30; // y position
public int radius = 15; // ball radius
public int dx = 10; // increment amount (x coord)
public int dy = 10; // increment amount (y coord)
public BallPanel()
{
timer = new Timer(delay, this);
timer.start(); // start the timer
}
public void actionPerformed(ActionEvent e)
// will run when the timer fires
{
repaint();
}
}
You've got several issues, but the main one is that you're over-using GUI components. You should have just one single component JPanel that does the drawing, a DrawingPanel, and not a ball panel and a line panel. Rather Ball and Line should be logical classes, not GUI classes, and their display should be in the same single DrawingPanel.
Other issues include:
A main method that has way too much code. Most of that code should be off-loaded into the OOP world where it belongs.
GUI component classes that also implement listener interfaces. This is giving the class too much responsibility making debugging and upgrading difficult. Separate these concerns.
One of your classes overrides the paint method, and this should be avoided. Override paintComponent.
The other class that overrides paintComponent has program logic within paintComponent, and this should be avoided since you have limited control over when or if this method gets called. Get the logic out of that class and into either the mouse listener code or the game loop code (Swing Timer).

How do I make a sprite move in a custom JPanel?

How do I make a sprite move in a custom JPanel?
I have looked at the similar questions and although one question is similar, it isn't addressing my problem. I have a sprite in a JPanel and I am unable to get it to move. One of the requirements I have to meet for the program is that it must begin moving when a JButton is pressed (Mouse Click). I have the code set-up in a way I believe should work, but it will spit out a long list of errors when I press the button. I'm also required to have the panel be a custom panel class.
What I need to know is this:
Methods (ha) of programming sprite movement.
Continuing to move the sprite without a trail.
Making the sprite bounce off the edges of the panel. Done (Unable to test due to no moving ball)
Here's the code I have (MainClient).
package clientPackage;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import logicPack.Logic;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ClientClass
{
Ball mSolo = new Ball();
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClientClass window = new ClientClass();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ClientClass()
{
initialize();
}
/**
* Initialize the contents of the frame.
*/
Logic Logical;
Graphics g;
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 590, 520);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
SpriteField panel = new SpriteField();
panel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
/* int tX = e.getX();
Logical.MoveBallX();
int tY = e.getY();
Logical.MoveBallY();
panel.repaint();*/
Logical.MoveBallX();
Logical.MoveBallY();
panel.repaint();
}
});
panel.setForeground(Color.WHITE);
panel.setBackground(Color.GRAY);
panel.setBounds(64, 92, 434, 355);
frame.getContentPane().add(panel);
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
Graphics2D g2 = (Graphics2D)g;
mSolo.DrawSprite(g2 , Logical.MoveBallX(), Logical.MoveBallY());
}
});
btnStart.setBounds(64, 13, 174, 60);
frame.getContentPane().add(btnStart);
}
}
And here are my other Classes (Logic)
package logicPack;
import clientPackage.Ball;
public class Logic
{
Ball mSolo;
public int MoveBallX()
{
int NewX = mSolo.xPos + 50;
return NewX;
}
public int MoveBallY()
{
int NewY = mSolo.yPos + 50;
return NewY;
}
//Motion, force, friction and collision GO HERE ONLY
}
SpriteField
package clientPackage;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class SpriteField extends JPanel
{
Ball mSolo;
SpriteField()
{
mSolo = new Ball();
repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
mSolo.DrawSprite(g2 , mSolo.xPos , mSolo.yPos);
}
}
Ball
package clientPackage;
import java.awt.Color;
import java.awt.Graphics2D;
public class Ball
{
Ball()
{
}
public int xPos = 25;
public int yPos = 25;
int diameter = 25;
public void DrawSprite(Graphics2D g2, int xPos, int yPos)
{
g2.setColor(Color.BLACK);
g2.fillOval(xPos - diameter / 2 , yPos - diameter / 2 , diameter , diameter);
}
}
If you do not understand my Java comments, you can just ignore them.
If you need more details to help me, let me know.
EDIT 1:
Andrew, the closest article I could find used arrow keys to move a sprite. The article was "Sprite not moving in JPanel". All the other articles I found either addressed JPanels without sprites, or animating a sprite. However, I need a JButton that is MouseClicked to simply start the movement, and the ball does not change shape or color. I believe I have the collision part working, but I'm unable to test it until the ball starts moving.
EDIT 2:
LuxxMiner, Thanks for the hints. I have refined my collision portion to be a little more accurate using the getHeight and getWidth methods.
EDIT 3:
MadProgrammer, Thanks...? The problem is not the painting of the ball, I cannot get the ball to move in the first place to repaint it. And the example uses arrow keys, not a mouse click or JButton.
First, take a look at Painting in AWT and Swing and Performing Custom Painting to understand how painting works in Swing.
Let's have a look at the code...
You have a Ball class, which has it's own properties, but then your DrawSprite method passes in values which override these properties?
public class Ball {
Ball() {
}
public int xPos = 25;
public int yPos = 25;
int diameter = 25;
public void DrawSprite(Graphics2D g2, int xPos, int yPos) {
g2.setColor(Color.BLACK);
g2.fillOval(xPos - diameter / 2, yPos - diameter / 2, diameter, diameter);
}
}
What's the point of that? The Ball should paint it's own current state. You should get rid of the additional parameters
public class Ball {
Ball() {
}
public int xPos = 25;
public int yPos = 25;
int diameter = 25;
public void DrawSprite(Graphics2D g2) {
g2.setColor(Color.BLACK);
g2.fillOval(xPos - diameter / 2, yPos - diameter / 2, diameter, diameter);
}
}
ClientClass, Logic and SpriteField all have their own Ball references, none of which is shared so if Logic where to update the state of it's Ball, neither ClientClass or SpriteField would actually see those changes.
In reality, only SpriteField needs an instance of Ball, as it's basically the "ball container", it has the information need to determine if the ball moves out of bounds and wants to know when the ball should be repainted, better to isolate the functionality/responsibility for the Ball to SpriteField at this time.
You also need a means to actually move the ball. While you could use other events, I'd be nice if the ball just moved itself, to this end, you can use a Swing Timer, which won't block the Event Dispatching Thread, but which notifies the registered ActionListener within the context of the EDT, making it safe to update the UI from within.
public class SpriteField extends JPanel {
private Ball mSolo;
private Timer timer;
private int xDelta, yDelta;
public SpriteField() {
mSolo = new Ball();
do {
xDelta = (int) ((Math.random() * 8) - 4);
} while (xDelta == 0);
do {
yDelta = (int) ((Math.random() * 8) - 4);
} while (yDelta == 0);
}
public void start() {
if (timer == null || !timer.isRunning()) {
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
mSolo.xPos += xDelta;
mSolo.yPos += yDelta;
if (mSolo.xPos - (mSolo.diameter / 2) < 0) {
mSolo.xPos = mSolo.diameter / 2;
xDelta *= -1;
} else if (mSolo.xPos + (mSolo.diameter / 2) > getWidth()) {
mSolo.xPos = getWidth() - (mSolo.diameter / 2);
xDelta *= -1;
}
if (mSolo.yPos - (mSolo.diameter / 2) < 0) {
mSolo.yPos = (mSolo.diameter / 2);
yDelta *= -1;
} else if (mSolo.yPos + (mSolo.diameter / 2) > getHeight()) {
mSolo.yPos = getHeight() - (mSolo.diameter / 2);
yDelta *= -1;
}
repaint();
}
});
timer.start();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
mSolo.DrawSprite(g2);
}
}
Now, all you need to do, is when the "Start" button is clicked, call the start method
public class ClientClass {
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClientClass window = new ClientClass();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ClientClass() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
// Logic Logical;
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 590, 520);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SpriteField panel = new SpriteField();
panel.setForeground(Color.WHITE);
panel.setBackground(Color.GRAY);
frame.getContentPane().add(panel);
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
panel.start();
}
});
frame.getContentPane().add(btnStart, BorderLayout.SOUTH);
}
}

New to java can't figure out how to use paint method

I am trying to learn the paint method and get a ball to move across the frame. here is my code so far. w=.
I currently have two classes One is the main and one for the ball.
this is the main class
import java.awt.;
import javax.swing.;
public class PaintTest extends JPanel {
int x = 0;
int y = 0;
public void moveBall(){
x = x + 1;
y = y + 1;
}
public static void main(String[] args){
JFrame frame = new JFrame();
frame.setSize(500,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Ball ball = new Ball(x,y);
while(true){
ball.moveBall();
repaint();
}
}
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.magenta);
g.drawLine(0,100,500,100);
g.drawLine(0,101,500,101);
g.drawLine(0,102,500,102);
g.drawLine(0,103,500,103);
g2.fillOval(x,y,35,35);
}
}
and here is the ball class
public class Ball {
int x,y;
public Ball(int x, int y){
this.x = x;
this.y = y;
}
}
now when i compile I get an error saying cannot find symbol ball in class PaintTest even though I am calling it from the class Ball. I am aware of the repaint error as i do not know what to put in front of it.
Draw in a JPanel
In its paintComponent method not in its paint method -- this gives you double buffering.
Call the super's paintComponent method in your override. This allows the JPanel to do housekeeping drawing including erasing the oval image at its old position.
Don't use a while (true) loop as this can cause serious Swing threading issues. Use a Swing Timer instead.
In the Swing Timer, increment your animation variables and then call repaint(). This will tell Swing to repaint the component which will re-draw the oval in the new location.
Don't guess at this stuff as that leads to frustration since Swing graphics coding is a different beast. Instead check the tutorials. You can find links to the Swing tutorials and to other Swing resources here: Swing Info. Also check out Performing Custom Painting with Swing.
Graphics2D goodies: RenderingHints can be used to smooth out your image jaggies.
More Graphics2D goodies: Stroke can be used to draw thicker lines when needed.
For example:
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.*;
#SuppressWarnings("serial")
public class PaintTest extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 20;
private static final Stroke STROKE = new BasicStroke(5f);
private int x;
private int y;
public PaintTest() {
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// to smooth graphics
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.magenta);
Stroke initialStroke = g2.getStroke();
g2.setStroke(STROKE);
g.drawLine(0, 100, 500, 100);
g2.setStroke(initialStroke);
g2.fillOval(x, y, 35, 35);
}
#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) {
x++;
y++;
repaint();
}
}
private static void createAndShowGui() {
PaintTest mainPanel = new PaintTest();
JFrame frame = new JFrame("PaintTest");
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();
}
});
}
}
You have to put the paintComponent method in a JPanel. You can do it by using something like this.
JPanel panel = new JPanel(){
#Overide
public void paintComponent(Graphics g){
super.paint();
// Draw Stuff Here
}
};
The reason you are not getting the ball to move across the frame is that you are not calling the repaint method. You should do so on a thread.
Thread th = new Thread(new Runnable(){
#Overide
public void run(){
while(frame.isVisible()){
ball.moveBall();
panel.repaint();
try{Thread.sleep(5);}catch(Exception e){e.printStackTrace();}
}
}
});
Also, why are you making ball a instance of the PaintTest class? To get only one frame and ball you would want to add a class named Ball and use that to make an instance:
public class Ball{
int x, y;
public Ball(int x, int y){
this.x = x;
this.y = y;
}
}
That is why you were getting 2 frames.
Then you would want to get rid of the x and y variables in the main class. To make an instance using this class you would do:
Ball ball = new Ball(x, y);
Then to paint the ball in the paintComponent method you would do:
g.fillOval(ball.x, ball.y, 35, 35);
You didn't call the repaint(); method.
You don't need the y + 1 part.
Instead of using the while(true) loop, you should use a for loop.
You didn't call the super.paint() method.
You didn't use any Thread.sleep(), which made the ball move across instantaneously.
Here is the code:
import java.awt.*;
import javax.swing.*;
public class PaintTest extends JFrame {
int x = 8;
int y = 30;
public void moveBall(){
x = x + 1;
//y = y + 1;
try{
Thread.sleep(500);
} catch(InterruptedException e){
}
repaint();
}
public static void main(String[] args){
PaintTest frame1 = new PaintTest();
PaintTest ball = new PaintTest();
for(int i = 0; i<100; i++){
//while(true){
ball.moveBall();
}
}
public PaintTest() {
super("Paint Test");
setSize(500,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public void paint(Graphics g){
Graphics2D g2 = (Graphics2D) g;
super.paint(g);
super.paint(g2);
g.setColor(Color.magenta);
g.drawLine(0,100,500,100);
g.drawLine(0,101,500,101);
g.drawLine(0,102,500,102);
g.drawLine(0,103,500,103);
g.fillOval(x,y,35,35);
}
}
This code will make the ball move across the screen VERY slowly. If you want to speed it up, change the number of miliseconds in the Thread.sleep(miliseconds) part to a smaller number of miliseconds.

Java using Graphics in a Method

Hi guys I'm super new to Java; I've looked around and haven't been able to find an answer to this question. Any chance you could help me?
Here is an example of what I'm trying to achieve.
public class FrameWork extends JFrame implements MouseListener {
... //Irrelevant to the question code
public void mouseClicked(MouseEvent e){
int x = e.getX();
int y = e.getY();
if (x==1 && y==1){
// This is where and when I want to draw GFXDice
}
}}
Now the other class, all imports left out for readability.
public class Board extends JPanel{
Image GFXDice1;
public Board() {
ImageIcon Dice1;
Dice1 = new ImageIcon(this.getClass().getResource("GFX/Dice1"));
GFXDice1 = Dice1.getImage();
}
Now the graphics part
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(GFXDice, 100, 100, null);
}
Now for the question - I want to use the method paint from the Class Board in the Class FrameWork - But can't get it to work - any ideas ? I'm offering a bazillion units of good karma to anyone who has an idea.
The general way to do most Swing drawing is via passive graphics. This means:
Do the drawing itself in the paintComponent(Graphics g) method of a JPanel or JComponent.
In your MouseListener change the state of some of the fields of the class. In your mouseClicked method you are setting the state of some local variables, and I recommend that you instead make your x and y fields, not local.
Then when the mouse listener is done making changes, call repaint() on the JPanel.
Then in the paintComponent method, use those fields that were changed in the mouse listener to do your drawing.
Don't forget to call the super's paintComponent method in your paintComponent override.
Don't forget to read tutorials on Swing Graphics to get the fine points.
Edit
For example, please have a look at a small graphics program that I created for an answer to another recent question.
The drawing occurs in the main class, SpaceShip, which extends JPanel. I add an anonymous inner MouseAdapter class for my Mouse Listener, and inside of the MouseAdapter, I call a method called moveIt, passing in the MouseEvent object.
MouseAdapter myMouseAdapter = new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
moveIt(evt);
count = count + 1;
}
#Override
public void mouseDragged(MouseEvent evt) {
moveIt(evt);
}
};
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
All moveIt(MouseEvent evt) does is to change the state of two fields, myX and myY, and then calls repaint() on the current class:
public void moveIt(MouseEvent evt) {
myY = evt.getY() - sprite.getHeight() / 2;
myX = evt.getX() - sprite.getWidth() / 2;
repaint();
}
And then in the class's paintComponent method, I first call the super's paintComponent to allow it to erase any previous old out of date images, then I paint a background image, background, then I draw a sprite that uses the myX and myY variables to tell it where to draw, then I draw some yellow rectangles at locations that are determined by the JPanel's size:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
font1 = new Font("Serif", Font.BOLD, 36);
g.drawImage(background, 0, 0, this);
g.drawImage(sprite, myX, myY, this);
g.setColor(Color.yellow);
int rectCount = 10;
int height = getHeight() / rectCount;
int width = 272;
int x = getWidth() - width;
for (int i = 0; i < rectCount; i++) {
int y = i * height;
g.drawRect(x, y, width, height);
}
g.setFont(font1);
g.drawString(Integer.toString(count), 500, 100);
}
The whole thing looks like this:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.awt.Graphics;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.io.IOException;
import java.net.URL;
import java.lang.String;
import java.awt.Font;
#SuppressWarnings("serial")
public class SpaceShip extends JPanel {
private static final String BACKGROUND_PATH = "http://www.thatsreallypossible.com/"
+ "wp-content/uploads/2012/12/Space-Colonialisation.jpg";
private static final String SPRITE_PATH = "http://www.pd4pic.com/"
+ "images250_/ufo-flying-saucer-spacecraft-spaceship-alien.png";
private Font font1;
int myX = 100;
int myY = 400;
int count = 0;
private BufferedImage background;
private BufferedImage sprite;
public SpaceShip() throws IOException {
URL backgroundUrl = new URL(BACKGROUND_PATH);
URL spriteUrl = new URL(SPRITE_PATH);
background = ImageIO.read(backgroundUrl);
sprite = ImageIO.read(spriteUrl);
MouseAdapter myMouseAdapter = new MouseAdapter() {
public void mousePressed(MouseEvent evt) {
moveIt(evt);
count = count + 1;
}
#Override
public void mouseDragged(MouseEvent evt) {
moveIt(evt);
}
};
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
#Override
public Dimension getPreferredSize() {
if (background != null) {
return new Dimension(background.getWidth(), background.getHeight());
}
return super.getPreferredSize();
}
public void moveIt(MouseEvent evt) {
myY = evt.getY() - sprite.getHeight() / 2;
myX = evt.getX() - sprite.getWidth() / 2;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
font1 = new Font("Serif", Font.BOLD, 36);
g.drawImage(background, 0, 0, this);
g.drawImage(sprite, myX, myY, this);
g.setColor(Color.yellow);
int rectCount = 10;
int height = getHeight() / rectCount;
int width = 272;
int x = getWidth() - width;
for (int i = 0; i < rectCount; i++) {
int y = i * height;
g.drawRect(x, y, width, height);
}
g.setFont(font1);
g.drawString(Integer.toString(count), 500, 100);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Basic Game");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
SpaceShip ex;
try {
ex = new SpaceShip();
frame.getContentPane().add(ex);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
ex.requestFocus();
} catch (IOException e) {
e.printStackTrace();
}
}
}

Categories

Resources