PaintComponent is not being called-Java - java

My code is supposed to draw a random sized image three times in random locations. For some reason, when I run this code using BlueJ, all that shows up is a gray screen. I think it is because PaintComponent isn't being called, but I am not quite sure. What went wrong with my code and how can I fix it?
class PanelHolder extends JPanel{//class PanelHolder extends JPanel
//variables
public boolean threesharks;
public int xcoord;
public int ycoord;
public int ratio;
public Image i;
public int w;
public int h;
public boolean background=true;
Color water = new Color(136, 180, 231);
public PanelHolder(){//constructor
i = Toolkit.getDefaultToolkit().getImage("$harkz.png");
}
public void randomxy(){
for(int x=0;x<3;x++){
threesharks=true;
ycoord=(int)(Math.random()*300+200);//use math.random to figure out coordinates and sizing
xcoord=(int)(Math.random()*1000+0);//make a loop
ratio=(int)(Math.random()*5+1);
w=ratio*523;
h=ratio*195;
repaint();
System.out.println("I'm in randomxy");
//call repaint() each time
//after three times, make threesharks=false
}
threesharks=false;
}
public void paintComponent(Graphics g){
if(threesharks){
setBackground(water);
System.out.print("hi!");
if(background){
super.paintComponent(g);//set backgroun
background=false;
}
g.drawImage(i, xcoord, ycoord, w, h, this);
}
}
}

You seem to have a misunderstanding with how painting works in Swing. Swing will call your paintComponent when ever it thinks your component needs to be repainted, this might occur for many different reasons, many of which you don't have control over.
Painting in Swing is destructive, that is, each time your paintComponent method is called, you are expected to repaint the entire state of the component from scratch, painting is not accumalitive.
This means that you need to store the state of things you want to paint in some meaningful manner and re-use these values are needed.
Have a look at Painting in AWT and Swing and Performing Custom Painting for more details about how painting works in Swing
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
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 PanelHolder());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
class PanelHolder extends JPanel {//class PanelHolder extends JPanel
//variables
public boolean threesharks;
public BufferedImage i;
public boolean background = true;
Color water = new Color(136, 180, 231);
private Point[] points;
private Image[] images;
public PanelHolder() {
//constructor
try {
i = ImageIO.read(...);
} catch (IOException ex) {
ex.printStackTrace();
}
randomxy();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
public void randomxy() {
points = new Point[3];
images = new Image[3];
for (int x = 0; x < 3; x++) {
points[x] = new Point();
double ratio = (Math.random() * 6d) + 0.1d;
int width = (int) (ratio * i.getWidth());
int height = (int) (ratio * i.getHeight());
points[x].y = Math.max(0, (int) (Math.random() * 800) - height);//use math.random to figure out coordinates and sizing
points[x].x = Math.max(0, (int) (Math.random() * 800) - width);//make a loop
images[x] = i.getScaledInstance(width, height, Image.SCALE_SMOOTH);
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);//set backgroun
g.setColor(water);
g.fillRect(0, 0, getWidth(), getHeight());
if (points != null && images != null) {
for (int index = 0; index < points.length; index++) {
g.drawImage(images[index], points[index].x, points[index].y, this);
}
}
}
}
}
This is a rough example, which uses Image#getScaledInstance which is not generally recommended, but works for the example.
Have a look at The Perils of Image.getScaledInstance() for more details
Have a look at Quality of Image after resize very low -- Java and Java: maintaining aspect ratio of JPanel background image for possible (scaling) alternatives
I'd also have a look at Reading/Loading an Image for a better mechanism for loading images

Related

flickering my jframe even i used doubleBuffer

