I want to load images in JFrame such a way that it should look like it is video.
For that I thought that I will change Images so much faster (20 images/sec.)
but Problem is when new Image get load its shows fully black window.
I dont know Why it happens.
Suggest me where I goes wrong.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.io.*;
import javax.imageio.ImageIO;
class VideoI extends JPanel {
private Image img;
private Graphics graphics;
ImageIcon icon;
VideoI(){
icon=new ImageIcon("D:\\Videos\\1.jpg");
add(icon);
}
public void paintComponent(Graphics g) {
graphics=g;
repeatImgs();
}
public void repeatImgs(){
for(int i=0;i<25;i++)
{ try{
img=ImageIO.read(new File("D:\\Videos\\"+i+".jpg"));
graphics.drawImage(img, 0, 0, null);
System.out.println(""+i);
Thread.sleep(1000);
}catch(Exception e){System.out.println(""+i+":"+e);}
}
}
}
public class Video extends JFrame
{
public static void main(String args[])
{
new Video().start();
}
public void start()
{
VideoI panel = new VideoI();
add(panel);
setVisible(true);
setSize(1300,800);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
You are blocking the event dispatch thread. Use a swing Timer to repaint the component at the desired frequency.
You should never, ever sleep() in the EDT. What you want is essentially
public void paintComponent(Graphics g) {
// No loops or delays, just fetch the next image, preferrably it has been
// already been loaded by another thread.
g.drawImage(getNextImage(), 0, 0, null);
}
And a timer task:
ActionListener timerTask = new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
panel.repaint();
}
};
Timer timer = new Timer(50, timerTask);
When you want to start the video, just call timer.start().
Finally, you should also wrap creating the GUI with SwingUtilities.invokeLater().
You're sending the Event Dispatch Thread (the UI update thread) to sleep, that's why you get screen issues.
Try loading and switching images in a worker thread (have a look at the SwingWorker JavaDoc).
I'm no Swing expect, but I would guess this happens because you stop the Swing thread with the Thread.sleep. You should do the image changing and timing outside of the swing thread and use SwingUtilities.invokeLater to draw the Image. Also you need to sleep 50ms, not a whole second for 20fps. Using a ScheduledExecutorService whould fit here.
Also you always load the image from disc, when it needs to be rendered. This could be to slow. It would be better to load all image on start up and then just change the image.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I'm new in Java and currently my level is around printing text. Even though, I wanted to start with graphical content but sadly I didn't be able to do it.
I began with JFrame and everything went well but when I had to print images I had problem. Thanks to YouTube I could copy this piece of code where shows clearly (not enough for me, though) how to print an image in a JFrame.
import java.awt.Graphics;
import javax.swing.*;
public class Main extends JPanel{
public static void main(String[] args){
JFrame j = new JFrame("Image");
j.setSize(1080,720);
j.setVisible(true);
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
j.add(new Main());
}
public void paintComponent(Graphics g){
super.paintComponent(g);
ImageIcon i = new ImageIcon("C:\\Users\\Hello\\Pictures\\picture.jpg");
i.paintIcon(this, g, 0, 0);
}
}
I honestly don't understand that. I looked for explanations on internet but no answer has really helped me out. What I don't comprehend is basically j.add(new Main()) (are we linking the same class?) and paintComponent(Graphics g)...
I don't think I've seen so many errors in a supposed teaching example.
Here's the rewritten code. You have to put the image in the same directory as the Java code to read the image.
package com.ggl.testing;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class DrawImage implements Runnable {
#Override
public void run() {
JFrame j = new JFrame("Image");
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
j.add(new ImagePanel(getImage()));
j.pack();
j.setLocationByPlatform(true);
j.setVisible(true);
}
private Image getImage() {
try {
return ImageIO.read(getClass().getResourceAsStream(
"StockMarket.png"));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new DrawImage());
}
public class ImagePanel extends JPanel {
private static final long serialVersionUID = -2668799915861031723L;
private Image image;
public ImagePanel(Image image) {
this.image = image;
this.setPreferredSize(new Dimension(image.getWidth(null), image
.getHeight(null)));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
}
}
Here are the important concepts to take from this code.
Always start a Java Swing application with a call to the SwingUtilities invokeLater method. This puts the creation and updates of the Swing components on the Event Dispatch thread (EDT).
As others have mentioned, read the images before you try and display them. This code will abend if the image is missing. This code will also work when you package your Java class in a JAR file, along with the image.
You don't set any sizes. You let the JFrame and JPanels calculate their own sizes using Swing layouts. In this particular example, the JPanel takes on the size of the image you read, and the JFrame is just large enough to hold the image JPanel.
You use Swing components. You only extend a Swing component when you want to override a method in the class. In this example, we used a JFrame and extended a JPanel.
I am trying to write a simple 2d animation engine in Java for visualizing later programming projects. However, I am having problems with the window refresh. On running, the frame will sometimes display a blank panel instead of the desired image. This begins with a few frames at a time at apparently random intervals, worsening as the program continues to run until the actual image only occasionally blinks into view. The code for processing each frame is run, but nothing in the frame is actually displayed. I believe the problem may come from my computer more than my code (certainly not from bad specs though), but am not sure. Help much appreciated.
Three classes. Code here:
package animator;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.applet.AudioClip;
public class APanel extends JPanel
{
public APanel(int l, int h){
setPreferredSize(new Dimension(l,h));
setLocation(80, 80);
setVisible(true);
setFocusable(true);
}
public Graphics renderFrame(Graphics g){
return g;
}
public void paintComponent(Graphics g) {
requestFocusInWindow();
renderFrame(g);
}
}
package animator;
import java.awt.*;
public class Animator extends APanel
//extending the APanel class allows you to code for different animations
//while leaving the basic functional animator untouched
{
public static final int SCREEN_X = 700;
public static final int SCREEN_Y = 700;
int frameNum;
public Animator() {
super(SCREEN_X, SCREEN_Y);
frameNum = 0;
}
public Graphics renderFrame(Graphics g) {
frameNum++;
g.drawString(""+frameNum,5,12);
return g;
}
}
package animator;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.Timer;
public class Runner {
int framerate = 30;
Animator a = new Animator();
JFrame j = new JFrame();
public Runner(){
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
j.add(a);
start();
j.setSize(a.getPreferredSize());
j.setVisible(true);
}
public void start() {
Timer t = new Timer(1000/framerate, new ActionListener() {
public void actionPerformed(ActionEvent e){
j.getComponent(0).paint(j.getComponent(0).getGraphics());
//The following line of code keeps the window locked to a preferred size
// j.setSize(j.getComponent(0).getPreferredSize());
}
});
t.start();
}
public static void main(String[] args){
Runner r = new Runner();
}
}
There are some serious mistakes in your code which could be the cause or a factor of your problem...
j.setSize(a.getPreferredSize()); is irrelevant, simply use JFrame#pack, you get better results as it takes into account the frame decorations
j.setSize(j.getComponent(0).getPreferredSize()); use JFrame#setResizable and pass it false instead...
NEVER do j.getComponent(0).paint(j.getComponent(0).getGraphics()); this! You are not responsible for the painting of components within Swing, that's the decision of the RepaintManager. Just call j.repaint()
super(SCREEN_X, SCREEN_Y);...just override the getPreferredSize method and return the size you want.
setLocation(80, 80); irrelevant, as the component is under the control of a layout manager
setVisible(true); (inside APanel)...Swing components are already visible by default, with the exception of windows and JInternalFrames
And finally...
public void paintComponent(Graphics g) {
requestFocusInWindow();
renderFrame(g);
}
There is never any need for this method to be made public, you NEVER want someone to be able to call it, this will break the paint chain and could have serious ramifications in the ability for the component to paint itself properly.
You MUST call super.paintComponent before performing any custom painting, otherwise you could end up with all sorts of wonderful paint artifacts and issues
Never modify the state of a component from within a paint method...while it "might" not be an immediate issue calling requestFocusInWindow(); within your paintComponent could have side effects you don't know about...
Instead, it should look more like...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
renderFrame(g);
}
I want to set a background to my jFrame, and I'm using this code:
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DemoBackgroundSwing extends JPanel {
private Image img;
public DemoBackgroundSwing() {
System.out.println("done");
img = Toolkit.getDefaultToolkit().createImage("red.png");
System.out.println("done");
loadImage(img);
System.out.println("done");
}
private void loadImage(Image img) {
try {
MediaTracker track = new MediaTracker(this);
track.addImage(img, 0);
track.waitForID(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected void paintComponent(Graphics g) {
setOpaque(false);
System.out.println("done");
g.drawImage(img, 0, 0, null);
super.paintComponent(g);
}
public static void main(String... argv) {
JFrame frame = new JFrame("Demo Background Image");
DemoBackgroundSwing back = new DemoBackgroundSwing();
System.out.println("done");
frame.getContentPane().add(back);
frame.setSize(400, 287);
frame.setVisible(true);
}
}
when I execute it, in system out I have 5 "done". so this means that all tasks are executed.
I don't understand where's the error. Please Help me!
Print the width of loaded image if it's -1 then image is not properly loaded.
img = Toolkit.getDefaultToolkit().createImage("red.png");
System.out.println(img.getWidth(null)); // check what it prints
It's worth reading Java Tutorial on Loading Images Using getResource
You can try any one based on image location.
// Read from same package
ImageIO.read(getClass().getResourceAsStream("c.png"));
// Read from images folder parallel to src in your project
ImageIO.read(new File("images/c.jpg"));
// Read from src/images folder
ImageIO.read(getClass().getResource("/images/c.png"))
// Read from src/images folder
ImageIO.read(getClass().getResourceAsStream("/images/c.png"))
Read more...
Some Points:
call super.paintComponent(g); at first line in the overridden paintComponent() method.
Use ImageIO instead of Toolkit to load the image.
Use frame.pack() instead of frame.setSize() that fits the components as per component's preferred size.
Override getPreferredSize() to set the preferred size of the JPanel in case of custom painting.
Use SwingUtilities.invokeLater() or EventQueue.invokeLater() to make sure that EDT is initialized properly.
Read more
Why to use SwingUtilities.invokeLater in main method?
SwingUtilities.invokeLater
Should we use EventQueue.invokeLater for any GUI update in a Java desktop application?
I am making an Animated ProgressBar, in which i used multiple fillRect() method of class javax.swing.Graphics.
To put a delay after each rectangle is painted, I am using Thread.sleep(500) method for making a delay, (Suggested by many Forums, for making a delay).
The Problem is, instead of making a delay of 0.5sec after each Rectangle box is displayed, it takes the whole delay required by all the rectangles, in the start, and then displays the final image, thats the Progress Bar.
Question 1
TO make a delay for every single bar, i put the delay "Thread.sleep(500)" along with the bars "fillRect()" in a single for() loop, i would like to know, Why does it takes all the delay in the beginning and then dislplays the completed ProgressBar.
Question 2
How can i change my code, so that the delay can occur simultaneously with each rectangle bar, so when i run the program it should generate an Animated Progress Bar.
Code:
import javax.swing.JOptionPane;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
class DrawPanel extends JPanel
{
public paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(new Color(71,12,3));
g.fillRect(35,30,410,90);
for ( int i=1; i<=40; i+=2)
{
Color c = new Color (12*i/2,8*i/2,2*i/2);
g.setColor(c);
g.fillRect( 30+10*i,35,20,80);
try
{ Thread.sleep(500); }
catch(InterruptedException ex)
{ Thread.currentThread().interrupt(); }
}
}
}
class ProgressBar
{
public static void main (String []args)
{
DrawPanel panel = new DrawPanel();
JFrame app = new JFrame();
app.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
app.add(panel);
app.setSize(500,200);
app.setVisible(true);
}
}
Your help is highly appreciated, Thankyou.
Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens. Instead of calling Thread.sleep(n) implement a Swing Timer for repeated tasks. See Concurrency in Swing for more details. Also be sure to check the progress bar tutorial linked by #Brian. It contains working examples.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class DrawPanel extends JPanel
{
int i = 0;
public DrawPanel() {
ActionListener animate = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
repaint();
}
};
Timer timer = new Timer(50,animate);
timer.start();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(new Color(71,12,3));
g.fillRect(35,30,410,90);
Color c = new Color (12*i/2,8*i/2,2*i/2);
g.setColor(c);
g.fillRect( 30+10*i,35,20,80);
i+=2;
if (i>40) i = 0;
}
}
class ProgressBar
{
public static void main (String []args)
{
DrawPanel panel = new DrawPanel();
JFrame app = new JFrame();
app.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
app.add(panel);
app.setSize(500,200);
app.setVisible(true);
}
}
I really wouldn't do this. The Swing refresh thread isn't supposed to be used like this. You're much better off using another thread (perhaps using a TimerTask) and redrawing rectangles upon demand.
Check out the Oracle ProgressBar tutorial for more info, code etc.
I'm trying to make an application that runs an animation. To do this I've got a Jframe that contains my subclass of Jpanel in which the animation runs. Here are my two classes:
Firstly, here's my driver class:
import javax.swing.*;
public class Life {
public static void main(String[] args){
JFrame game = new JFrame("Life");
game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.setSize(500, 500);
MainPanel mainPanel = new MainPanel();
game.setContentPane(mainPanel);
game.setVisible(true);
}
}
Secondly, here's my subclass of Jpanel:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MainPanel extends JPanel implements ActionListener{
int i = 0;
int j = 0;
public MainPanel(){
super();
}
public void paintComponent(Graphics g){
j++;
g.drawLine(10,10, 20 + i, 20 + i);
Timer t = new Timer(1000, this);
t.start();
}
#Override
public void actionPerformed(ActionEvent actionEvent) {
i++;
repaint();
}
}
Notice that the variable i is incremented each time actionPreformed is called and the variable j is called each time paintComponent is called. What winds up happening is that i starts to be much larger than j and the line drawn by paintComponent seems to grow at a faster and faster rate.
Here are my questions:
Why does this happen?
How can I sync things up so that the line gets redrawn each every 1000 ms?
Given what I'm trying to do, is my approach wrong? Should I be doing things differently?
Thanks in advance.
Don't start a Swing Timer from within a paintComponent method.
This method should do your painting, only your painting and nothing but painting. It should contain absolutely no program logic. Understand that you have very limited control over this method since you cannot predict when or if it will be called, how often it will be called. You can't even call it yourself or be guaranteed that when you suggest it be called via repaint() that it will in fact be called.
Also this method must be fast, as fast possible since anything that slows it down, be it object creation or reading in files will reduce the perceived responsiveness of your GUI, the last thing that you want to see happen.
The solution is to separate the program logic out of that method and into better methods such as your constructor. Repeating code should be in a Swing Timer.
Edit:
You state:
I just did that to test things out. One more question: What happens if paintComponent, or some thread on which the work in paintComponent depends, takes longer than 1000 ms (or whatever it is) to do its work? The only thing I can think of is having paintComponent paint the progress of the animation so far, rather than waiting for the animation to reach the next step(if that makes any sense). Thoughts?
You should never have code in paintComponent that takes that long or even 10's of milliseconds. If there's a risk of something like that happening, then do the drawing in a background thread and to a BufferedImage, and then on the Swing event thread show the BufferedImage in paintComponent method using the Graphics#drawImage(...) method.
A few minor additions to #HFoE's essential insights:
A public start() method is a handy way to ensure that your view is completely constructed before starting.
Fields have well-defined default values, and they should be private.
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
Override getPreferredSize() and pack() the enclosing Window.
Revised code:
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Life {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
private final JTabbedPane jtp = new JTabbedPane();
#Override
public void run() {
JFrame game = new JFrame("Life");
game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MainPanel mainPanel = new MainPanel();
game.setContentPane(mainPanel);
game.pack();
game.setVisible(true);
mainPanel.start();
}
});
}
private static class MainPanel extends JPanel implements ActionListener {
private Timer t = new Timer(100, this);
private int i;
private int j;
#Override
public void paintComponent(Graphics g) {
g.drawLine(10, 10, 20 + i, 20 + i);
}
public void start() {
t.start();
}
#Override
public void actionPerformed(ActionEvent actionEvent) {
i++;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
}
}
A Timer by defaults keep on running. Only when you call setRepeats( false ) it will stop.
So the following lines
Timer t = new Timer(1000, this);
t.start();
in your paintComponent method means that after a few repaints you will have a number of Timer instances running, explaining why i increases that much faster then j.
The solution is of course to move your Timer outside the paintComponent method, and stick to one Timer instance.
Further remarks (which weren't said by the others, not gonna repeat their very useful advise):
Never override the paintComponent method without calling the super method
You shouldn't expose the ActionListener interface. Just use an ActionListener internally