I have a paint method that I have draw a rectangle and move it across the screen and what I want it to do I when the rectangle goes off the screen to move it back to the start of the JFrame. I assume it would be something like if(rectangle.isoffthescreen){ put it back on the screen } but I don't know how to do that. Also, I want to know if you can do something to the rect like JFrame frame = new JFrame();, but for obviously for the rectangle. I'm sorry if this is confusing.
#MadProgrammer Here is where I am at so far
public class main extends JPanel {
public static int place = -350;
public Rectangle rect;
public int xDelta;
public main() {
rect = new Rectangle(0, 75, 50, 50);
xDelta = 4;
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
rect.x += xDelta;
if (rect.x + rect.width > getWidth() - 1) {
rect.x = getWidth() - rect.width;
xDelta *= -1;
} else if (rect.x < 0) {
rect.x = 0;
xDelta *= -1;
}
repaint();
}
});
timer.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g.create();
Random r = new Random();
int r1;
r1 = r.nextInt(5);
if (r1 == 0) {
g2d.setColor(Color.WHITE);
} else if (r1 == 1) {
g2d.setColor(Color.BLUE);
} else if (r1 == 2) {
g2d.setColor(Color.RED);
} else if (r1 == 3) {
g2d.setColor(Color.GREEN);
} else if (r1 == 4) {
g2d.setColor(Color.PINK);
} else {
g2d.setColor(Color.CYAN);
}
place += 50;
rect = new Rectangle(place, 100, 300, 200);
g2d.draw(rect);
g2d.fill(rect);
g2d.dispose();
try {
Thread.sleep(400);
} catch (Exception e) {
}
repaint();
}
}
and
public class frame {
public static JFrame frame;
public static void main(String args[]){
frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//frame.setResizable(false);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
main m = new main();
m.setBackground(Color.BLACK);
frame.add(m);
}
}
I still can't get it to work.
First of, you should avoid overriding paint of top level containers and instead use something the extends from JComponent (like JPanel) and override it's paintComponent.
There are a number of reasons, but in your case, the frame contains a decoration which sits inside the viewable area.
The basic process would be to check edge cases...
if (box.x + box.width > getWidth() - 1) {
// The boxes right edge is beyond the right edge of it's container
} else if (box.x < 0) {
// The boxes left edge is beyond the left edge of it's container
}
This checks to see if the box's right edge is beyond the containers right edge and if the box's left edge is beyond the left edge of the container.
It would be a simple process to included vertical checking as well.
Updated with example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Move {
public static void main(String[] args) {
new Move();
}
public Move() {
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 Rectangle box;
private int xDelta;
public TestPane() {
box = new Rectangle(0, 75, 50, 50);
xDelta = 4;
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
box.x += xDelta;
if (box.x + box.width > getWidth() - 1) {
box.x = getWidth() - box.width;
xDelta *= -1;
} else if (box.x < 0) {
box.x = 0;
xDelta *= -1;
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLUE);
g2d.fill(box);
g2d.dispose();
}
}
}
Update from OP's example code
Immediately, a number of things worry me...
Overriding paint
Calling Thread.sleep in the paint method
Calling repaint from within the paint method
Creating a new Rectangle in the paint method
The reliance on the place variable
Check out Performing Custom Painting for more details about painting in Swing
You should never block or perform long running processing the Event Dispatching Thread, this will prevent the EDT from processing (amongst other things) paint requests and new events. By calling Thread.sleep in the paint method, you are preventing Swing from updating the screen.
Check out Concurrency in Swing for more details.
Calling repaint (or any method that might call repaint) within a paint method is a sure way to consume you CPU cycle.
You may want to check out Painting in AWT and Swing for more details about the paint process in Swing
By creating a new Rectangle in the paint method, you are throwing away any changes that the Timer has made, effectively stopping you rectangle from moving effectively...
The place method is not required. Animation is the illusion of change over time, hence the use of the Timer and xDelta.
Updated based on your code example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
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.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Move {
public static void main(String[] args) {
new Move();
}
public Move() {
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 Main());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class Main extends JPanel {
public static int place = -350;
public Rectangle rect;
public int xDelta;
public Main() {
rect = new Rectangle(0, 75, 50, 50);
xDelta = 4;
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
rect.x += xDelta;
if (rect.x + rect.width > getWidth() - 1) {
rect.x = getWidth() - rect.width;
xDelta *= -1;
} else if (rect.x < 0) {
rect.x = 0;
xDelta *= -1;
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Random r = new Random();
int r1;
r1 = r.nextInt(5);
if (r1 == 0) {
g2d.setColor(Color.WHITE);
} else if (r1 == 1) {
g2d.setColor(Color.BLUE);
} else if (r1 == 2) {
g2d.setColor(Color.RED);
} else if (r1 == 3) {
g2d.setColor(Color.GREEN);
} else if (r1 == 4) {
g2d.setColor(Color.PINK);
} else {
g2d.setColor(Color.CYAN);
}
// place += 50;
// rect = new Rectangle(place, 100, 300, 200);
g2d.draw(rect);
g2d.fill(rect);
g2d.dispose();
}
}
}
Related
Hi Everyone I want to draw lines to Jlabel Icon with DrawLines() class but the program isn't drawing .When I use to frame.add(m) the program is drawing lines to frame .....
DrawLine m = new DrawLine();
frame.add(m);
but when I use to label.add(m) method .The program isn't working .I need to figure out this problem Why can't I draw lines to jlabelIcon and How can I fix this ?
This is my DrawLine Class
package com.company;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Line2D;
public class DrawLine extends JComponent {
public void paint(Graphics g) {
super.paintComponents(g);
g.drawLine(300, 152, 63, 185);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
g.drawLine(63, 185, 120, 198);
}
}
This is my main class
package com.company;
import com.sun.source.tree.Tree;
import jdk.swing.interop.SwingInterOpUtils;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.awt.geom.Line2D;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
public class Main extends JFrame {
public static void main(String[] args) {
JFrame frame = new JFrame("Display Image");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = (JPanel) frame.getContentPane();
frame.setSize(1000,560);
JLabel label = new JLabel();
label.setSize(1000,560);
label.setIcon(new ImageIcon("myimage path"));
DrawLine m = new DrawLine();
label.add(m);
panel.add(label);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
If your goal is to draw lines and images together, then your best bet for the money is to get rid of ImageIcon and JLabel and instead is to draw them all within a single paintComponent. The images can be drawn as image sprite, and the lines as lines by calling Graphics#drawLine(...) or as Line2D objects as you have using Graphics2D#draw(...)
For example, say we had two BufferedImage objects, upImg and dnImg, and two Point objects that determined the location of these sprites, upPt and dnPt
public class Foo01 extends JPanel {
// .....
private Point upPt = new Point(300, 100);
private Point dnPt = new Point(700, 650);
private BufferedImage upImg, dnImg;
And say we wanted to draw a line that connected the two image sprites, then these could all be draw within the paintComponent method like so:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // tell the JPanel to do its house-keeping painting
// make sure that neither image is null
if (upImg != null && dnImg != null) {
// draw both images at their respective locations
g.drawImage(upImg, upPt.x, upPt.y, this);
g.drawImage(dnImg, dnPt.x, dnPt.y, this);
// to get a smooth line, use rendering hiints
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// to give the line some thickness
g2.setStroke(new BasicStroke(5f));
// calculate the end-points of the line
int x1 = upPt.x + upImg.getWidth() / 2;
int y1 = upPt.y + upImg.getHeight() / 2;
int x2 = dnPt.x + dnImg.getWidth() / 2;
int y2 = dnPt.y + dnImg.getHeight() / 2;
// and then draw it
g.drawLine(x1, y1, x2, y2);
}
}
Here is an example program that does just this -- draws two images with a line connecting. I've also added a MouseAdapter to allow the user to move the first image, the green up-arrow, showing that the line will move as well, since it is calculated within the painting method:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.URL;
import java.io.IOException;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class Foo01 extends JPanel {
private static final String UP_IMG_PATH = "https://upload.wikimedia.org/wikipedia/commons/7/7d/Green_circle_icon.jpg";
private static final String DN_IMG_PATH = "https://upload.wikimedia.org/wikipedia/commons/b/bc/Red_circle_icon.jpg";
private static final int GUI_W = 1000;
private static final int GUI_H = 800;
private Point upPt = new Point(300, 100);
private Point dnPt = new Point(700, 650);
private BufferedImage upImg, dnImg;
public Foo01() {
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
setBackground(Color.WHITE);
try {
URL url = new URL(UP_IMG_PATH);
upImg = ImageIO.read(url);
url = new URL(DN_IMG_PATH);
dnImg = ImageIO.read(url);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(GUI_W, GUI_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (upImg != null && dnImg != null) {
g.drawImage(upImg, upPt.x, upPt.y, this);
g.drawImage(dnImg, dnPt.x, dnPt.y, this);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(5f));
int x1 = upPt.x + upImg.getWidth() / 2;
int y1 = upPt.y + upImg.getHeight() / 2;
int x2 = dnPt.x + dnImg.getWidth() / 2;
int y2 = dnPt.y + dnImg.getHeight() / 2;
g.drawLine(x1, y1, x2, y2);
}
}
private class MyMouse extends MouseAdapter {
private Point p1 = null;
#Override
public void mousePressed(MouseEvent e) {
if (e.getX() < upPt.x || e.getX() > upPt.x + upImg.getWidth()) {
return;
}
if (e.getY() < upPt.y || e.getY() > upPt.y + upImg.getHeight()) {
return;
}
p1 = new Point(e.getX(), e.getY());
}
#Override
public void mouseReleased(MouseEvent e) {
if (p1 != null) {
moveSprite(e);
p1 = null;
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (p1 != null) {
moveSprite(e);
}
}
private void moveSprite(MouseEvent e) {
Point p2 = new Point(e.getX(), e.getY());
int x = upPt.x + p2.x - p1.x;
int y = upPt.y + p2.y - p1.y;
upPt = new Point(x, y);
p1 = p2;
repaint();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()-> {
Foo01 foo01 = new Foo01();
JFrame frame = new JFrame("Draw Sprites");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(foo01);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
If the need is to add custom painting to a JLabel you can override its paintComponent:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
public class Main extends JFrame {
private static final String BUG = "https://www.growtopiagame.com/forums/attachment.php?attachmentid=141847&d=1477126665";
public static void main(String[] args) throws MalformedURLException {
JFrame frame = new JFrame("Display Image");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawLine m = new DrawLine("A crossed bug", new ImageIcon(new URL(BUG)));
frame.add(m);
frame.pack();
frame.setVisible(true);
}
}
class DrawLine extends JLabel {
DrawLine(String text, Icon icon) {
super(text, icon, SwingConstants.CENTER);
setVerticalTextPosition(SwingConstants.BOTTOM);
setHorizontalTextPosition(SwingConstants.CENTER);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.YELLOW);
g2d.setStroke(new BasicStroke(10));
int w = getWidth(); int h = getHeight();
g2d.drawLine(0, 0, w, h); //draw right to left diagonal
g2d.drawLine(0, h, w, 0); //draw left to right diagonal
}
}
Swing is a single Thread library. All painting tasks are executed in the Event Dispatcher Thread (EDT).
Running long processes (such as sleep) on the EDT makes keeps this thread busy, so it does not do other things like updating the gui. The gui becomes unresponsive (freezes).
If you want to add a line after a certain delay, use swing Timer for the job:
public class Main extends JFrame {
private static final String BUG = "https://www.growtopiagame.com/forums/attachment.php?attachmentid=141847&d=1477126665";
public static void main(String[] args) throws MalformedURLException {
JFrame frame = new JFrame("Display Image");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawLine m = new DrawLine("A crossed bug", new ImageIcon(new URL(BUG)));
frame.add(m);
frame.pack();
frame.setVisible(true);
}
}
class DrawLine extends JLabel {
private static final int DELAY = 1500; //millies
private boolean isDrawSecondDialgonal = false;
DrawLine(String text, Icon icon) {
super(text, icon, SwingConstants.CENTER);
setVerticalTextPosition(SwingConstants.BOTTOM);
setHorizontalTextPosition(SwingConstants.CENTER);
//use timer to enable painting of a second diagonal
javax.swing.Timer timer = new javax.swing.Timer(DELAY, e-> {
isDrawSecondDialgonal = true;
repaint();
});
timer.setRepeats(false);
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.YELLOW);
g2d.setStroke(new BasicStroke(10));
int w = getWidth(); int h = getHeight();
g2d.drawLine(0, 0, w, h); //draw right to left diagonal
if(isDrawSecondDialgonal) {
g2d.drawLine(0, h, w, 0);//draw left to right diagonal
}
}
}
I have a for loop that iterates within an actionPerformed method. Basically, I have a car game. I have a panel where car images travel from side to side in a JPanel. I am trying to have the cars stop at the finish line (I am doing that by sleeping when the image reaches a certain x value) display the race results, leave the screen and race again. I need to do that four times until we have a winner.
private class RaceDisplay extends JPanel implements ActionListener{
private Image img1,img2;
private int velX1,velX2;
private int x1,x2;
private Timer tm;
private JTextArea text1 = new JTextArea();
public RaceDisplay(){
tm = new Timer(30,this);
x1=0;
x2=0;
velX1=2;
velX2 =2;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
ImageIcon car1 = new ImageIcon("...");
ImageIcon car2 = new ImageIcon("...");
img1 = car1.getImage();
img2 = car2.getImage();
g.drawImage(img1,x1,100,null);
g.drawImage(img2,x2,200,null);
tm.start();
}
public void actionPerformed(ActionEvent e) {
x1=x1+velX1;
velX2= x2+velX2;
repaint();
for(int count = 0;count<=4;count++){//<-----loop with issues.
if(count == 1){
text1.setText(result());
}
if(x1>=650 && x2>=650){ //does this when both cars reach the line
velX1=0;
velX2=0;
try {
Thread.sleep(2500);
} catch (InterruptedException ex) {
Logger.getLogger(Display.class.getName()).log(Level.SEVERE, null, ex);
}
x1=0;
x2=0;
repaint();
velX1= x1+velX1;
velX2= x2+velX2;
}
}
repaint();
}
I created the for loop that should check the if statements when it reaches the last counter it displays the winner(method for winner in the code). I was expecting the images to travel from side to four times and display the results four times.
But it only display anything if i set the if(counter==1) to (counter==0).
Can anyone help?
Thanks.
Don't call tm.start(); from within the paintComponent method, this is just asking for trouble. Painting may occur for any number of reasons, many of which you don't control or have knowledge about
Don't call Thread.sleep from within the context of the Event Dispatching Thread. This isn't stopping the Timer, but is prevent the EDT from processing the Event Queue, which includes things like repaint events and timer events...
Instead, once you've detected that a car has passed the finish line, you can either, stop updating that car's position and/or stop the Timer
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
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 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 Map<BufferedImage, Rectangle> carBounds;
private BufferedImage blueCar;
private BufferedImage redCar;
public TestPane() {
carBounds = new HashMap<>(25);
try {
blueCar = ImageIO.read(getClass().getResource("/BlueCar.png"));
redCar = ImageIO.read(getClass().getResource("/RedCar.png"));
int x = 0;
int y = (200 / 2 ) - blueCar.getHeight();
carBounds.put(blueCar, new Rectangle(x, y, blueCar.getWidth(), blueCar.getHeight()));
y = (200 / 2);
carBounds.put(redCar, new Rectangle(x, y, redCar.getWidth(), redCar.getHeight()));
} catch (IOException ex) {
ex.printStackTrace();
}
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (BufferedImage img : carBounds.keySet()) {
Rectangle bounds = carBounds.get(img);
int xDelta = (int)Math.round((Math.random() * 7) + 1);
bounds.x += xDelta;
if (bounds.x + bounds.width > getWidth()) {
bounds.x = getWidth() - bounds.width;
((Timer)e.getSource()).stop();
}
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (BufferedImage img : carBounds.keySet()) {
Rectangle bounds = carBounds.get(img);
g2d.drawImage(img, bounds.x, bounds.y, this);
}
g2d.dispose();
}
}
}
Take a closer look at Concurrency in Swing for more details
class DrawIma extends JPanel{
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i=0;i<20;i++){
for (int j=0;j<20;j++) {
g.drawImage(BuArr[i*20+j], 20*i, 20*j, 20, 20, null);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
In this part, BuArr are the 400 blocks divided from a BufferedImage, now i want them to be draw one by one, but the method can not draw the blocks separately, how can i do this?
Swing is single thread and not thread safe.
This means that you should not perform any long running or blocking (Thread.sleep) operations within the IU thread (the Event Dispatching Thread). It also means that you can not update, modify or create UI elements outside of the EDT context.
Instead, use a Swing Timer to generate a repeated callback at a specified interval and render the portions of the image to something like a BufferedImage, which you can the paint to the component via its paintComponent method...
See Concurrency in Swing and How to use Swing Timers for more details
Because it was a good time waster
This generates a List of Rectangles which represent the individual blocks I want to paint, I then randomise the List and run the Timer, picking the top most Rectangle off the List and using BufferedImage#getSubImage to draw it from the master to the buffer, which gets painted to the screen...
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
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 TestImage {
public static void main(String[] args) {
new TestImage();
}
public TestImage() {
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 BufferedImage master;
private BufferedImage copy;
private List<Rectangle> blocks;
public TestPane() {
setBackground(Color.BLACK);
try {
master = ImageIO.read(new File("..."));
copy = new BufferedImage(master.getWidth(), master.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = copy.createGraphics();
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f);
g2d.setComposite(composite);
g2d.setColor(new Color(0, 0, 0, 0));
g2d.fillRect(0, 0, master.getWidth(), master.getHeight());
g2d.dispose();
int blockSize = 40;
int width = master.getWidth();
int height = master.getHeight();
float aspect = Math.min(width, height) / (float) Math.max(width, height);
int blockHeight = blockSize;
blocks = new ArrayList<>(blockSize * 2);
for (int y = 0; y < master.getHeight(); y += blockHeight) {
if (y + blockHeight > master.getHeight()) {
blockHeight = master.getHeight() - y;
}
int blockWidth = blockSize;
for (int x = 0; x < master.getWidth(); x += blockWidth) {
if (x + blockWidth > master.getWidth()) {
blockWidth = master.getWidth() - x;
}
Rectangle block = new Rectangle(x, y, blockWidth, blockHeight);
blocks.add(block);
}
}
Collections.shuffle(blocks);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (blocks.isEmpty()) {
((Timer) e.getSource()).stop();
} else {
Graphics2D g2d = copy.createGraphics();
Rectangle block = blocks.remove(0);
g2d.drawImage(master.getSubimage(block.x, block.y, block.width, block.height), block.x, block.y, TestPane.this);
g2d.dispose();
repaint();
}
}
});
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return master == null ? new Dimension(200, 200) : new Dimension(master.getWidth(), master.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (copy != null) {
int x = (getWidth() - copy.getWidth()) / 2;
int y = (getHeight() - copy.getHeight()) / 2;
g2d.drawImage(copy, x, y, this);
}
g2d.dispose();
}
}
}
I'm trying to draw Images with Graphics2D on JFrame.
But this code only displays blank background.
How to do that?
Java Version: SE-1.6
IDE: Eclipse
My code looks like this:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.awt.geom.Line2D;
import java.util.TimerTask;
import javax.swing.JFrame;
public class GraphicTest extends JFrame{
public static void main(String[] args) {
GraphicTest gt = new GraphicTest();
gt.start();
}
JFrame frame;
BufferStrategy strategy;
GraphicTest(){
int width = 320;
int height = 240;
this.frame = new JFrame("test");
this.frame.setSize(width, height);
this.frame.setLocationRelativeTo(null);
this.frame.setLocation(576, 336);
this.frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
this.frame.setUndecorated(true);
this.frame.setBackground(new Color(0, 0, 0, 50));
this.frame.setVisible(true);
this.frame.setIgnoreRepaint(true);
this.frame.createBufferStrategy(2);
this.strategy = this.frame.getBufferStrategy();
}
public void onExit(){
System.exit(0);
}
void start(){
java.util.Timer timer = new java.util.Timer();
timer.schedule(new RenderTask(), 0, 16);
}
class RenderTask extends TimerTask{
int count = 0;
#Override
public void run() {
GraphicTest.this.render();
}
}
void render() {
// Some moving images
Graphics2D g2 = (Graphics2D)this.strategy.getDrawGraphics();
g2.setStroke(new BasicStroke(5.0f));
Line2D line = new Line2D.Double(20, 40, 120, 140);
g2.draw(line);
this.strategy.show();
}
}
Thank you for any help you can provide.
BufferStrategy is normally associated with heavy weight components, which don't have any concept of transparency.
Transparent and translucent (per alpha pixeling) is not "officially" supported under Java 6
Making a window translucent effects anything else painted to it...this very annoying, regardless if you are using Java 6 or 7
The secret is to make the Window transparent to begin with, then overlay a transparent component that has a special "translucent" paint effect.
Under Java 6 (update 10 I think), there became available a private API called AWTUtilities which provide the ability to make a window transparent or translucent, the following example is based on that API.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
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 TransparentWindowAnimation {
public static void main(String[] args) {
new TransparentWindowAnimation();
}
public TransparentWindowAnimation() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
if (supportsPerAlphaPixel()) {
try {
JFrame frame = new JFrame("Testing");
frame.setUndecorated(true);
setOpaque(frame, false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PaintPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception exp) {
exp.printStackTrace();
}
} else {
System.err.println("Per pixel alphering is not supported");
}
}
});
}
public static boolean supportsPerAlphaPixel() {
boolean support = false;
try {
Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
support = true;
} catch (Exception exp) {
}
return support;
}
public static void setOpaque(Window window, boolean opaque) throws Exception {
try {
Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
if (awtUtilsClass != null) {
Method method = awtUtilsClass.getMethod("setWindowOpaque", Window.class, boolean.class);
method.invoke(null, window, opaque);
}
} catch (Exception exp) {
throw new Exception("Window opacity not supported");
}
}
public class PaintPane extends JPanel {
private BufferedImage img;
private int xPos, yPos = 100;
private int xDelta = 0;
private int yDelta = 0;
public PaintPane() {
while (xDelta == 0) {
xDelta = (int)((Math.random() * 8)) - 4;
}
while (yDelta == 0) {
yDelta = (int)((Math.random() * 8)) - 4;
}
setOpaque(false);
try {
img = ImageIO.read(new File("AngryBird.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += xDelta;
yPos += yDelta;
if (xPos - (img.getWidth() / 2) <= 0) {
xPos = img.getWidth() / 2;
xDelta *= -1;
}
if (xPos + (img.getWidth() / 2) >= getWidth()) {
xPos = getWidth() - (img.getWidth() / 2);
xDelta *= -1;
}
if (yPos - (img.getHeight() / 2) <= 0) {
yPos = img.getHeight() / 2;
yDelta *= -1;
}
if (yPos + (img.getHeight() / 2) >= getHeight()) {
yPos = getHeight() - (img.getHeight() / 2);
yDelta *= -1;
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(128, 128, 128, 128));
g2d.fillRect(0, 0, getWidth(), getHeight());
int x = xPos - (img.getWidth() / 2);
int y = yPos - (img.getHeight()/ 2);
g2d.drawImage(img, x, y, this);
g2d.dispose();
}
}
}
Another way can be seen here. It can be accomplished by
frame.setBackground(new Color(0, 0, 0, 0));
....
setOpaque(false); //for the JPanel being painted on.
So, I'm trying to dynamically draw a Polygon starting from when I click the mouse until I stop dragging and release. Instead of, for the purpose of this question, a square outline being drawn when I click, drag down, then right-across, then up, then left-across, this is what happens: http://imgur.com/t8ZN3Pp
Any suggestions?
Notes:
model.addPolygon() creates a Polygon with starting points and adds it to an ArrayList called 'polys'
model.addPolygonPoint() adds points to this created polygon that is stored in 'polys'
my paint function iterates through polys to paint
public void mousePressed(MouseEvent e) {
oldX = e.getX();
oldY = e.getY();
model.addPolygon(oldX, oldY);
}
public void mouseDragged(MouseEvent e) {
currentX = e.getX();
currentY = e.getY();
model.addPolyPoint(currentX, currentY);
repaint();
}
.
.
. then in paintComponent:
for (ListIterator<Polys> iter =
model.polys.listIterator(model.polys.size());
iter.hasPrevious();){
graphics2D.draw(iter.previous().poly);
Full paintComponent:
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (image == null) {
image = createImage(getSize().width, getSize().height);
graphics2D = (Graphics2D) image.getGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
g.drawImage(image, 0, 0, null);
for (ListIterator<Polys> iter =
model.polys.listIterator(model.polys.size());
iter.hasPrevious();){
graphics2D.draw(iter.previous().poly);
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
public class Testing {
private static int lastX;
private static int lastY;
private static int modX;
private static int modY;
private static final BasicStroke STROKE = new BasicStroke(2.0F);
private static final Point[] SHAPE = new Point[]{
new Point(10, 10),
new Point(10, 40),
new Point(60, 90),
new Point(50, 50)
};
public static void main(final String[] args) {
final JFrame frame = new JFrame("Polygon Movement");
final JPanel pane = new JPanel() {
#Override
public void paintComponent(final Graphics g1) {
final Graphics2D g = (Graphics2D) g1;
g.setColor(Color.RED);
g.translate(modX, modY);
g.setStroke(STROKE);
for (int i = 0; i < SHAPE.length; i++) {
g.drawLine(SHAPE[i].x, SHAPE[i].y, SHAPE[(i + 1) % SHAPE.length].x, SHAPE[(i + 1) % SHAPE.length].y);
}
}
};
pane.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
modX += e.getX() - lastX;
modY += e.getY() - lastY;
lastX = e.getX();
lastY = e.getY();
frame.repaint();
}
});
pane.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
}
});
pane.setPreferredSize(new Dimension(200, 200));
frame.add(pane);
frame.pack();
frame.setVisible(true);
}
}
As you can see, I make a basic shape with defined points. It is the most effective way to do this, unless you wish to change the basic shape (here it is static). In that case, you find the point the mouse it 'grabbing' and modify that one. Either way, no adding or removing of points is needed. I use the terms lastX instead of oldX just by preference.
The BasicStroke is quite optional, same with casting to a Graphics2D object.
The line:
g.drawLine(SHAPE[i].x, SHAPE[i].y, SHAPE[(i + 1) % SHAPE.length].x, SHAPE[(i + 1) % SHAPE.length].y);
Should make some sense if you've tried this thing before. It iterates through all the points, drawing a line from the current (SHAPE[i]) to the next (SHAPE[(i + 1) & SHAPE.length).
The reason behind that logic, is that say you have 4 points, as we do here. The last iteration through them, you will be given i = 3. Due to this and the array only containing 4 indexes (0-3), we must get that value back down to zero. For simplicity I use the % SHAPE.length so there wouldn't be a need for special cases.
I also opted to use adapters seeing as there were only 2 methods needed of the 7 possible ones.
If you have any questions feel free to ask me about this.
~Legend
If all you want to do is draw a polygon. You could simply use the Shape API
This will allow you to "add" points to the shape and allow the shape to paint itself.
Here I use a simple Path2D shape, as it allows me to append points over time. I keep a running list of shapes, which allows me to generate multiple polygons as required
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawPolygon {
public static void main(String[] args) {
new DrawPolygon();
}
public DrawPolygon() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PolyPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class PolyPane extends JPanel {
private MouseHandler mouseHandler;
private Path2D currentShape;
private List<Path2D> lstPloys;
private Point lastPoint;
private Point currentPoint;
public PolyPane() {
lstPloys = new ArrayList<>();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void addNotify() {
super.addNotify();
addMouseListener(getMouseHandler());
addMouseMotionListener(getMouseHandler());
}
#Override
public void removeNotify() {
removeMouseListener(getMouseHandler());
removeMouseMotionListener(getMouseHandler());
super.removeNotify();
}
public MouseHandler getMouseHandler() {
if (mouseHandler == null) {
mouseHandler = new MouseHandler();
}
return mouseHandler;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (lastPoint != null) {
g2d.setColor(Color.RED);
g2d.fillOval(lastPoint.x - 2, lastPoint.y - 2, 4, 4);
}
if (currentShape != null) {
g2d.setColor(Color.RED);
g2d.draw(currentShape);
if (lastPoint != null && currentPoint != null) {
System.out.println(lastPoint + " - " + currentPoint);
g2d.setColor(new Color(255, 0, 0, 64));
g2d.draw(new Line2D.Float(lastPoint, currentPoint));
}
}
g2d.setColor(Color.BLACK);
for (Shape shape : lstPloys) {
g2d.draw(shape);
}
g2d.dispose();
}
public class MouseHandler extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
if (e.getClickCount() == 1) {
Point p = e.getPoint();
lastPoint = p;
if (currentShape == null) {
currentShape = new Path2D.Float();
currentShape.moveTo(p.x, p.y);
} else {
currentShape.lineTo(p.x, p.y);
}
repaint();
} else if (e.getClickCount() == 2) {
currentShape.closePath();
lstPloys.add(currentShape);
currentShape = null;
lastPoint = null;
repaint();
}
}
}
#Override
public void mouseMoved(MouseEvent e) {
if (currentShape != null) {
currentPoint = e.getPoint();
repaint();
} else {
currentPoint = null;
}
}
}
}
}
Take a look at Working with Geometry for more details