When redrawing circles, the window is not cleared; the new circles get added to the existing content.
The goal is to create three circle, one for each color.
The thread calls move function which draws the circles with different radii.
public void run() {
try {
while(true){
box.removeAll();
move();
box.removeAll();
sleep(500);
}
} catch (InterruptedException e) {
}
}
public synchronized void move() {
Graphics g = box.getGraphics();
g.setXORMode(box.getBackground());
x1= one.x + ofset;
y1= one.y + ofset;
System.out.println("My Point ("+ x1 + " , " + y1 +")" );
g.setColor(Color.green);
g.drawOval(pointA.x-(int)distance1, pointA.y-(int)distance1, (int)distance1*2, (int)distance1*2);
g.setColor(Color.blue);
g.drawOval(pointB.x-(int)distance2, pointB.y-(int)distance2, (int)distance2*2, (int)distance2*2);
g.setColor(Color.red);
g.drawOval(pointC.x-(int)distance3, pointC.y-(int)distance3, (int)distance3*2, (int)distance3*2);
g.dispose();
}
First of all, your approach is not recommended. But, if you only want a quick and dirty fix, you have to clear the panel before drawing the circles.
Graphics g = box.getGraphics();
g.clearRect(0, 0, box.getWidth(), box.getHeight()); // this should do it
g.setXORMode(box.getBackground());
Graphics g = box.getGraphics();
No. Don't use getGraphics(). Any painting done with that Graphics object is only temporary and will be deleted any time Swing determines a component needs to be repainted.
For custom painting override the getPreferredSize() method of a JPanel:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g); // clears the background
// add your custom painting here
}
Also, don't forget to override the getPreferredSize() method of your panel. Read the section from the Swing tutorial on Custom Painting for more information.
Related
I'm pretty new to Java and the GUI world. Right now I'm trying to create a really basic space shooter. To create it I started creating a JFrame, in which I've later on put a personal extension of a JPanel called GamePanel, on which I'm now trying to display all my components. Until here it's all pretty clear, the problem comes now: I have my GamePanel in which I display my player, and on the KeyEvent of pressing S the player should shoot the Bullets. I've managed the bullets as an Array, called Shooter[], of Bullet Objects, created by myself this way:
public class Bullet implements ActionListener{
Timer Time = new Timer(20, this);
private int BulletY = 430;
public int PlayerX;
public Rectangle Bound = new Rectangle();
public Bullet(int playerx) {
this.PlayerX = playerx;
Time.start();
}
public void draw(Graphics g){
g.setColor(Color.RED);
g.fillRect(PlayerX + 2, BulletY, 3, 10);
g.dispose();
}
#Override
public void actionPerformed(ActionEvent e) {
if (Time.isRunning()) {
BulletY = BulletY - 5;
Bound = new Rectangle (PlayerX + 2, BulletY, 3, 10);
}
}
}
I thought that calling the draw method in the GamePanel's paint() method would have allowed me to display both all the bullets shot and the player. What actually happens is that at the start it seems allright, but when I press S the player disappears and just one bullet is shot. Can you explain me why? This is how my paint() method looks like:
public void paint(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, 500, 500);
for(int i = 0; i < BulletsCounter; i++) {
Shooter[i].draw(g);
}
g.setColor(Color.RED);
g.fillRect(PlayerX, PlayerY, 20, 20);
//System.out.println("Here I should have painted the player...");
g.dispose();
}
BulletsCounter is a counter I've created to avoid any NullPointerExceptions in painting the whole array, it increases when S is pressed and so another bullet of the array is initialized and shot.
Thank you for your patience, I'm new to the site, so warn me for any mistake.
You've several significant problems, the biggest given first:
You're disposing a Graphics object given to you by the JVM. Never do this as this will break the painting chain. Instead, only dispose of a Graphics object that you yourself have created.
You're drawing within paint which is not good for several reasons, but especially bad for animation since you don't have automatic double buffering of the image
You don't call the super painting method within your override and thus don't allow the JPanel to do house-keeping painting.
Recommendations:
Don't dispose of the Graphics object, not unless you, yourself, create it, for example if you extract one from a BufferedImage.
Override the JPanel's paintComponent method, not its paint method to give you double buffering and smoother animation.
And call super.paintComponent(g) first thing in your override to allow for housekeeping painting
I'm trying to write a JPanel Picture to a BufferedImage (later converted to Rendered Image). I am getting a stack overflow error in the AWT-EventQueue-0 thread for some reason and am not sure if there is a reason I have overlooked.
The code in question:
public BufferedImage createImage() {
int w = getWidth();
int h = getHeight();
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
cp.paint(bi.getGraphics());
//debug script
File outputfile = new File("image"+index+".jpg");
try {
ImageIO.write(bi, "jpg", outputfile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
index++;
return bi;
}
The JPanel paintComponent
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
r = new Random(System.nanoTime());
int maxSize = 100;
int randX = r.nextInt(getWidth());
int randY = r.nextInt(getHeight());
int randWidth = r.nextInt(maxSize);
int randHeight = r.nextInt(maxSize);
Color color = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256));
Graphics2D g2d = (Graphics2D) g;
ovals.add(new MyCircles(randX, randY, randWidth, randHeight, color));
for (MyCircles c : ovals) {
c.paint(g2d);
}
g2d.setColor(getForeground());
repaint();
double current = ImageComparator.calcDistance((RenderedImage)createImage());
//debugging script
System.out.println("Current: " + current);
System.out.println("Previous" + previous);
if(current > previous) {
ovals.remove(ovals.size()-1);
}
else {
previous = current;
}
}
Any insight as to how to amend this issue would be greatly appreciated!
Remove the the call to repaint in paintComponent which causes that method to be called ad infinitum
Not directly related to your problem but, you should never use the Random class in the painting method. Every time you call the method the painting will change, so the image you create and save will not be the same as the image on the panel.
Also, you should not be adding ovals in the paint method for the same reason give above.
You need to create an addOval(...) method that will set the random color of the Oval and add the oval to the List. The painting code will just iterate through the List and paint the Oval.
You also should NOT be removing ovals in the painting code. Painting code is for painting only, not manipulating the objects painted.
You can also try the Screen Image class which is basically a more flexible version of your image creation code.
Of course you have infinite loop there:
here is how you call your methods:
createImage()
|__paint()
|__createImage() // again in ImageComparator.calcDistance line
|__paint()
|__createImage() // again in ImageComparator.calcDistance line
|__paint()
|__createImage() // again in ImageComparator.calcDistance line
|__paint()
TOY STORY 1 (Buzz) : to the infinite and beyond it :)
You never stop this cycle.
I suggest that you need to get the images and then compare them outsize of your paint. Let the paint just paint the image and do the comparison outside of it.
Dear wonderful people of stackoverflow
A group of my friends are attempting to make a level editor in Java.
We have a Jpanel instead of a Jframe and we are trying to put small images onto the Jpanel from a filepath saved as a string. In the end we want a list of images that you can just drop on. So far we have tried a few methods with no luck.
We can load the images, however we can't get these images to actually display, What would be the best means of solving said problem?
below is a sample of what we have so far.
EnemyPlacementGrid = new JPanel();
EnemyPlacementGrid.addMouseListener(new MouseAdapter() {
//#Override
public int mouseX;
public int mouseY;
public void mouseClicked(MouseEvent arg0) { //what happens when you click in the EnemyPlacementGrid
System.out.println("Correct Area for placement");
mouseX = arg0.getX();
mouseY = arg0.getY();
//System.out.println("X:" + mouseX + ", Y:" + mouseY );
Enemy newEnemy = workingEnemy.cloneSelf();
newEnemy.setLocation(mouseX, mouseY);
System.out.println("newEnemy object: " + newEnemy);
System.out.println(newEnemy.weaponList);
currentWave.addEnemy(newEnemy);
System.out.print(currentLevel);
}
});
Any and all help is greatly appreciated.
UPDATE:
As of now I have an image appearing, however I can't update said image. Note code below:
public void run() {
try {
BufferedImage img = ImageIO.read(new File(IMG_PATH));
ImageIcon icon = new ImageIcon(img);
WaveScreen frame = new WaveScreen();
JPanel panel = (JPanel)frame.getContentPane();
JLabel label = new JLabel();
label.setIcon(new ImageIcon("images/map_on.png"));// your image here
panel.add(label);
frame.setVisible(true);
panel.add(label);
panel.repaint();
} catch (Exception e) {
e.printStackTrace();
}
update, method tried from comments:
Graphics2D g = null;
Graphics2D g2 = (Graphics2D)g;
Image imageVariable = new ImageIcon("images/map_on.png").getImage();
g.drawImage(imageVariable, mouseX, mouseY, null);
Well, i'd say to try using Graphics, meaning you need to override the paint method; i'd recommend that you put the mouseX and mouseY as global variables though…
// creating global image variable for use later
Image imageVariable = new ImageIcon("image path").getImage();
public void paintComponent(Graphics g) {
// here you could either create a Graphics2D object
// Graphics2D g2 = (Graphics2D)g;
// or you could use the g parameter as it is, doesn't matter.
// use the global variable for the image to be drawn onto the screen
// use the global value of the mouseX and mouseY for where you click the mouse
// to place the image, and this should be it
g.drawImage(imageVariable, mouseX, mouseY, null);
}
Hope this helps!
If the game is simple, user2277872's solution will work and you can use graphics2D from java. However, if you are planning on a more sophisticated game (lots of interaction, lots of textures), then the default Java framework for 2D graphics will prove to be too slow.
If you are planning on such a game, I can highly recommend either learning OpenGL or using an existing framework for graphics, such as
JMonkeyEngine (http://jmonkeyengine.com/)
or
Slick (http://slick.cokeandcode.com/index.php)
More information: What should I use to display game graphics?
Brief description of Program
Hi guys. I got bored this morning and decided to write a graphing program. Eventually i'll be able to run things like Dijksta's Algorithm on this software.
When anything changes on screen, a call to the repaint method of the JPanel where everything is painted to is made. This is the JPanel paint method:
public void paint(Graphics g)
{
for(Node node : graph.getNodes()){
node.paint(g);
}
for(Link link : graph.getLinks()){
link.paint(g);
}
}
It simply cycles through each element in the lists, and paints them.
The paint method for the node class is:
public void paint(Graphics g)
{
g.setColor(color);
g.drawOval(location.x, location.y, 50, 50);
g.setColor(Color.BLACK);
g.drawString(name, location.x + 20, location.y + 20);
}
And for the link it is:
public void paint(Graphics g)
{
Point p1 = node1.getLocation();
Point p2 = node2.getLocation();
// Grab the two nodes from the link.
g.drawLine(p1.x + 20, p1.y + 20, p2.x + 20, p2.y + 20);
// Draw the line between them.
int midPointX = ((p1.x + p2.x) / 2) + (100 / (p2.x - p1.x));
int midPointY = ((p1.y + p2.y) / 2) + 30;
// Compute the mid point of the line and get it closer to the line.
g.setColor(Color.BLACK);
g.drawString(String.valueOf(weight), midPointX, midPointY);
}
The Problem
The issue I am having arises when I use the JOptionPane class. When I select the option to add a new node, and select where to place it, an inputDialog pops up, asking for the node's name.
The nodes are added fine, for this behaviour occurs:
Is this a common problem; an issue with paint or repaint perhaps?
Nonetheless, here is the code that calls the inputDialog:
Function addNode = functionFac.getInstance(state);
String name = "";
while(!name.matches("[A-Za-z]+")) {
name = JOptionPane.showInputDialog("Please enter the name of the node.", null);
}
addNode.execute(stage, new NodeMessage(arg0.getPoint(), name));
PS: Function is an interface type that I have written.
"Swing programs should override paintComponent() instead of overriding paint()."—Painting in AWT and Swing: The Paint Methods.
"If you do not honor the opaque property you will likely see visual artifacts."—JComponent
See also this Q&A that examines a related issue.
How can I mousedrag different BufferedImages in Java2D?
For instance, if I have ten or more images, how can I move that images which my mouse is over?
Now I'm importing an BufferedImage with
BufferedImage img = new BufferdImage(new File("filename"));
And I'm painting this with Graphics2D with
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.drawImage(img, x1, y1, null);
g2d.drawImage(img2, x2, y2,null);
}
Everytime I'm moving on a image I'm repaint()-ing the entire screen.
My mousemove class is as follows
class MouseMotionHandler extends MouseMotionAdapter {
#Override
public void mouseDragged(MouseEvent e) {
x1 = e.getX() - (img.getWidth() / 2);
y1 = e.getY() - (img.getHeight() / 2);
repaint();
}
}
With this method I'm able to "drag" one picture, but what to do when I will drag more individually?
Use the BufferedImage to create an ImageIcon which you use to create a JLabel. Then you add the JLabel to the panel that uses a null layout. No custom painting code is required to do this.
Now if you want to drag the label around you can use the Component Mover.
You can try making a custom component that contains only a single image. Along with your painting and mouse motion handling code, the component overrides the contains method so that it returns true only if the coordinates are within the image.
These components are then stacked in a JLayeredPane, (hopefully) only moving the images that the mouse is on top of.
From what you ask I suppose that your current repainting logic is global. You need to apply it to every image you have. So, if you for instance display every image in JPanel attach MouseMotionListener to every such panel and make this logic happen in JPanel.
If you post more code - especially of the component you show your images in - I will be able to go into more details.
Here's is a simple example that implements dragging for either single- or multiple-selections. The object Node would correspond roughly to your object Card.
Addendum: Also considered the Overlap Layout mentioned in this answer to a related question. Instead of List<Node>, your program would manage a List<Card>, where each Card is a JLabel having a card image.
I should make tree arrays:
one for the x-values
one for the y-values
one for the BufferedImages
So, something like this:
int[] xValues = new int[10];
int[] yValues = new int[10];
BufferedImage[] imgs = new BufferedImage[10];
Then the
class MouseMotionHandler extends MouseMotionAdapter {
#Override
public void mouseDragged(MouseEvent e) {
for (int i = 0; i < 10; i++)
{
xValues[i] = e.getX() - (imgs[i].getWidth() / 2);
yValues[i] = e.getY() - (imgs[i].getHeight() / 2);
}
repaint();
}
}
Then paint them like this:
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
for (int i = 0; i < 10; i++)
{
g2d.drawImage(imgs[i], xValues[i], yValues[i], null);
}
}
I think something like this is what you need.
Here's the code for my JLayeredPane init. My problem here is that my images don't show up...
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new java.awt.Dimension(500, 410));
layeredPane.setBorder(javax.swing.BorderFactory.createTitledBorder(
"Center deck"));
for(BufferedImage imgs : images){
JLabel label = new JLabel(new ImageIcon(imgs));
layeredPane.add(label, JLayeredPane.DEFAULT_LAYER);
}
add(layeredPane);