when I call a repaint method its flickering, I was searching on the internet about doubleBuffered but its stil flickering all of objects on the same time,
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Random;
public class GuiGame extends JFrame implements ActionListener {
private final Flower flower;
private final ArrayList<MyObjects> objekty;
private Image dbImage;
private Graphics dbGraphics;
public GuiGame() {
this.setFocusable(true);
Timer timer = new Timer(40, this);
timer.start();
this.setVisible(true);
/*
public void paint(Graphics g) {
this.dbImage = this.createImage(this.getWidth(), this.getHeight());
this.dbGraphics = this.dbImage.getGraphics();
this.paintComponents(g);
g.drawImage(this.dbImage, 0 ,0, this);
}
*/
public void paint(Graphics g) {
//when i use doubleBuffering this method was called paintComponents
g.drawImage(background.getImage(), 0, 0, this);
g.drawImage(flower.getImage(), flower.getPozX(), flower.getPozY(), this);
String skore = "Score: " + player.getScore() ;
g.drawString(skore, 20, 50);
this.paintObjects(g);
}
public void paintObjects(Graphics g) {
if (this.objekty != null) {
for (objekty o : this.objekty) {
o.move();
g.drawImage(o.getImage(), o.getPozX(), o.getPozY(), this);
}
}
}
when I used doubleBuffering I tried to slow down a timer e.g. to 1000, it was blank page for most of the time and my objects was there only for moment.
when I do not used it, only last objects which i draw were flickering.
how can I avoid?
I would recommend you start by taking a look at Painting in AWT and Swing and Performing Custom Painting.
Swing components, when used correctly, are double buffered by default.
Painting is very complex, there's a lot of work that goes into a paint pass. When you override a "paint" method, you either need to honour the existing workflow (ie, call the super.paintXxx method) or be prepared to take over the full responsibility of the method. Your code has circumvented whole sections of the paint workflow, this is never a good idea.
It's not generally recommend that you extend from top level containers like JFrame, you're not adding any new functionality and they are generally complex components to start with. JFrame for example, is actually a composite component, that is, it has a number of child components added to it that form it's core functionality
By overriding paint (of JFrame), you start competing with these components, and because a child component can be painted without the parent been involved, this can lead to any number of issues.
Instead, start with a JPanel and override it's paintComponent method, this guarantees that you will be operating in a double buffered workflow.
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.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class MainPane extends JPanel {
private int xDelta = 1;
private int xPos = 0;
private BufferedImage ufo;
public MainPane() throws IOException {
ufo = ImageIO.read(getClass().getResource("/images/ufo.png"));
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += xDelta;
if (xPos + ufo.getWidth() > getWidth()) {
xPos = getWidth() - ufo.getWidth();
xDelta *= -1;
} else if (xPos < 0) {
xPos = 0;
xDelta *= -1;
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
paintUFO(g2d);
g2d.dispose();
}
protected void paintUFO(Graphics2D g2d) {
int y = (getHeight() - ufo.getHeight()) / 2;
g2d.drawImage(ufo, xPos, y, this);
}
}
}

How can I add more than one paintComponent() to a frame?

So this is my main class:
package testgame;
import java.awt.EventQueue;
import javax.swing.JFrame;
public class Game extends JFrame {
public static JFrame frame = new JFrame("Just a test!");
public static void LoadUI() {
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(550, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true); }
public static void main(String[] args) {
LoadUI();
frame.add(new Circles());
}
}
And this is the class that handles what I want to paint:
package testgame;
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.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Circles extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawBubbles(g); }
public void drawBubbles(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
RenderingHints rh
= new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(rh);
int x, y, size;
x = (int) (Math.random() * 500) + 15;
y = (int) (Math.random() * 450) + 15;
size = (int) (Math.random() * 50) + 25;
g2d.setColor(Color.GREEN);
g2d.drawOval(x, y, size, size);
g2d.fillOval(x, y, size, size); }
}
If I add another
frame.add(new Circles());
Nothing happens. I think it has to do with the layout of the frame, but the coordinates of the bubbles are random so I'm not sure how to work with this.
In this case I'm using a fixed-size array of 5, you may change it to a bigger fixed-size array or an ArrayList, as shown in this answer
For your particular case I would create a Circle class that may contain the data for each circle, being the coords and the size
Then create a CirclePane class that would paint all the Circles in a single paintComponent() method.
And finally, the Main class that would have a JFrame that may contain the CirclePane added to it.
With the above tips in mind, you could end up with something like this:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CircleDrawer {
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new CircleDrawer()::createAndShowGui); //We place our program on the EDT
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
CirclePane circle = new CirclePane(5); //We want to create 5 circles, we may want to add more so we change it to 10, or whatever number we want
frame.add(circle);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//Data class
class Circle {
private Point coords;
private int size;
public Circle(Point coords, int size) {
this.coords = coords;
this.size = size;
}
public Point getCoords() {
return coords;
}
public void setCoords(Point coords) {
this.coords = coords;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
}
//The drawing class
#SuppressWarnings("serial")
class CirclePane extends JPanel {
private int numberOfCircles;
private Circle[] circles;
public CirclePane(int numberOfCircles) {
this.numberOfCircles = numberOfCircles;
circles = new Circle[numberOfCircles];
for (int i = 0; i < numberOfCircles; i++) {
Point coords = new Point((int) (Math.random() * 500) + 15, (int) (Math.random() * 450) + 15); //We generate random coords
int size = (int) (Math.random() * 50) + 25; //And random sizes
circles[i] = new Circle(coords, size); //Finally we create a new Circle with these properties
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (int i = 0; i < numberOfCircles; i++) {
g2d.draw(new Ellipse2D.Double(circles[i].getCoords().getX(), circles[i].getCoords().getY(), circles[i].getSize(), circles[i].getSize())); //We iterate over each circle in the array and paint it according to its coords and sizes
}
}
#Override
public Dimension getPreferredSize() { //Never call JFrame.setSize(), instead override this method and call JFrame.pack()
return new Dimension(500, 500);
}
}
}
Which produces a similar output to this:
I hope this helps you to get a better idea, read about the MVC pattern as I made use of it for this answer.
Note:
In this answer I used the Shapes API, according to the recommendation of #MadProgrammer in this other answer. I used it in the g2d.draw(...) line.
For a deeper understanding in how custom painting works in Swing, check Oracle's Lesson: Performing Custom Painting and Painting in AWT and Swing tutorials.

Moving an image to the position of the cursor where is clicked on a JFrame

i need some help here with an exercise of my Java class. What i'm trying to do is that when i click on any part of the JFrame, an image moves closer to the pointer. Right now i have done the part that once i click on the JFrame the image is in the same position as the pointer but it does like it's "teleporting" and i'm trying to make it more like a constant movement to the pointer position.
So far this is my code i have atm:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ControlaRaton extends MouseAdapter {
JLabel label;
public ControlaRaton(JLabel label){
this.label = label;
}
public void mouseClicked(MouseEvent evt){
Point pos = evt.getPoint();
System.out.println(pos.x+" "+pos.y);
//System.out.println("Boton: "+evt.getButton());
label.setLocation(pos.x-20,pos.y-50);
}
}
Any ideas on how to do it that way? I was thinking maybe using a Thread but i don't know exactly how to implement it here :s
This is pretty simple approach, basically all this does is uses a Swing Timer to move an entity towards the last known click point
Have a look at
Concurrency in Swing
How to use Swing Timers
How to Write a Mouse Listener
for more details
Swing is NOT thread safe, when performing these types of operations, it's important to take this into consideration. The Swing API provides several ways to work threads, in this case, I've used a simple Timer as it generates updates at a regular interval and triggers updates from within the EDT, making it safe to update the UI from within
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MoveTowards {
public static void main(String[] args) {
new MoveTowards();
}
public MoveTowards() {
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 MoveTowardsPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
class MoveTowardsPane extends JPanel {
private final BufferedImage image;
private Point imagePosition = new Point(150, 150);
private Point mousePoint;
private double imageAngleRad = 0;
public MoveTowardsPane() {
BufferedImage i = null;
try {
i = ImageIO.read(getClass().getResource("/sprite.png"));
} catch (IOException e) {
e.printStackTrace();
}
image = i;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
mousePoint = e.getPoint();
double dx = e.getX() - imagePosition.getX();
double dy = e.getY() - imagePosition.getY();
imageAngleRad = Math.atan2(dy, dx);
repaint();
}
});
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (mousePoint != null) {
int centerX = imagePosition.x + (image.getWidth() / 2);
int centerY = imagePosition.y + (image.getHeight() / 2);
if (mousePoint.x != centerX) {
imagePosition.x += mousePoint.x < centerX ? -1 : 1;
}
if (mousePoint.y != centerY) {
imagePosition.y += mousePoint.y < centerY ? -1 : 1;
}
repaint();
}
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics gr) {
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr.create();
g.setRenderingHint(
RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
int cx = image.getWidth() / 2;
int cy = image.getHeight() / 2;
AffineTransform oldAT = g.getTransform();
g.translate(cx + imagePosition.x, cy + imagePosition.y);
g.rotate(imageAngleRad);
g.translate(-cx, -cy);
g.drawImage(image, 0, 0, null);
g.setTransform(oldAT);
g.dispose();
}
}
}
Why doesn't this use a JLabel? A lot of reasons, JLabel isn't really well fitted for the task, as it needs to take into account a lot of other information. This example also "turns" the sprite towards the click point, something which isn't easily achieved with a JLabel.
In principle, the theory is still the same for moving a component.
See
Painting in AWT and Swing
Performing Custom Painting
for more details about how this approach works
Redraw your circle on mouse move
void mouseMoved(MouseEvent evt){
Point pos = evt.getPoint();
System.out.println(pos.x+" "+pos.y);
//System.out.println("Boton: "+evt.getButton());
label.setLocation(pos.x-20,pos.y-50);
}
});
If you want to move the label just a bit with each click you could do the following:
public void mouseClicked(MouseEvent evt){
Point clickedPos = evt.getPoint();
Point newPosForLabel = calculateNewPos(clickedPos, labelPos);
label.setLocation(newPosForLabel);
}
private Point calculateNewPos(Point clickedPos, Point labelPos) {
// calculate newPos based on labelPos and clickedPos
return newPos;
}
else use the timer from Hannes or from the link given by MadProgrammer:

