I'm using a JLabel in an attempt to draw an animated gif image onto it. I can use the constructor new JLabel(new ImageIcon(FML.class.getResource("giphy.gif"))); and that will work just fine however when I go override the paint method it just doesn't seem to want to draw it, at all. The image isn't still, it's not there! I should mention that both methods shown below work perfectly fine with a PNG but not a GIF. Am I missing something or is this a java bug?
(Using java 1.8)
Edit: I've been looking around and it seems that I'm not completely off point on what I need to be doing but I'm missing something. I've seen many posts Like this one but that doesn't seem to be working in this case.
I should note that there's literally nothing else going on in the class, it's a simple JPanel.
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FML {
public static void main(String[] args) {
new FML();
}
public FML() {
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.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private ImageIcon animatedGif;
public TestPane() {
setLayout(new GridBagLayout());
JButton btn = new JButton(new ImageIcon(FML.class.getResource("FmDxW.png")));
btn.setSize(50, 50);
btn.setRolloverEnabled(true);
animatedGif = new ImageIcon(FML.class.getResource("giphy.gif"));
btn.setRolloverIcon(animatedGif);
add(btn);
btn.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
animatedGif.getImage().flush();
}
});
//I need this
final ImageIcon image = new ImageIcon(FML.class.getResource("giphy.gif"));
//To render over this
final ImageIcon image2 = new ImageIcon(FML.class.getResource("fmDxW.png"));
//I'm not understanding why I can add the gif into the constructor but the paint method fails.
JLabel label = new JLabel(image) {
#Override
public void paint(Graphics g) {
super.paint(g);
g.drawImage(image2.getImage(), 64, 64, this);
}
};
//setSize because I want to be 100% sure that it's loading the correct size.
//Removing it doesn't affect the problem at hand.
label.setSize(64, 64);
add(label);
}
}
}
g.drawImage(image2.getImage(), 64, 64, this);
Assuming the size of your image is 64, why are you trying to draw the second image outside the bounds of the label? When I change the location to (0, 0) the gif paints fine for me (but I'm not using an animated gif).
both methods shown below work perfectly fine with a PNG but not a GIF.
I don't see how for the reason I gave above. In any case, did you try it with a normal gif?
This is a reason why you should not be trying to manage the painting yourself since now you need to worry about location and hardcoding a location is not a good practice. Let your layout manager do this for you.
Again, why are you doing this. Why are you trying to override paint method to draw a second image?
Instead you should be doing something like:
Drawing both images in the paintComponent() method
Use a panel with an OverlayLayout, Then you can stack the two labels on top of one another.
You can even set the layout manager of the layout to something like a GridBagLayout and then when you add a JLabel to the label it will automatically be centered.
//setSize because I want to be 100% sure that it's loading the correct size.
You said you know how layout managers work. Well if you do then you know that this statement will do nothing since the layout manager will override any value you set, so using code like this wrong and shows a basic lack of understanding and should not be included in an MCVE since the point of the MCVE` is to simplify the problem as much as possible.
Related
I watched a tutorial on YouTube on how to display an image in Java, and he used something called "Applet" and "Graphics" to display it, I've got it to work, and I am happy with it. Now what I was planning to make is a chrome logo in the center of my screen, transparent with no background, and then chrome opens. I've called the class CustomChrome cus you know. Custom and stuff. I just want a cool opening whenever I start chrome.
Here is the current code:
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.net.URL;
public class CustomChrome extends Applet {
/**
*
*/
private static final long serialVersionUID = 1L;
private Image logo = null;
public void paint(Graphics g) {
this.setSize(960, 540);
if (logo == null)
logo = getImage("/chromelgo.png");
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(logo, 0, 0, 960, 540, this);
}
public Image getImage(String path) {
Image tempImage = null;
try {
URL imageURL = CustomChrome.class.getResource(path);
tempImage = Toolkit.getDefaultToolkit().getImage(imageURL);
} catch (Exception e) {
System.out.println("An error occured - " + e.getMessage());
}
return tempImage;
}
}
What I want to do, however, is the make the background + the window to vanish, Once that's was done I will set the image and window size to 1920x1080. How do I go forward on making the window behind the image disappear? I've heard something about implementing ActionListener, but still I ain't sure what to do.
Keep in mind! I have no experience with java so sorry for upsetting you if you try to help me :P
Okay, let's start with the really obvious issue, applets are effectively deprecated and you should stop using them, see Java Plugin support deprecated and Moving to a Plugin-Free Web for more details.
paintXxx is for painting, you should never change the state of a component from within a paint method (and you should not be calling setSize in an applet anyway). See Painting in AWT and Swing and Performing Custom Painting for more details about how painting works and how you can make use of it
As a general piece of advice, I'd recommend ImageIO.read over Toolkit#getImage or ImageIcon for loading images, apart from throwing an Exception when the image can't be read (instead of failing silently), it's also a blocking call, meaning that when it returns, the image is fully loaded.
See Reading/Loading an Image for more details
A word of warning - What you're trying to do is not difficult per se, but it's involved and requires a certain amount of knowledge about how the API works.
Now what I was planning to make is a chrome logo in the center of my screen, transparent with no background, and then chrome opens.
Okay, well, this was never going to work with applets, as applets are intended to be displayed inside a browser, so you can't control the window, instead, you need something like a JFrame, see How to Make Frames (Main Windows)
What I want to do, however, is the make the background + the window to vanish, Once that's was done I will set the image and window size to 1920x1080. How do I go forward on making the window behind the image disappear?
Now, this is where the real fun begins. You're going to want to start by having a look at How to Create Translucent and Shaped Windows. This will allow you control the opacity of the frame, making it disappear, but allowing the content to remain, there are some tricks you need to do to pull it off, but that's the basic idea
So, this example will make a transparent window and display and image centered within it.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
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 {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
BufferedImage img = ImageIO.read(getClass().getResource("/Chrome.png"));
ImageIcon icon= new ImageIcon(img);
JLabel label = new JLabel(icon);
JFrame frame = new JFrame("Testing");
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
}
In this is example, I make use of a JLabel to display the image, in most cases, it's all you really need, see How to Use Labels for more details.
What I want to do, however, is the make the background + the window to vanish
Well, it just comes down to what you mean by vanish. You could just all dispose or setVisible on the window, which would close it, all you could use some animation to make it fade away ... and because I like animation...
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
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 {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
BufferedImage img = ImageIO.read(getClass().getResource("/Chrome.png"));
ImageIcon icon = new ImageIcon(img);
JLabel label = new JLabel(icon);
JFrame frame = new JFrame("Testing");
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer timer = new Timer(40, new ActionListener() {
float opacity = 1.0f;
float delta = 0.05f;
#Override
public void actionPerformed(ActionEvent e) {
opacity -= delta;
if (opacity < 0.0f) {
opacity = 0.0f;
((Timer)(e.getSource())).stop();
frame.dispose();
}
frame.setOpacity(opacity);
}
});
timer.setInitialDelay(2000);
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
}
Okay, there is a lot going on here, you'll want to have a read through Concurrency in Swing and How to use Swing Timers for more details, needless to say, it's a little complicated to get started with.
Basically, all this does is, waits 2 seconds and then every 40 milliseconds, decreases the frames opacity by a factor or 0.05 till it reaches 0 and it then disposes of the window.
Now, I might use a mixture of these techniques, for example, I might show the logo frame (maybe set to display on top), I would then show the main frame and then trigger the logo frame to fade out. This could be done from a controller class to simplify the code, but, that's up to you.
A would also suggest having a look at Creating a GUI With JFC/Swing
So I finally got a Canvas to work the way I want it but it flickers constantly, repaint() is run 20 times a second and the flicking does lessen when I make it run 10 times a second.
package pd.data;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import pd.areas.MainMenu;
#SuppressWarnings("serial")
public class Main extends JFrame implements Runnable {
private JPanel contentPane = new JPanel();
private Thread gameThread = new Thread(this);
public boolean running = false;
#SuppressWarnings("unused")
private int current = PDU.PD_MAIN_MENU;
private MainMenu mainmenu;
public Main() {main.setTitle("PD");
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setLocation(SPU.screenWidth / 2 - SPU.windowSize.width / 2,
SPU.screenHeight / 2 - SPU.windowSize.height / 2);
main.setResizable(false);
main.setVisible(true);
contentPane.setLayout(new BorderLayout(0, 0));
contentPane.setPreferredSize(SPU.windowSize);
main.setContentPane(contentPane);
main.pack();
mainmenu = new MainMenu();
contentPane.add(mainmenu, BorderLayout.CENTER);
this.gameThread.start();
}
#Override
public void run() {
running = true;
while (running) {
{
mainmenu.repaint();
}
try {
Thread.sleep(SPU.TSU);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new Main();
}
}
And below is my MainMenu class:
package pd.areas;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
#SuppressWarnings("serial")
public class MainMenu extends Canvas{
BufferedImage img = null;
public MainMenu() {
this.setBackground(Color.BLACK);
}
public void paint(Graphics graphics){
try {
img = ImageIO.read(this.getClass().getResource(
"/image.png"));
} catch (IOException e1) {
e1.printStackTrace();
}
graphics.drawImage(img, this.getWidth() / 2 - img.getWidth()/2, 50, null);
}
}
Although the flicker is actually a nice effect, it's going to effect the whole canvas and not just the image I'm guessing, how can I fix the flicker?
Don't use java.awt.Canvas. Use a JPanel instead.
Draw in the JPanel's paintComponent method.
Don't forget to call your super's painting method which for paintComponent would be super.paintComponent(graphics)
Never try to read in an image from within any painting method. That will slow down painting and make your program seem unresponsive. Why keep reading in the same image over and over again anyway? Read it in once and save it to a variable.
Though this thread was opened 5 years ago, it's still going to be an issue for anyone new to Java SE.
I had the same problem with Canvas myself, but wasn't convinced enough to switch to javax components because I recalled a VLC project called JVLC which used Canvas to render videos flawlessly (granted jvlc uses some native code).
Tip 1: perhaps it's better to try JPanel instead.
Tip 2: in gaming systems, it's better to use a game engine (it'll save you lots of time).
Tip 3: in gaming systems, implement a fps mechanism rather than calling repaint() on every change.
Tip 4: if you have to call more than 10 lines of code in your paint() implementation you're going to slow it down. It's better to draw to a BufferedImage for every graphical change you need to make to your app, then have the paint() implementation itself draw just the image itself. This should play nicely with an fps mechanism and will reduce the chances of any flickering.
Currently in my code I have a BufferedImage drawn like:
Graphics2D g2d = (Graphics2D) g;
g2d.transform(at); //at is an AffineTransform that just rotates the .gif
g2d.drawImage(sharkie, xCenter-5*radius, yCenter-3*radius, 10*radius, 6*radius, null);
I already have it fully functioning as a BufferedImage, but as expected it shows only the first frame. I was wondering if anyone knew of a way that is analogous to BufferedImage that works for animated gifs. I can't seem to get swing to work with the technique of adding it to a JButton. I also cannot figure out if there is a viable ImageObserver that would animate the .gif in the drawImage() call.
I am willing to try anything, but I am most interested in the possibility of making the draw call work with an ImageObserver as that would be only a small change.
Thanks all!
"I already have it fully functioning as a BufferedImage, but as expected it shows only the first frame"
This will happen when trying to read the image with ImageIO.read(...). If you read it with new ImageIcon(...).getImage(), you'll get the gif animation. See here.
"I also cannot figure out if there is a viable ImageObserver that would animate the .gif in the drawImage() call."
The ImageObserver is the component you are painting on. So instead of using drawImage(..., null), you should be using drawImage(..., this)
"I am willing to try anything, but I am most interested in the possibility of making the draw call work with an ImageObserver as that would be only a small change."
Combine the two points above and you got your answer.
Give this code a test run. gif image taken from this answer
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestGif {
public TestGif() {
JFrame frame = new JFrame();
frame.add(new GifPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class GifPanel extends JPanel {
Image image;
{
try {
image = new ImageIcon(new URL("http://i.stack.imgur.com/lKfdp.gif")).getImage();
} catch (MalformedURLException ex) {
Logger.getLogger(TestGif.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
return image == null ? new Dimension(200, 200)
: new Dimension(image.getWidth(this), image.getHeight(this));
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestGif();
}
});
}
}
I am placing two images. One is supposed to be the background picture and the other one a picture of a stick-figure. I'd like to get the stick figure in front of the background. I can accomplish this by inserting the code to put the background picture after the code to display the stick figure. I was wondering if there was anyway to accomplish the same thing, by inserting the stick figure code after the background code, so I could keep placing new JLabels on top of the background.
The code that works:
guy.setBounds(0,0,100,100);
panel.add(guy);
backgroundPic.setBounds(0,0,550,550);
panel.add(backgroundPic);
setVisible(true);
The code that I'd like to work:
backgroundPic.setBounds(0,0,550,550);
panel.add(backgroundPic);
guy.setBounds(0,0,100,100);
panel.add(guy);
setVisible(true);
If you are only worrying about one background image, then I would suggest to extend JPanel, override paintComponent and paint the background image.
If you have to care about the Z-order of several components, then JLayeredPane is your best alternative.
Here is a small snippet showing the first option:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test2 {
private static class PanelWithBackground extends JPanel {
private Image backgroundImage;
private Point backgroundLocation = new Point();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (getBackgroundImage() != null) {
g.drawImage(getBackgroundImage(), backgroundLocation.x, backgroundLocation.y, this);
}
}
public Image getBackgroundImage() {
return backgroundImage;
}
public void setBackgroundImage(Image backgroundImage) {
this.backgroundImage = backgroundImage;
repaint();
}
public Point getBackgroundLocation() {
return backgroundLocation;
}
public void setBackgroundLocation(Point backgroundLocation) {
this.backgroundLocation = backgroundLocation;
repaint();
}
}
protected static void initUI() throws MalformedURLException {
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
PanelWithBackground panelWithBackground = new PanelWithBackground();
panelWithBackground.setLayout(null);
panelWithBackground.setBackgroundImage(new ImageIcon(new URL(
"http://2.bp.blogspot.com/-PqKuKt5mRmc/Tvi-K-4FVVI/AAAAAAAACKg/YwzkME5gGvk/s1600/black+background.jpg")).getImage());
JLabel guy = new JLabel(new ImageIcon(new URL(
"http://www.clipproject.info/Cliparts_Free/Menschen_Free/Clipart-Cartoon-Design-04.gif")));
// Next 2 lines should rather be performed by a LayoutManager
panelWithBackground.setPreferredSize(new Dimension(1024, 768));
guy.setBounds(50, 200, guy.getPreferredSize().width, guy.getPreferredSize().height);
panelWithBackground.add(guy);
frame.add(panelWithBackground);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
initUI();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
You can use the Z order, to indicate the order the components should be rendered:
java.awt.Container.getComponentZOrder(Component)
According documentation:
Returns the z-order index of the component inside the container. The
higher a component is in the z-order hierarchy, the lower its index.
The component with the lowest z-order index is painted last, above all
other child components.
Parameters: comp the component being queried
Returns: the z-order
index of the component; otherwise returns -1 if the component is null
or doesn't belong to the container
I wont claim to be a java expert nor will i claim this is correct, just a guess, but try and remove the
.setBounds()
and if that has no effect, try using
.setSize()
best of luck :)
I've found that my container is actually changing it's size a short while after being constructed
When it's constructed, I set my components to be at the place I want (like 30px away from the right edge) but later after a short while, I find that it turns from 1008x730 to 1018x740...
(My JFrame is 1024x768)
Does anyone know why this happens and how I can stop this auto-resizing thing?
Thank you.
I just did a -
while (true) {
System.out.println(c.getSize());
}
And the size changed after a few iterations.
~Edited
It sounds like you're doing something that changes a component's size and revalidates after calling pack() on the JFrame. Also, rather than calling setSize(), it's often better to set a component's preferred size and let the LayoutManager arrange things.
Addendum: It's generally better to invoke pack() on a top-level container in order to get the initial sizes correct. Also, the content pane of a JFrame defaults to BorderLayout, which may not be what you want.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MyPanel extends JPanel {
public MyPanel() {
this.setLayout(new GridLayout(2, 2));
this.setPreferredSize(new Dimension(320, 240));
for (int i = 0; i < 4; i++) {
JLabel label = new JLabel(String.valueOf(i));
label.setHorizontalAlignment(JLabel.CENTER);
label.setBorder(BorderFactory.createLineBorder(Color.blue));
this.add(label);
}
}
private static void create() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
create();
}
});
}
}
If you can run this from your IDE or attach remotely with the debugger it would be pretty easy to just set a breakpoint on the methods that set the size.
Or alternately, you could subclass JFrame with your own class and similarly override those methods and do
try { throw new Exception("here!"); } catch(Exception e) { e.printStackTrace(); }
That would tell you what is causing the change in size.
Sounds like the layout manager kicking in. Try running with the Swing Explorer to see what it thinks of the world.