My gif animation flickers too much. I've heard about double buffering that can help but how do I do that to my gif animation? Or is there a better faster shortcut to it. This is just a small test applet thing Im doing for fun but will implement the lessons in class.
Context:
import java.net.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class HiroshimaBlock extends Applet implements ActionListener {
TextField distanceText = new TextField(10);
TextField accelerationText = new TextField(10);
Button security = new Button("Account Manager");
Button launch = new Button("LAUNCH!");
Button Reportl = new Button("Report Logs");
Image dancer;
URL base;
MediaTracker mt;
Timer tm = new Timer(10, this);
TextArea answers = new TextArea("I am ready for your first trip.", 4, 20,
TextArea.SCROLLBARS_NONE);
Image image;
#Override
public void init() {
setSize(550, 500);
// Some messages for the top of the Applet:
addHorizontalLine(Color.orange);
addNewLine();
// JOptionPane.showMessageDialog(null, "HiroshimaBlock",
// "Welcome to HiroshimaBlock", JOptionPane.PLAIN_MESSAGE);
// The two text fields and the launch button:
Frame c = (Frame) this.getParent().getParent();
c.setTitle("HiroshimaBlock");
mt = new MediaTracker(this);
try {
base = getDocumentBase();
} catch (Exception e) {
}
dancer = getImage(base, "dancer1.gif");
mt.addImage(dancer, 9);
try {
mt.waitForAll();
} catch (InterruptedException e) {
}
loadImage();
// add(distanceText);
// add(new Label("Distance of trip in light years"));
addNewLine();
addNewLine();
// add(accelerationText);
// add(new Label("Acceleration of rocket in g's"));
addNewLine();
add(launch);
addNewLine();
add(security);
addNewLine();
add(Reportl);
// A text area for printing the answers:
// answers.setEditable(false);
// add(answers);
addNewLine();
addNewLine();
addHorizontalLine(Color.orange);
}
public void loadImage() {
URL url = getClass().getResource("hsblock.png");
image = getToolkit().getImage(url);
}
public void paint(Graphics g) {
g.drawImage(image, 20, 20, this);
this.security.setLocation(25, 200);
addNewLine();
this.launch.setLocation(25, 230);
this.Reportl.setLocation(25, 260);
this.security.setSize(100, 25);
this.launch.setSize(100, 25);
this.Reportl.setSize(100, 25);
g.drawImage(dancer, 150, 200, this);
tm.start();
}
private void addHorizontalLine(Color c) {
// Add a Canvas 10000 pixels wide but only 1 pixel high, which acts as
// a horizontal line to separate one group of components from the next.
Canvas line = new Canvas();
line.setSize(10000, 1);
line.setBackground(c);
add(line);
}
private void addNewLine() {
// Add a horizontal line in the background color. The line itself is
// invisible, but it serves to force the next Component onto a new line.
addHorizontalLine(getBackground());
}
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
}
You want to:
Create a drawing class that extends JPanel (or JComponent)
Override the paintComponent(Graphics g) method of your class.
Call the super's method first in this method
And then do your animation drawing in this method.
This will give you Swing's automatic double buffering which should help smooth out your animation.
Display your drawing JPanel in your JApplet by adding it to the applet's contentPane.
For example:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.nio.Buffer;
import javax.swing.*;
public class SimpleAnimation extends JApplet {
#Override
public void init() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
DrawPanel drawPanel = new DrawPanel();
getContentPane().add(drawPanel);
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
}
class DrawPanel extends JPanel {
private static final int I_WIDTH = 20;
private static final int I_HEIGHT = 20;
private static final int TIMER_DELAY = 15;
private int x = 0;
private int y = 0;
private BufferedImage img = new BufferedImage(I_WIDTH, I_HEIGHT, BufferedImage.TYPE_INT_ARGB);
public DrawPanel() {
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.red);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.fillOval(1, 1, I_WIDTH - 2, I_HEIGHT - 2);
g2.dispose();
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, x, y, this);
}
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
x++;
y++;
repaint();
}
}
}
Well LOL I made it stop flickering by using update LOLOLOL
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Test extends Applet {
Image img;
public void init() {
setSize(700, 700);
img = getImage(getDocumentBase(), "dancer1.gif");
}
public void update(Graphics g) {
g.drawImage(img, 140, 200, this);
}
public void paint(Graphics g) {
update(g);
}
}
Related
I am trying to rotate a JLabel 90 degrees that shows the current time 90 degrees. After doing some research, most people have recommended using Graphics2D and AffineTransform. This almost works, but when the minute in the time is updated, the new digit appears to merge with the old digit.
This does not happen for the seconds. Does anybody have any idea how to fix this issue or have an alternate solution?
Driver class:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class Driver extends JFrame implements KeyListener {
private boolean running = true;
ClockWidget clockWidget;
static Dimension screenSize;
public static void main(String[] args) {
screenSize = Toolkit.getDefaultToolkit().getScreenSize();
DisplayMode displayMode = new DisplayMode((int) screenSize.getWidth(), (int) screenSize.getHeight(), 32,
DisplayMode.REFRESH_RATE_UNKNOWN);
new Driver().run(displayMode);
}
public void run(DisplayMode displayMode) {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(null);
getContentPane().setBackground(Color.BLACK);
setFont(new Font("Arial", Font.PLAIN, 24));
Screen screen = new Screen();
screen.setFullScreen(displayMode, this);
initClockWidgit();
addKeyListener(this);
System.out.println("RUNNING");
while (running) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
quitProgram(screen);
return;
}
public void initClockWidgit() {
clockWidget = new ClockWidget();
clockWidget.setFont(new Font("Arial", Font.PLAIN, 36));
clockWidget.setForeground(Color.WHITE);
clockWidget.setBackground(Color.BLUE);
clockWidget.setBounds((int) (screenSize.getWidth() * 0.90), (int) (screenSize.getHeight() * 0.10), 250, 100);
add(clockWidget);
new Thread(clockWidget).start();
}
public void quitProgram(Screen screen) {
screen.restoreScreen();
clockWidget.disable();
}
#Override
public void keyPressed(KeyEvent keyEvent) {
int keyCode = keyEvent.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE) {
running = false;
}
keyEvent.consume();
}
#Override
public void keyReleased(KeyEvent keyEvent) {
keyEvent.consume();
}
#Override
public void keyTyped(KeyEvent keyEvent) {
keyEvent.consume();
}
}
ClockWidget Class:
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.swing.JLabel;
public class ClockWidget extends RotatedJLabel implements Runnable{
private String currentTime;
private boolean running;
public ClockWidget() {
running = true;
}
#Override
public void run() {
while(running) {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("hh:mm:ss a");
currentTime = simpleDateFormat.format(calendar.getTime());
setText(currentTime);
}
}
public void disable() {
running = false;
}
}
RotatedJLabel Class:
[import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import javax.swing.Icon;
import javax.swing.JLabel;
public class RotatedJLabel extends JLabel {
public RotatedJLabel() {
super();
}
public RotatedJLabel(Icon image) {
super(image);
}
public RotatedJLabel(Icon image, int horizontalAlignment) {
super(image, horizontalAlignment);
}
public RotatedJLabel(String text) {
super(text);
}
public RotatedJLabel(String text, Icon icon, int horizontalAlignment) {
super(text, icon, horizontalAlignment);
}
public RotatedJLabel(String text, int horizontalAlignment) {
super(text, horizontalAlignment);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform aT = g2.getTransform();
Shape oldshape = g2.getClip();
double x = getWidth()/2.0;
double y = getHeight()/2.0;
aT.rotate(Math.toRadians(90), x, y);
g2.setTransform(aT);
g2.setClip(oldshape);
super.paintComponent(g);
}
}
A few things jump out at me:
I wouldn't use a JLabel for this purpose, it's a complicate component, starting with a JPanel and simply painting the text would be simpler. In my testing it was very hard to get the sizing hints to work correctly when the graphics context was rotated.
You're not managing the component's "new" sizing hints, this could be an issue when coupled with more complex layouts, as the width of the component should now be the height and visa-versa
I'd recommend the key bindings API over KeyListener
Swing is NOT thread safe, updating the UI from outside the context of the UI could produce any number of issues; instead of using a Thread, you should be using a Swing Timer, and since you probably really only want to update the seconds, running it at a much slower speed. See Concurrency in Swing and How to Use Swing Timers for more details
And Calendar and Date are effectively deprecated. See Standard Calendar for more details
For example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
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 RotatedLabel timeLabel;
private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm:ss a");
public TestPane() {
setLayout(new GridBagLayout());
timeLabel = new RotatedLabel(currentTime());
add(timeLabel);
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timeLabel.setText(currentTime());
}
});
timer.start();
}
public String currentTime() {
LocalTime lt = LocalTime.now();
return lt.format(formatter);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class RotatedLabel extends JPanel {
private String text;
public RotatedLabel() {
super();
setOpaque(false);
setFont(UIManager.getDefaults().getFont("label.font"));
}
public RotatedLabel(String text) {
this();
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
revalidate();
repaint();
}
protected Dimension getTextBounds() {
FontMetrics fm = getFontMetrics(getFont());
return new Dimension(fm.stringWidth(text), fm.getHeight());
}
#Override
public Dimension getPreferredSize() {
Dimension size = getTextBounds();
return new Dimension(size.height, size.width);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform aT = g2.getTransform();
double x = getWidth() / 2.0;
double y = getHeight() / 2.0;
aT.rotate(Math.toRadians(90), x, y);
g2.setTransform(aT);
FontMetrics fm = g2.getFontMetrics();
float xPos = (getWidth() - fm.stringWidth(getText())) / 2.0f;
float yPos = ((getHeight() - fm.getHeight()) / 2.0f) + fm.getAscent();
g2.drawString(text, xPos, yPos);
g2.dispose();
}
}
}
Now, if you "absolutely, must, no questions asked" use a component like Label, then I recommend using JLayer instead.
Unfourtantly, I've not had time to update my examples to use JLayer, but they use the predecessor library, JXLayer
Java rotating non-square JPanel component
Is there any way I can rotate this 90 degrees?
I am trying to simply rotate an image in a for loop like so:
class MyCanvas extends JComponent {
AffineTransform identity = new AffineTransform();
Image arrow;
Double angle = -180.0;
public void spin() {
angle += 10.0;
for(int i = 0; i < 10; i++) {
repaint();
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
arrow = Toolkit.getDefaultToolkit().getImage("red-arrow-right-th.png");
// Rotate + translate
AffineTransform trans = new AffineTransform();
trans.setTransform(identity);
trans.translate(getWidth()/2, getHeight()/2);
trans.rotate(Math.toRadians(angle));
System.out.println(trans);
g2.drawImage(arrow, trans, this);
g2.finalize();
}
}
However when I run call spin() in main, it appears to apply only a single rotation, whilst still printing out the loop correctly. What am I overlooking something?
I've transformed your code using the recommendation of MadProgrammer:
Don't override paint, override paintComponent.
Call super.paint before performing any custom painting,
Never call finalize on anything and especially not on objects you didn't create yourself.
Use a Swing Timer
Note the following
A qualified this is used to access the ImageRotationView instance from the ActionListener inner class.
AffineTransform.getRotateInstance returns a transform that rotates coordinates around an anchor point.
Speed can be optimized but it works correctly like this.
This class works as a standalone application
A file named dice.png should be present in the base directory.
.
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
public class ImageRotationFrame {
public static void main(String[] args) {
new ImageRotationFrame();
}
public ImageRotationFrame() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Testing");
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ImageRotationComponent());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private class ImageRotationComponent extends JComponent {
Image arrow;
double angle = 0.0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
angle += 0.4;
AffineTransform trans = AffineTransform.getRotateInstance(angle, getWidth() / 2, getHeight() / 2);
((Graphics2D) g).drawImage(arrow, trans, this);
}
public ImageRotationComponent() {
try {
arrow = ImageIO.read(new File("dice.png"));
} catch (IOException e) {
e.printStackTrace();
}
int delay = 500; //milliseconds
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
ImageRotationComponent.this.repaint();
}
};
new Timer(delay, taskPerformer).start();
}
}
}
I'm trying to render a dynamically changing image on a JPanel. I have tried many things and I simply do not get how to do it or what I'm doing wrong and I'm at the point of going insane. I'm new to swing and this is a small but essential part of my program which enables me to see what I'm doing.
The simple requirement is the picture rendered initially in the JPanel in the code mentioned below should update at each tick with random colors. I cannot get the image to update. Instead I get another square appearing in the middle of the image I'm trying to update and that small square dynamically changes. I do not know what I'm doing wrong. Please let me know how to rectify the code below so I could get the whole image to update.
Thank you.
The code is as follows
(MainTest.java)
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.Timer;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
public class MainTest extends JFrame {
static ImageEditor img ;
JPanel panel;
Timer to;
public MainTest()
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(265, 329);
getContentPane().setLayout(null);
img = new ImageEditor();
panel = new ImageEditor();
panel.setBounds(10, 11, 152, 151);
panel.add(img);
getContentPane().add(panel);
JButton btnIterate = new JButton("Iterate");
btnIterate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ThirdClass t = new ThirdClass(img);
to = new Timer(10,t);
to.start();
}
});
btnIterate.setBounds(10, 230, 89, 23);
getContentPane().add(btnIterate);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
MainTest frame = new MainTest();
//frame.getContentPane().add(imgx);
frame.setVisible(true);
}
});
}
}
(ImageEditor.java)
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
#SuppressWarnings("serial")
public class ImageEditor extends JPanel {
private static BufferedImage img = new BufferedImage(100, 100, 1);
public ImageEditor() {
render();
}
public void paintComponent(Graphics g) {
if (img == null)
super.paintComponents(g);
else
g.drawImage(img, 0, 0, this);
}
public void render() {
float cellWidth = 10;
float cellHeight = 10;
int imgW = img.getWidth();
int imgH = img.getHeight();
float r, g, b;
Graphics2D g2 = img.createGraphics();
g2.setBackground(Color.black);
g2.clearRect(0,0,imgW,imgH);
for (int x=0; x<100; x++) {
for (int y=0; y<100; y++) {
r = (float)Math.random();
g = (float)Math.random();
b = (float)Math.random();
g2.setColor(new Color(r,g,b));
g2.fillRect((int)(x*cellWidth), (int)(y*cellHeight),
(int)cellWidth+1, (int)cellHeight+1);
}
}
g2.setColor(Color.black);
g2.dispose();
repaint();
}
public BufferedImage getImage() {
if (img == null)
img = (BufferedImage)createImage(100, 100);
return img;
}
public void setImage(BufferedImage bimg) {
img = bimg;
}
}
(ThirdClass.java)
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ThirdClass implements ActionListener {
static ImageEditor mock;
public ThirdClass(ImageEditor img)
{
mock = img;
Train();
}
public void Train()
{
mock.render();
}
#Override
public void actionPerformed(ActionEvent arg0) {
Train();
}
}
From the code:
img = new ImageEditor();
panel = new ImageEditor();
panel.setBounds(10, 11, 152, 151);
panel.add(img);
getContentPane().add(panel);
You are defining img as an ImageEditor, but you probably meant for it to be a BufferedImage. You are then adding it to panel, which is another ImageEditor - but it is being added through the superclass method JPanel.add, when you probably meant to use the method you wrote, ImageEditor.setImage().
EDIT: To summarize:
The tiny block of updating pixels you described is the BufferedImage (img.img), inside of your ImageEditor img, which is in turn inside of the ImageEditor panel, which is itself inside the applet's content pane.
replace static ImageEditor img in the MainTest class with BufferedImage img
replace img = new ImageEditor in MainTest's no-args constructor with img = new BufferedImage()
replace panel.add(img) in MainTest's no-args constructor with panel.setImage(img)
replace static ImageEditor mock in the ThirdClass class with BufferedImage mock
replace the ImageEditor img argument in ThirdClass's constructor with BufferedImage img
replace any other stuff involving img to correctly handle it as a BufferedImage
One other thing, although this problem isn't itself the cause of any program malfunction, you should rename the Train() method in ThirdClass to something like train() so it matches proper java style.
After my initial post of the above question I have gone back to the basics and managed to answer my own question.
I am inserting the complete code below so another user having the same problem would not have to go through the same process over again.
My mistake was very trivial. However for people starting swing I think this might be helpful
(MainTest.java)
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.Timer;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class MainTest extends JFrame {
ImageEditor img;
JPanel panel;
Timer t;
public MainTest()
{
setSize(600,600);
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().setLayout(null);
img = new ImageEditor();
panel = img;
panel.setBounds(0, 0, 510, 510);
getContentPane().add(panel);
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
test();
}
});
btnStart.setBounds(0, 528, 89, 23);
getContentPane().add(btnStart);
}
private void test() {
Train tx = new Train(img);
t = new Timer(100, tx);
t.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
MainTest frame = new MainTest();
frame.setVisible(true);
}
});
}
}
(ImageEditor.java)
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
#SuppressWarnings("serial")
public class ImageEditor extends JPanel {
private static BufferedImage img = new BufferedImage(500, 500, 1);
public ImageEditor() {
setLayout(null);
}
public void paintComponent(Graphics g) {
if (img == null)
super.paintComponents(g);
else
g.drawImage(img, 0, 0, this);
}
public void render() {
float cellWidth = 10;
float cellHeight = 10;
int imgW = img.getWidth();
int imgH = img.getHeight();
float r, g, b;
Graphics2D g2 = img.createGraphics();
g2.setBackground(Color.black);
g2.clearRect(0,0,imgW,imgH);
for (int x=0; x<100; x++) {
for (int y=0; y<100; y++) {
r = (float)Math.random();
g = (float)Math.random();
b = (float)Math.random();
g2.setColor(new Color(r,g,b));
g2.fillRect((int)(x*cellWidth), (int)(y*cellHeight),
(int)cellWidth+1, (int)cellHeight+1);
}
}
g2.setColor(Color.black);
g2.dispose();
repaint();
}
public BufferedImage getImage() {
if (img == null)
img = (BufferedImage)createImage(500, 500);
return img;
}
public void setImage(BufferedImage bimg) {
img = bimg;
}
}
(Train.java this class was named ThirdClass.java in my question)
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Train implements ActionListener {
ImageEditor temp;
Train(ImageEditor img)
{
temp = img;
}
public void train()
{
temp.render();
}
#Override
public void actionPerformed(ActionEvent e) {
train();
}
}
Thank you for all who commented and answered my question.
I have a Java project that's about traffic network simulation in a random city, I've managed to figure out a way to implement this project, so I divided each intersection into a section which is basically an extended JPanel class (named Carrefour)...everything works well until I got stuck with how to draw vehicles and make them pass through roads.
So my problem is how to draw an image (vehicle image) over an another image (road)?
Another approach that does not require extending components.
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.Random;
import java.net.URL;
import javax.imageio.ImageIO;
public class ImageOnImage {
ImageOnImage(final BufferedImage bg, BufferedImage fg) {
final BufferedImage scaled = new BufferedImage(
fg.getWidth()/2,fg.getHeight()/2,BufferedImage.TYPE_INT_RGB);
Graphics g = scaled.getGraphics();
g.drawImage(fg,0,0,scaled.getWidth(),scaled.getHeight(),null);
g.dispose();
final int xMax = bg.getWidth()-scaled.getWidth();
final int yMax = bg.getHeight()-scaled.getHeight();
final JLabel label = new JLabel(new ImageIcon(bg));
ActionListener listener = new ActionListener() {
Random random = new Random();
public void actionPerformed(ActionEvent ae) {
Graphics g = bg.getGraphics();
int x = random.nextInt(xMax);
int y = random.nextInt(yMax);
g.drawImage( scaled, x, y, null );
g.dispose();
label.repaint();
}
};
Timer timer = new Timer(1200, listener);
timer.start();
JOptionPane.showMessageDialog(null, label);
}
public static void main(String[] args) throws Exception {
URL url1 = new URL("http://i.stack.imgur.com/lxthA.jpg");
final BufferedImage image1 = ImageIO.read(url1);
URL url2 = new URL("http://i.stack.imgur.com/OVOg3.jpg");
final BufferedImage image2 = ImageIO.read(url2);
//Create the frame on the event dispatching thread
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new ImageOnImage(image2, image1);
}
});
}
}
If this is Swing, then draw the background image in a BufferedImage. Display this BufferedImage in a JComponent's (such as a JPanel's) paintComponent method using Graphic's drawImage(...) method, and then draw the changing images over this in the same paintComponent method. Don't forget to call the super.paintComponent(...) method first though.
Please note that this question has been asked quite a bit here and elsewhere, and as you would expect, there are lots of examples of this sort of thing that you can find here with a bit of searching.
Edit
You ask:
Thanks, this is how I draw the firt image (road)
Again, you would create a BufferedImage for this, likely by using ImageIO.read(...). Then you'd draw this in your JPanel's paintComponent(Graphics g) method override using g.drawImage(...).
For example...
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.*;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class IntersectionImagePanel extends JPanel {
private static final String INTERSECTION_LINK = "http://www.weinerlawoffice.com/" +
"accident-diagram.jpg";
private BufferedImage intersectionImage;
public IntersectionImagePanel() {
URL imageUrl;
try {
imageUrl = new URL(INTERSECTION_LINK);
intersectionImage = ImageIO.read(imageUrl );
} catch (MalformedURLException e) {
e.printStackTrace();
System.exit(-1);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (intersectionImage != null) {
g.drawImage(intersectionImage, 0, 0, this);
}
}
#Override
public Dimension getPreferredSize() {
if (intersectionImage != null) {
int width = intersectionImage.getWidth();
int height = intersectionImage.getHeight();
return new Dimension(width , height );
}
return super.getPreferredSize();
}
private static void createAndShowGui() {
IntersectionImagePanel mainPanel = new IntersectionImagePanel();
JFrame frame = new JFrame("IntersectionImage");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I am trying to create a game where I have a background image and text on top of that in the form of JLabels. How would I go about doing that?
The main reason I'm doing this is so that I can have 2 different text areas with different font sizes. Using g.drawString() will only let you use 1 text size for the whole thing.
Here is my code so far:
package com.cgp.buildtown;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class Intro extends JPanel implements Runnable {
private static final long serialVersionUID = 1L;
private Thread thread;
private BufferedImage bg;
private Font font;
public Intro() {
super();
loadImages();
setFont(loadFont(50f));
}
private Font loadFont(Float f) {
try {
font = Font.createFont(Font.TRUETYPE_FONT, new File("res/komikatext.ttf"));
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(font);
} catch (FontFormatException | IOException e) {
e.printStackTrace();
}
return font.deriveFont(f);
}
private void loadImages() {
try {
bg = ImageIO.read(new File("res/introbg.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
public void addNotify() {
super.addNotify();
thread = new Thread(this);
thread.start();
}
public void run() {
while(true) {
repaint();
}
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(bg, 0, 0, null);
}
}
Here is one way.
But of course that is merely a version hacked out of your own code/ specs. For a much better implementation of the same idea, see this Background Panel.
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.net.URL;
import javax.imageio.ImageIO;
public class Intro extends JPanel implements Runnable {
private static final long serialVersionUID = 1L;
private Thread thread;
private BufferedImage bg;
private String html =
"<html><body style='color: yellow;'>" +
"<h1>Game</h1>" +
"<p>Welcome to the Game!";
public Intro() {
super();
loadImages();
setLayout(new BorderLayout());
setBorder(new EmptyBorder(40,40,40,40));
add(new JLabel(html), BorderLayout.NORTH);
add(new JTextField("..enter name"), BorderLayout.SOUTH);
}
private void loadImages() {
try {
URL url = new URL("http://pscode.org/media/stromlo2.jpg");
bg = ImageIO.read(url);
setPreferredSize(new Dimension(bg.getWidth(), bg.getHeight()));
} catch (Exception e) {
e.printStackTrace();
}
}
public void addNotify() {
super.addNotify();
thread = new Thread(this);
thread.start();
}
public void run() {
while(true) {
repaint();
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(bg, 0, 0, this);
}
public static void main(String[] args) {
JOptionPane.showMessageDialog(null, new Intro());
}
}
first load the background image then opaque its property then add the other components on it.