Graphics2D and Jpanel Query: Easier Way?

Is there an easier way to code my program such that I can draw my tile-based map onto a Panel (of some sort), such that the map wont redraw each time I resize the window (with resizable off)? I realize that that is great for debugging and testing my mapDrawing function, but, I also don't think I'm doing it ideally, or even in a smart way at all.
My code is as follows.. if you need my subclasses for some reason, I can edit those in too.
import java.awt.*;
import javax.swing.*;
public class AhnkorMyst extends JPanel { // main game class
static final int screenWidth = 760;
static final int screenHeight = 760;
public void paintComponent(Graphics g) {
super.paintComponent(g); // paint background
setBackground(Color.BLACK);
Graphics2D g2d = (Graphics2D) g;
Map newMap = new Map(g2d, screenWidth, screenHeight);
newMap.generateBaseMap();
newMap.populateSurroundings();
newMap.quadSmoothingIteration ();
int i, j;
for (j = 0; j < (newMap.mapHeight / 20); j++) {
for (i = 0; i < (newMap.mapWidth / 20); i++) {
newMap.mainMap[i][j].paint();
}
}
}
public static void main (String[] args) {
AhnkorMyst game = new AhnkorMyst();
JFrame frame = new JFrame("Ahnkor Myst");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(game);
frame.setSize(screenWidth + 10, screenHeight + 30);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setResizable(false);
}
}
edit** my Map is randomly generated with the generateBaseMap () function.
This is "very" basic example of the concept. Basically, this re-builds the BufferedImage which represents the basic "view" of the map every time the JPanel is invalidated.
You should note, that I simple randomise the map each time it is built, presumably, you will be using some kind of virtual structure which defines the map itself and would use this to build the map instead...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestTiles {
public static void main(String[] args) {
new TestTiles();
}
public TestTiles() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TileMap());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TileMap extends JPanel {
private int tileColumns = 8;
private int tileRows = 8;
private BufferedImage tileSheet;
private BufferedImage tileMap;
public TileMap() {
try {
tileSheet = ImageIO.read(getClass().getResource("/TileSet.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void invalidate() {
tileMap = null;
super.invalidate();
}
protected void buildMap() {
tileMap = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = tileMap.createGraphics();
int tileWidth = tileSheet.getWidth() / tileColumns;
int tileHeight = tileSheet.getHeight() / tileRows;
Random random = new Random();
for (int x = 0; x < getWidth(); x += tileWidth) {
for (int y = 0; y < getHeight(); y += tileHeight) {
int xCell = random.nextInt(tileColumns - 1) * tileWidth;
int yCell = random.nextInt(tileRows - 1) * tileHeight;
BufferedImage tile = tileSheet.getSubimage(xCell, yCell, tileWidth, tileHeight);
g2d.drawImage(tile, x, y, this);
}
}
g2d.dispose();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (tileSheet != null) {
Graphics2D g2d = (Graphics2D) g.create();
if (tileMap == null) {
buildMap();
}
g2d.drawImage(tileMap, 0, 0, this);
g2d.dispose();
}
}
}
}
You could take this concept further and pre-generate the entire world into a single BufferedImage and use getSubImage to grab a smaller portion which what you want to display. This starts to form the basic concept of scrolling, as you could maintain a virtual position in the world and calculate what portion of the map would need to be shown to represent it...
Avoid lengthy calculations and instantiations in your implementation of paintComponent(). You can get an idea of the available rendering budget on your target platform using the approach shown in this AnimationTest. Instead, pre-compute as much as possible. In this tile example, the ground map is entirely static, and the rendering is handled by paintIcon(). A related example is examined here.

Image animation not getting displayed

I have the following code.I want to display the images in my chick array on the screen after some time delay.The coordinates have to be shifted by 10px each x direction when the new
image is loaded.I would like to know what additional code snippets can i add in this code to have an animation on my Frame with the two images i have .Below is my code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
public class Chicken extends Frame implements Runnable{
Thread animation;
long frameDelay = 3000;
Image chick[] = new Image[2];
int numFrames = chick.length;
Toolkit tk = getToolkit();
public Chicken()
{
setSize(new Dimension(300,300));
setVisible(true);
setBackground(Color.BLACK);
animation = new Thread(this);
chick[0] = tk.createImage("stand.png");
chick[1] = tk.createImage("walk.png");
animation.start();
//setVisible(false);
}
public void paint(Graphics g)
{
g.drawImage(chick[0],100,100,null );
}
#Override
public void run() {
// TODO Auto-generated method stub
repaint();
try {
Thread.sleep(frameDelay);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String args[])
{
Chicken instance = new Chicken();
}
}
So, the first problem you have is a resource issue.
Resources stored "within" the application (typically known as embedded resources) can't be loaded like external resources.
chick[0] = tk.createImage("stand.png");
Is expecting a file "./stand.png" which doesn't exist. Instead, you need to load the resource through the Class#getResource API...
chick[0] = tk.createImage(getClass().getResource("/stand.png"));
The second problem you will face is the fact that you are overriding paint of a top level container. This really shouldn't be done. Let's start with the fact it's not double buffered and end with the fact that frames have decorations which sit inside the viewable area. This means that the decorations will overlap what ever you paint to the surface...not pretty...
The third problem is you are not telling the image where it should move to. It's static.
You need some kind of x/y value that tells the image where it should be painted. You would modify these values by a given x/y delta within you thread before you called repaint...
The forth problem you might have is the fact that you are using AWT...which is kind of dated. Swing would solve your double buffering issue for you...IMHO, would make a better choice - there's a lot more documentation and examples on Swing laying around now days ;)
While I'm on my hobble horse...I would, personally, recommend ImageIO over Toolkit#createImage or ImageIcon, mostly because it supports more formats, but also because it will throw an Exception when the image can't be read for some reason...
I have a simple example if Swing, but I won't post it, because I'll get in trouble for running of topic...let me know if you would like to see it
Updated with Swing example
This uses a embedded image in the default package...
import java.awt.BorderLayout;
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.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ChickenDance {
public static void main(String[] args) {
new ChickenDance();
}
public ChickenDance() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage chicken;
private int xPos;
private int yPos;
private int xDelta = 4;
public TestPane() {
try {
chicken = ImageIO.read(getClass().getResource("/Chicken.png"));
} catch (IOException ex) {
Logger.getLogger(ChickenDance.class.getName()).log(Level.SEVERE, null, ex);
}
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += xDelta;
if (xPos + chicken.getWidth() > getWidth()) {
xPos = getWidth() - chicken.getWidth();
xDelta *= -1;
} else if (xPos < 0) {
xPos = 0;
xDelta *= -1;
}
yPos = (getHeight() - chicken.getHeight()) / 2;
repaint();
}
});
if (chicken != null) {
timer.start();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (chicken != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(chicken, xPos, yPos, this);
g2d.dispose();
}
}
}
}

Categories

Resources