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.
Related
as part of a school project we have to create a little game using Applets. I'm working on some tests right now but there's one thing I can't quite figure out:
I want to have multiple objects flying on my screen at the same time on my Applet screen. The animation effect is created by drawing the object, deleting it then moving it after a while.
Here's my code:
Robotworld class
package core;
import items.Obstacle;
import java.applet.Applet;
import java.awt.*;
import java.util.ArrayList;
public class Roboterwelt extends Applet {
private ArrayList<Obstacle> obstacles = new ArrayList<>();
#Override
public void init() {
setSize(600, 600);
Graphics g = getGraphics();
g.setColor(Color.BLACK);
for(int x = 0; x < 5; x++) {
Obstacle h = new Obstacle((x+1)*100, 100, g, this);
obstacles.add(h);
Thread t = new Thread(h);
t.start();
}
}
#Override
public void paint(Graphics g) {
for(Obstacle o : obstacles) {
o.draw();
}
}
}
Obstacle class
package items;
import java.applet.Applet;
import java.awt.*;
public class Obstacle implements Runnable {
private int x;
private int y;
private Graphics g;
public Hindernis(int x, int y, Graphics g) {
this.x = x;
this.y = y;
this.g = g;
}
public void draw() {
g.drawOval(x, y, 50, 50); //Draw obstacle
}
//Deleting the obstacle by covering it with a white circle
public void delete() {
g.setColor(Color.WHITE); //Change the color to white
g.fillOval(x-5,y-5,60,60); //Making it a bit bigger than the obstacle to fully cover it
g.setColor(Color.BLACK); //Reset the color to black
}
#Override
public void run() {
try {
while(y < 600) {
delete();
y += 10;
draw();
Thread.sleep(1000);
}
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
The problem is the part where I change the color of the Graphics object to cover the circle in white. When I have multiple threads running to represent the multiple obstacles on my screen and redrawing AND deleting happens concurrently, a thread gets interrupted after changing the color to white and draws a filled oval with the Graphics object which color was set to black by another thread that ran the delete() method to the end.
How can I force the program to not interrupt the delete() method between the color change to white and the drawing of the filled oval shape?
Disclaimer
Applet is deprecated, it is no longer supported by browsers, Oracle or the community. It would be unprofessional of me to try and encourage you to keep using them.
I appreciate that this is a "school" assignment, but perhaps it's time your instructor caught up with the rest of the world and started using something which doesn't actual cause more issues then it solves (hint JavaFX) - IMHO
Answer...
Don't use getGraphics, this is not how custom painting should be done. Painting should be done within the confines of the paint methods. Take a look at Painting in AWT and Swing for details. Apart from solving your immediate issue, your current approach risks been "wiped" clean when the applet repaints itself.
Overriding paint of the top level containers like Applet is a bad idea. Apart from locking you into a single use case, they aren't double buffered, which will cause flickering when painting occurs. The simplest solution is to start with a JPanel, which is double buffered and which can be added to what ever container you want to use.
You don't need multiple threads. Thread is a bit of an art form. More threads doesn't always mean more work gets done and can actually degrade the performance of the system. In your case you want to "update" the state in a single pass and then schedule a paint pass, so that the operations are synchronised in a single step and you don't end up with "dirty" updates
The following example simple makes use of Swing, which is based on AWT. It uses a JFrame instead of an Applet, but the concept is easily transferable, because the core functionality is based on a JPanel, so you can add it to what ever you want.
It makes use of a Swing Timer, which basically schedules a callback on a regular bases, but does it in away which makes it safe to update the state of the UI from (this replaces your Thread).
By using paintComponent to paint the Obstacles, we get two things for free.
Double buffering, so no more flickering
The Graphics context is automatically prepared for us, we don't need to "delete" the objects first, we simply paint the current state
The example also removes the Obstacle once it passes the edge of the panel, so you don't waste time trying to move/paint it when it's no longer visible.
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 java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<Obstacle> obstacles;
public TestPane() {
Color[] colors = new Color[]{Color.RED, Color.GREEN, Color.BLUE, Color.MAGENTA, Color.YELLOW};
obstacles = new ArrayList<>(10);
int y = 0;
for (int index = 0; index < 5; index++) {
y += 55;
Obstacle obstacle = new Obstacle(y, 0, colors[index]);
obstacles.add(obstacle);
}
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Iterator<Obstacle> it = obstacles.iterator();
while (it.hasNext()) {
Obstacle ob = it.next();
if (ob.move(getSize())) {
it.remove();
}
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Iterator<Obstacle> it = obstacles.iterator();
while (it.hasNext()) {
Obstacle ob = it.next();
ob.paint(g2d);
}
g2d.dispose();
}
}
public class Obstacle {
private int x, y;
private Color color;
public Obstacle(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void paint(Graphics2D g2d) {
g2d.setColor(color);
g2d.fillRect(x, y, 50, 50);
}
public boolean move(Dimension size) {
y += 1;
return y > size.height;
}
}
}
But all the Obstacles move at the same rate!
Yeah, that's because you used a single delta. If you want the Obstacles to move at different rates, then change the deltas, for example...
public static class Obstacle {
private static Random RND = new Random();
private int x, y;
private Color color;
private int yDelta;
public Obstacle(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
yDelta = RND.nextInt(5) + 1;
}
public void paint(Graphics2D g2d) {
g2d.setColor(color);
g2d.fillRect(x, y, 50, 50);
}
public boolean move(Dimension size) {
y += yDelta;
return y > size.height;
}
}
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;
}
}
}
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);
}
});
}
}
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();
}
}
}
I'm making a Java game. I have a Board class which extends JPanel, and have painted a node jim, which is an object instance of Ball class, to the screen using the paintComponent(Graphics g) {. I can move the Ball object around on the JPanel using arrow keys.
Instead of representing the Ball object as a red square, I would like to have an image loaded.
Board class extends JPanel:
jim = new Ball(0, 0, 50, 50, "jim");
Ball class:
public class Ball extends Component {
int x, y, w, h;
BufferedImage jimImg;
public Ball(int xLoc, int yLoc, int width, int height, String imgtype) throws IOException {
x = xLoc;
y = yLoc;
w = width;
h = height;
jimImg = ImageIO.read(new File("resources/Jim.png"));
loadImages(imgtype);
}
public void paint(Graphics g) {
g.drawImage(jimImg, 200, 0, null);
}
It's not drawing on top of the red square though, or even appearing at all.
I have also tried appending the image as a JLabel... here's code within the Ball class:
public void loadImages(String imgType) throws IOException {
if (imgType == "jim") {
JLabel utc = new JLabel("test");
ImageIcon utcImg = new ImageIcon("resources/Miller.png");
utc.setIcon(utcImg);
}
}
But since Ball class is not a JPanel, I can't add the label to the object.
Don't compare Strings with ==, use equals
if (imgType == "jim") ==>> if ("jim".eqauls(imgType))
You should be reading from a URL and not a File object. A URL can be obtained by using getClass().getResource(). e.g.
jimImg = ImageIO.read(
Ball.class.getResource("/resources/Jim.png"));
You should use a try/catch in the constructor, so you won't have to later when you instantiate the Ball
It's preffered to paint on JPanel and override its paintCompoent, or JComponet
You never actually use the variable taken from your constructor to use to paint`
g.drawImage(jimImg, x, y, w, h, this);
When working with painting, you should override the getPreferredSize of the JPanel so the frame will size it accordingly.
Here's a running example that works. Keep in mind my file structure look like
ProjectRoot/src/resources/image.png
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Ball extends JPanel {
int x, y, w, h;
BufferedImage jimImg;
public Ball(int xLoc, int yLoc, int width, int height) {
x = xLoc;
y = yLoc;
w = width;
h = height;
try {
jimImg = ImageIO.read(
Ball.class.getResource("/resources/stackoverflow5.png"));
System.out.println(jimImg);
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(jimImg, x, y, w, h, this);
}
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new Ball(50, 50, 100, 100));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}
}
EDIT in response to OP commment
"the way my classes are structured: Main class adds JFrame, where I add a new Board class object to the JFrame (Board class extends JPanel). Then I have Board class extends JPanel, which adds a new Ball object and has keyboard methods, etc... so I can move the Ball around. Then Ball Class is just a node with get/set methods getX(), setX() etc. So I can't add an image to the Ball class without extending JPanel?"
It doesn't look like you need ball to be a Component at all. Instead of making the ball a compoent, just make it a regular class. And in the paintComponent of your Board JPanel, just call ball.paint() for each ball you have.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Board extends JPanel {
Ball ball = new Ball(50, 50, 200, 200);
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
ball.drawBall(g);
}
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new Board());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}
}
class Ball {
int x, y, w, h;
BufferedImage jimImg;
public Ball(int xLoc, int yLoc, int width, int height) {
x = xLoc;
y = yLoc;
w = width;
h = height;
try {
jimImg = ImageIO.read(
Board.class.getResource("/resources/stackoverflow5.png"));
System.out.println(jimImg);
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void drawBall(Graphics g) {
g.drawImage(jimImg, x, y, w, h, null);
}
}