I am making a program that takes a two dimensional integer array and uses its data to draw tiles to the screen in the arrangement specified in the array. Without modifying any of the code, the program will execute fine about 4 out of 5 times. Other times the custom JPanel will not display anything. After inserting system.out.print() in various places I have determined that it is caused by the paintComponent method not being called when nothing is displayed. Obviously it is called when the tiles are displayed perfectly. I can't seem to find the source of this inconsistency. Why would it work the majority of the time and not every once in a while?
Its called Isopanel because it will eventually display tiles in an isometric formation.
0s equate to water tiles and 1s equate to sand tiles.
JPanel Class
public class IsoPanel extends JPanel
{
private ArrayList <BufferedImage> tiles;
private int[][] leveldata =
{
{0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,0},
{0,0,0,0,0,0,0,0,0,0}
};
public IsoPanel()
{
tiles = new ArrayList<BufferedImage>();
tiles.add(Frame.loadImage("water.png"));
tiles.add(Frame.loadImage("sand.png"));
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
for (int i=0; i<10; i++)
{
for (int j=0; j<10; j++)
{
int x = j * 50;
int y = i * 50;
int tileType = leveldata[i][j];
placeTile(tileType, x, y, g);
}
}
}
public void placeTile (int tile,int x,int y, Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(tiles.get(tile), null, x, y);
}
}
and JFrame class:
public class Frame extends JFrame
{
public Frame()
{
super ("Iso");
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(0,0,screenSize.width, screenSize.height);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setVisible(true);
BorderLayout bord = new BorderLayout();
setLayout(bord);
IsoPanel iso = new IsoPanel();
add(iso,BorderLayout.CENTER);
GridLayout grid = new GridLayout(1,1);
iso.setLayout(grid);
iso.setVisible(true);
}
public static BufferedImage loadImage(String filename)
{
{
try
{
return ImageIO.read(new File(System.getProperty( "user.dir" )+"/src/"+filename));
}
catch(IOException e)
{
}
}
return null;
}
public static void main(String[] args)
{
Frame one = new Frame();
}
}
The main issue is the fact that you are calling setVisible on your frame before you've finished initialising the child components. This is a known issue with how frame prepares it's state...
So, instead of...
public Frame()
{
/*...*/
setVisible(true);
/*...*/
add(iso,BorderLayout.CENTER);
}
Try...
public Frame()
{
/*...*/
add(iso,BorderLayout.CENTER);
/*...*/
setVisible(true);
}
Additional...
You shouldn't be throwing away your exceptions. At the very least, you should be printing the exception or logging it.
You should be using an ImageObsever when drawing images. Instead of g2.drawImage(tiles.get(tile), null, x, y);, you should try using g2.drawImage(tiles.get(tile), x, y, this);. Images aren't always in state to be rendered immediately, this provides a means for the component to react to changes in the image state and automatically repaint themselves...
Even though you are setting the size of the parent frame, your IsoPanel component should be providing layout hints in the form of overriding getPreferredSize, allowing you to simply pack the main window. This discounts the possibility of different frame border sizes on different platforms and look and feel settings.
You may wish to take a look at Initial Threads and the important of using EventQueue.invokeLater to launch your UI
System.getProperty( "user.dir" )+"/src/"+filename) looks like it should be referencing an embedded resource...
Related
I have a problem with a Java 2d game I'm developing.
Some of the game screen elements are drawn in a Jpanel overriding the paint() method and some other elements are Jlabels added to that panel.
The problem is that the elements drawn in the paint() method are always up to date (I recall a Jpanel.repaint() every 30ms from a thread) but the Jlabels often are not shown or are blinking in a random way.
I've searched a lot but couldn't find a way to update all the elements at the same time.
This is the core structure of the code:
Main Class
final class ATC_sim {
// ...
public CustomLabel[] label;
// CustomLabel is just a class in which are organized differents JLabels in
// a certain relative position.
// ...
private ATC_sim(){
initGui();
initGame();
}
private void initGui(){
frame = new JFrame();
pGame = new GamePanel(this);
for(int i=0; i< label.length; i++){
pGame.add(label[i]);
}
frame.add(pGame);
// ...
}
private void initGame(){
loop = new Loop(this, 30);
thread = new Thread(loop);
thread.start();
pGame.repaint();
}
// method recalled every 30ms from thread(loop)
public void updateGame(){
// do the math of the game (in which I also move the position of labels)
// ...
for(int i=0; i< label.length; i++){
label[i].update(); // this method update text in Jlabels and position of label.
}
pGame.repaint();
}
}
GamePanel class
public class GamePanel extends JPanel {
// ...
public GamePanel(ATC_sim a) {
this.atc = a;
// ...
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
// do some drawings according to some variable calculated in updateGame()
}
}
At first, if you want better performance and a lower chance of lagging/stuttering, use paintImmediately() instead of repaint().
It could be, that this solves your problem already.
The second part is: Do you use double buffering? If you use this, this problem will definitely disappear (in my oppinion).
Hello fellow programmers,
I've ran into a little issue in my code that I can't seem to crack. It has to do with the Jframe; Graphics area of Java. The code that I'll post below, is over a drawing method. Which purpose is to draw the "rooms" that are in a ArrayList roomList which is located in another class hence lvl. before. This off-course doesn't happen, hence the post on here.
public class LevelGUI implements Observer {
private Level lv;
private Display d;
public LevelGUI(Level level, String name) {
this.lv = level;
JFrame frame = new JFrame(name);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
d = new Display(lv, 500, 500);
frame.getContentPane().add(d);
frame.pack();
frame.setLocation(0, 0);
frame.setVisible(true);
}
private class Display extends JPanel {
public Display(Level fp, int x, int y) {
addKeyListener(new Listener());
setBackground(Color.GRAY);
setPreferredSize(new Dimension(x + 20, y + 20));
setFocusable(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
private void draw(Graphics g) {
Level lvl = new Level();
for(int i = 0; i < lvl.roomList.size(); i++) {
Room room = lvl.roomList.get(i);
g.setColor(room.floorColor);
g.drawRect(room.posX, room.posY, room.roomWidth, room.roomHeight);
}
}
}
}
To get some background info on the program. roomList is the ArrayList, and it is filled with various different sized and colored rooms. The rooms themselves are objects.
Here comes first Level class:
public class Level extends Observable {
private boolean Switch = true;
public ArrayList<Room> roomList = new ArrayList<Room>();
(...)
}
Here is the Class Room() that is used to create the rooms.
public class Room {
Color floorColor;
int roomWidth;
int roomHeight;
int posX;
int posY;
public Room(int dx, int dy, Color color) {
this.floorColor = color;
this.roomHeight = dy;
this.roomWidth = dx;
this.posY = 0;
this.posX = 0;
}
(...)
}
I've managed to locate where the problem is thought to occur, and it's the code in the for-loop. I tried switching the roomList.size() for an integer to test if it was the loop., But it wasn't. It is possible to draw a figure outside of the for-loop.
and again, the problem isn't an error message, the program simply doesn't draw the rooms that I've instructed it to draw in the method draw().
The display output looks like this:
Thanks beforehand!
Be aware that the paintComponent() method is invoked by Swing whenever the framework thinks the component needs to be rendered on screen. This usually is when the window is getting visible - initially or because some other window no longer hides the component. Such events are out of your control.
So your application should create a state and be ready to draw it anytime. Therefore you do not create state (like a level) inside the paint() or paintComponent() method. Put that elsewhere - if need be into the constructor.
Looking at you code:
As you are creating a new level inside paintComponent()/draw(), is it correct to assume that this level has no rooms associated? In that case the method is right to return without having painted anything.
If your application thinks the screen should be updated call repaint(), knowing that the paint() method will be called by the framework soon.
I'm creating a graphical front-end for a JBox2D simulation. The simulation runs incrementally, and in between the updates, the contents of the simulation are supposed to be drawn. Similar to a game except without input.
I only need geometric primitives to draw a JBox2D simulation. This API seemed like the simplest choice, but its design is a bit confusing.
Currently I have one class called Window extending JFrame, that contains as a member another class called Renderer. The Window class only initializes itself and provides an updateDisplay() method (that is called by the main loop), that calls updateDisplay(objects) method on the Renderer. I made these two methods myself and their only purpose is to call repaint() on the Renderer.
Is the JPanel supposed to be used that way? Or am I supposed to use some more sophisticated method for animation (such that involves events and/or time intervals in some back-end thread)?
If you are wanting to schedule the updates at a set interval, javax.swing.Timer provides a Swing-integrated service for it. Timer runs its task on the EDT periodically, without having an explicit loop. (An explicit loop would block the EDT from processing events, which would freeze the UI. I explained this more in-depth here.)
Ultimately doing any kind of painting in Swing you'll still be doing two things:
Overriding paintComponent to do your drawing.
Calling repaint as-needed to request that your drawing be made visible. (Swing normally only repaints when it's needed, for example when some other program's window passes over top of a Swing component.)
If you're doing those two things you're probably doing it right. Swing doesn't really have a high-level API for animation. It's designed primarily with drawing GUI components in mind. It can certainly do some good stuff, but you will have to write a component mostly from scratch, like you're doing.
Painting in AWT and Swing covers some of the 'behind the scenes' stuff if you do not have it bookmarked.
You might look in to JavaFX. I don't know that much about it personally, but it's supposed to be more geared towards animation.
As somewhat of an optimization, one thing that can be done is to paint on a separate image and then paint the image on to the panel in paintComponent. This is especially useful if the painting is long: repaints can be scheduled by the system so this keeps when it happens more under control.
If you aren't drawing to an image, then you'd need to build a model with objects, and paint all of them every time inside paintComponent.
Here's an example of drawing to an image:
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
/**
* Holding left-click draws, and
* right-clicking cycles the color.
*/
class PaintAnyTime {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new PaintAnyTime();
}
});
}
Color[] colors = {Color.red, Color.blue, Color.black};
int currentColor = 0;
BufferedImage img = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB);
Graphics2D imgG2 = img.createGraphics();
JFrame frame = new JFrame("Paint Any Time");
JPanel panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Creating a copy of the Graphics
// so any reconfiguration we do on
// it doesn't interfere with what
// Swing is doing.
Graphics2D g2 = (Graphics2D) g.create();
// Drawing the image.
int w = img.getWidth();
int h = img.getHeight();
g2.drawImage(img, 0, 0, w, h, null);
// Drawing a swatch.
Color color = colors[currentColor];
g2.setColor(color);
g2.fillRect(0, 0, 16, 16);
g2.setColor(Color.black);
g2.drawRect(-1, -1, 17, 17);
// At the end, we dispose the
// Graphics copy we've created
g2.dispose();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(img.getWidth(), img.getHeight());
}
};
MouseAdapter drawer = new MouseAdapter() {
boolean rButtonDown;
Point prev;
#Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
prev = e.getPoint();
}
if (SwingUtilities.isRightMouseButton(e) && !rButtonDown) {
// (This just behaves a little better
// than using the mouseClicked event.)
rButtonDown = true;
currentColor = (currentColor + 1) % colors.length;
panel.repaint();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (prev != null) {
Point next = e.getPoint();
Color color = colors[currentColor];
// We can safely paint to the
// image any time we want to.
imgG2.setColor(color);
imgG2.drawLine(prev.x, prev.y, next.x, next.y);
// We just need to repaint the
// panel to make sure the
// changes are visible
// immediately.
panel.repaint();
prev = next;
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
prev = null;
}
if (SwingUtilities.isRightMouseButton(e)) {
rButtonDown = false;
}
}
};
PaintAnyTime() {
// RenderingHints let you specify
// options such as antialiasing.
imgG2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
imgG2.setStroke(new BasicStroke(3));
//
panel.setBackground(Color.white);
panel.addMouseListener(drawer);
panel.addMouseMotionListener(drawer);
Cursor cursor =
Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
panel.setCursor(cursor);
frame.setContentPane(panel);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
If the routine is long-running and repaints could happen concurrently, double buffering can also be used. Drawing is done to an image which is separate from the one being shown. Then, when the drawing routine is done, the image references are swapped so the update is seamless.
You should typically use double buffering for a game, for example. Double buffering prevents the image from being shown in a partial state. This could happen if, for example, you were using a background thread for the game loop (instead of a Timer) and a repaint happened the game was doing the painting. Without double buffering, this kind of situation would result in flickering or tearing.
Swing components are double buffered by default, so if all of your drawing is happening on the EDT you don't need to write double buffering logic yourself. Swing already does it.
Here is a somewhat more complicated example which shows a long-running task and a buffer swap:
import java.awt.*;
import javax.swing.*;
import java.awt.image.*;
import java.awt.event.*;
import java.util.*;
/**
* Left-click to spawn a new background
* painting task.
*/
class DoubleBuffer {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new DoubleBuffer();
}
});
}
final int width = 640;
final int height = 480;
BufferedImage createCompatibleImage() {
GraphicsConfiguration gc =
GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration();
// createCompatibleImage creates an image that is
// optimized for the display device.
// See http://docs.oracle.com/javase/8/docs/api/java/awt/GraphicsConfiguration.html#createCompatibleImage-int-int-int-
return gc.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
}
// The front image is the one which is
// displayed in the panel.
BufferedImage front = createCompatibleImage();
// The back image is the one that gets
// painted to.
BufferedImage back = createCompatibleImage();
boolean isPainting = false;
final JFrame frame = new JFrame("Double Buffer");
final JPanel panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Scaling the image to fit the panel.
Dimension actualSize = getSize();
int w = actualSize.width;
int h = actualSize.height;
g.drawImage(front, 0, 0, w, h, null);
}
};
final MouseAdapter onClick = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
if (!isPainting) {
isPainting = true;
new PaintTask(e.getPoint()).execute();
}
}
};
DoubleBuffer() {
panel.setPreferredSize(new Dimension(width, height));
panel.setBackground(Color.WHITE);
panel.addMouseListener(onClick);
frame.setContentPane(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
void swap() {
BufferedImage temp = front;
front = back;
back = temp;
}
class PaintTask extends SwingWorker<Void, Void> {
final Point pt;
PaintTask(Point pt) {
this.pt = pt;
}
#Override
public Void doInBackground() {
Random rand = new Random();
synchronized(DoubleBuffer.this) {
Graphics2D g2 = back.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE);
g2.setBackground(new Color(0, true));
g2.clearRect(0, 0, width, height);
// (This computes pow(2, rand.nextInt(3) + 7).)
int depth = 1 << ( rand.nextInt(3) + 7 );
float hue = rand.nextInt(depth);
int radius = 1;
int c;
// This loop just draws concentric circles,
// starting from the inside and extending
// outwards until it hits the outside of
// the image.
do {
int rgb = Color.HSBtoRGB(hue / depth, 1, 1);
g2.setColor(new Color(rgb));
int x = pt.x - radius;
int y = pt.y - radius;
int d = radius * 2;
g2.drawOval(x, y, d, d);
++radius;
++hue;
c = (int) (radius * Math.cos(Math.PI / 4));
} while (
(0 <= pt.x - c) || (pt.x + c < width)
|| (0 <= pt.y - c) || (pt.y + c < height)
);
g2.dispose();
back.flush();
return (Void) null;
}
}
#Override
public void done() {
// done() is completed on the EDT,
// so for this small program, this
// is the only place where synchronization
// is necessary.
// paintComponent will see the swap
// happen the next time it is called.
synchronized(DoubleBuffer.this) {
swap();
}
isPainting = false;
panel.repaint();
}
}
}
The painting routine is just intended draw garbage which takes a long time:
For a tightly coupled simulation, javax.swing.Timer is a good choice. Let the timer's listener invoke your implementation of paintComponent(), as shown here and in the example cited here.
For a loosely coupled simulation, let the model evolve in the background thread of a SwingWorker, as shown here. Invoke publish() when apropos to you simulation.
The choice is dictated in part by the nature of the simulation and the duty cycle of the model.
Why not just use stuff from the testbed? It already does everything. Just take the JPanel, controller, and debug draw. It uses Java 2D drawing.
See here for the JPanel that does the buffered rendering:
https://github.com/dmurph/jbox2d/blob/master/jbox2d-testbed/src/main/java/org/jbox2d/testbed/framework/j2d/TestPanelJ2D.java
and here for the debug draw:
https://github.com/dmurph/jbox2d/blob/master/jbox2d-testbed/src/main/java/org/jbox2d/testbed/framework/j2d/DebugDrawJ2D.java
See the TestbedMain.java file to see how the normal testbed is launched, and rip out what you don't need :)
Edits:
Disclaimer: I maintain jbox2d
Here is the package for the testbed framework: https://github.com/dmurph/jbox2d/tree/master/jbox2d-testbed/src/main/java/org/jbox2d/testbed/framework
TestbedMain.java is in the j2d folder, here:
https://github.com/dmurph/jbox2d/tree/master/jbox2d-testbed/src/main/java/org/jbox2d/testbed/framework/j2d
What code should I add so that the rectangles painted before continue to exist on the screen when new ones are printed.Here is the code
import javax.sound.midi.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class MiniMusicPlayer3
{
private boolean fire = false;
private JFrame frame;
public static void main(String args[])
{
MiniMusicPlayer3 mini = new MiniMusicPlayer3();
mini.go();
}
public void go()
{
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600,600);
frame.setVisible(true);
MyDrawPanel boxes = new MyDrawPanel();
frame.getContentPane().add(boxes);
try
{
Sequencer player =MidiSystem.getSequencer();
player.open();
Sequence seq = new Sequence(Sequence.PPQ,4);
Track track = seq.createTrack();
int arr[] ={127};
player.addControllerEventListener(new MyDrawPanel(),arr);
//add notes to the track
for(int i = 5;i<61;i+=4)
{
track.add(makeEvent(144,1,i,100,i));
track.add(makeEvent(176,1,127,0,i));
track.add(makeEvent(128,1,i,100,(i+2)));
}
player.setSequence(seq);
player.setTempoInBPM(220);
player.start();
}
catch(Exception ex)
{
}
}
public MidiEvent makeEvent(int onOff,int one,int note,int vel,int tick)
{
MidiEvent event = null;
try
{
ShortMessage a = new ShortMessage();
a.setMessage(onOff,one,note,vel);
event = new MidiEvent(a,tick);
}
catch(Exception e)
{
}
finally
{
return event;
}
}
class MyDrawPanel extends JPanel implements ControllerEventListener
{
public void controlChange(ShortMessage message)
{
System.out.println("control change happens");
fire = true;
frame.repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(fire)
{
Graphics2D g2d = (Graphics2D)g;
int red = (int)(Math.random()*255);
int blue = (int)(Math.random()*255);
int green = (int)(Math.random()*255);
Color color = new Color(red,blue,green);
g2d.setColor(color);
int height = (int)((Math.random()*120)+10);
int width = (int)((Math.random()*120)+10);
int x = (int)((Math.random()*40)+10);
int y = (int)((Math.random()*40)+10);
g2d.fillRect(x, y, width, height);
fire = false;
}
}
}
}
Also why does the code above not let the rectangles persist as opposed to the code below that allows the circles to persist
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Animate
{
private JFrame frame;
private int x=10,y=10;
public static void main(String args[])
{
Animate ballRoll = new Animate();
ballRoll.go();
}
public void go()
{
frame = new JFrame();
frame.setSize(500,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyRoll ball = new MyRoll();
frame.getContentPane().add(ball);
for(x = 5;x<=350;x++)
{
y=x;
try
{
Thread.sleep(50);
}
catch(Exception e)
{
System.out.println("dsfsd");
}
ball.repaint();
}
}
class MyRoll extends JPanel
{
public void paintComponent(Graphics g)
{
g.setColor(Color.ORANGE);
g.fillOval(x, y, 100, 100);
}
}
}
Replace frame.repaint() with this.repaint() and remove super.paintComponent(g) if you want to persist the previous painting as well but I never suggest you to use this approach. You have to redraw all the objects again in paintComponent().
Please have a look at below sections for detail information about Paint processing in Swing application.
Painting in AWT and Swing
The Paint processing
A Closer Look at the Paint Mechanism
"What code should I add so that the rectangles previously printed persist,instead of getting erased when the new rectangles are painted?"
Create a list of Rectangle2D object (as a class member).
Loop through the list in the paintComponent and paint each rectangle.
When you want to add a new rectangle, well, add a new rectangle to the list and repaint.
If you want different colors (or and other state) for each rectangle, create a wrapper like (See some of the examples below).
"Also why does the code above not let the rectangles persist as opposed to the code below that allows the circles to persist"
No rectangle are not "persisting" per se. What you are seeing are paint artifacts from not calling super.paintComponent which clears the previous paint. You should always call super.paintComponent though, like in your first example. So your best options is to go with the first part of my answer.
See a bunch of examples here and here and here and here and here and here.
The basic premise of all those examples is storing a list of similar object in a list and iterating through the list to paint all the objects. Each object can have its own specific state.
You could also extend your MyDrawPanel class so you keep track of which rectangles to paint plus their colors. So you could add each new rectangle to a list of rectangles and keep track of the rectangle colors using a map. Then you just need to add new rectangles to the list and the new color to the map. Finally, you'll need to loop through the list of rectangles and paint these one by one.
Here is how it could be done:
class MyDrawPanel extends JPanel implements ControllerEventListener
{
// List of all rectangles that needs to be painted
java.util.List<Rectangle> rectangles = new ArrayList<Rectangle>();
// Map over all colors for each rectangle that must be painted
Map<Rectangle, Color> rectangleColors = new HashMap<Rectangle, Color>();
public void controlChange(ShortMessage message)
{
System.out.println("control change happens");
fire = true;
frame.repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(fire)
{
Graphics2D g2d = (Graphics2D)g;
int red = (int)(Math.random()*255);
int blue = (int)(Math.random()*255);
int green = (int)(Math.random()*255);
Color color = new Color(red,blue,green);
g2d.setColor(color);
int height = (int)((Math.random()*120)+10);
int width = (int)((Math.random()*120)+10);
int x = (int)((Math.random()*40)+10);
int y = (int)((Math.random()*40)+10);
// Create a new rectangle to paint
Rectangle newRect = new Rectangle(x, y, width, height);
// Store the rectangle in the list over rectangles to paint
rectangles.add(newRect);
// Add the color of the rectangle in the map over rectangle colors
rectangleColors.put(newRect, color);
// Paint all the rectangles using their colors one by one
for (Rectangle rect : rectangles) {
// Get the color of the rectangle
Color rectColor = rectangleColors.get(rect);
// Set the rectangle color
g2d.setColor(rectColor);
// Fill the rectangle with the rectangle color
g2d.fill(rect);
}
fire = false;
}
}
}
There are two common approaches:
as has already been mentioned a couple of times, you keep a List of objects to paint and then iterate through the List in the paintComponent(...) method.
draw to a BufferedImage.
Take a look at Custom Painting Approaches which exams both of these approaches and contains working examples of both.
Following is the code I have so far:
All the imports are correct. I'm sure. :D
When I run the program, all I get is a blank frame, without the picture. It should show up.
public class WindowPractice extends JFrame {
final static int width= 800;
final static int height= 400;
int x;
int y;
Image steve;
Dimension gamesize= new Dimension (width, height);
public WindowPractice(){
setTitle ("Hangman");
setSize (gamesize);
setVisible (true);
setResizable (false);
setLocationRelativeTo (null);
setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
}
public static void main (String[] args) {
new WindowPractice();
ImageIcon steve= new ImageIcon ("Libraries/Pictures/ba190cd951302bcebdf216239e156a4.jpg");
JLabel imageLabel = new JLabel(steve);
}
public void paint(Graphics g){
g.setColor(Color.red);
//g.fillRect( x, y, 100, 20);
g.drawImage(steve, x, y,this);
x= 150;
y= 250;
}
}
There are so many things wrong with this I'm not sure where to start...
Let's start at the beginning...
Problem #1
You declare a instance field called steve in you WindowPractice class, this is fine, but in your main method, you declare ANOTHER variable called steve which you are using the reference to the loaded image...
public static void main(String[] args) {
new WindowPractice();
ImageIcon steve = new ImageIcon("C:/Users/shane/Dropbox/issue459.jpg");
JLabel imageLabel = new JLabel(steve);
}
This means that the class instance variable is never initialised and remains null.
Problem #2
While not directly related, you never call super.paint from your paint method. This is a BIG NO, NO. You are obligated to maintain the paint chain. The paint methods are complex and very, very important.
Problem #3
You should never override a top level container (such as JFrame) nor you should you override any of it's paint methods. There are lots of reasons for this, but among the top two are, most top level containers actually contain a number of components (JRootPane, which houses the glass pane, content pane, layer pane and menu bar) which can sit over your painting efforts and, generally, they're not double buffered, meaning you paint updates will flicker and look horrible ;)
You should also avoid using paint, instead, you should look towards using paintComponent where it's available.
Problem #4
ImageIcon is not you best choice for loading images. The main reason I don't use them is that you have no idea of when the image being loaded will actually become available (actually there are ways, but to be frank, ImageIO is just simpler). This was a great feature back in 1999 when dial-up speeds where around the 14.4k mark, but now days...
ImageIO supports a wider range of picture formats, supports reading and writing of images and guarantees that when the method returns (successfully), the image pixel data is available to your application.
Example
Here's a better (IMHO) approach...
public class BetterDrawing {
public static void main(String[] args) {
new BetterDrawing();
}
public BetterDrawing() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PaintPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PaintPane extends JPanel {
private BufferedImage background;
public PaintPane() {
try {
background = ImageIO.read(new File("/path/to/image"));
// Use this instead to load embedded resources instead
//background = ImageIO.read(getClass().getResource("/path/to/image"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return background == null ? super.getPreferredSize() : new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g.drawImage(background, x, y, this);
}
}
}
}
Have a read of
Performing custom painting
Working with Images
For some more information.