im trying to insert a gif as a background for my app. I cut all frames and renamed them f1/f2/f3/f4/f5/f6/..... I would use a timer to change the frame so it looks like an animation.
There is a total of 42 frames, so f42.png is the last frame. The code seems to be fine, but there is no result. Any help?
Global variables:
private String backgroundFile;
public JPanel backgroundPanel, areaImage;
private BufferedImage background;
private javax.swing.Timer timerBackground;
Constructor where the Timer is initialized:
public Game()
{
entryWindow();
this.setLayout(null);
timerBackground = new javax.swing.Timer(100,this);
timerBackground.stop();
}
Animation method code:
private void backgroundAnimation()
{
backgroundFile = "f"+backgroundNum+".png";
try{
background=ImageIO.read(new File(backgroundFile));
}
catch(IOException e)
{
}
backgroundPanel = new JPanel()
{
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(background, 0, 0, 1100,800,null);
}
};
backgroundPanel.setBackground(Color.BLACK);
backgroundPanel.setBounds(0, 0, 1100, 800);
if (backgroundNum>42)backgroundNum++;
else backgroundNum=1;
add(backgroundPanel);
backgroundPanel.setVisible(true);
}
Action Listener for timer:
if (ae.getSource() == timerBackground)
{
backgroundAnimation();
}
In order to show JPanel, you need to add it to something like JFrame with an BorderLayout for instance, then you need to show the JFrame. JFrame is a application window, the JPanel can be only added and drawn on Window, it can't be viewed without something on which it can draw (like app Window). Beside that you don't need to create new JPanel each time the animation changes, just make a setter for the current image to show, and after assigning the image call repaint(), the ImagePanel could be like this:
public class ImagePanel extends JPanel {
private volatile BufferedImage image;
public void showImage(BufferedImage image) {
this.image=image;
repaint();
}
public void paintComponent(Graphics g) {
g.drawImage(image, 0,0,getWidth(),getHeight(),null);
}
}
add it to your JFrame at application start, also set the LayoutManager of JFrame to BorderLayout preferably, because without that your panel will have size(0,0) since you didn't set it, and it could be one of reasons why you don't see it (you can't see something which is 0 pixel in size, can you?).
Then in your timer just call the ImagePanel method public void showImage(BufferedImage image) with the image to show. If that's don't solve your problem, then post your entire code. As without that i'm just guessing, but those are common problems, so there's big chance you hit something from this.
I can see a few issues here
1. Assuming your Game class is extending JFrame, You need to add the JPanel to the ContentPane of the JFrame. Use one of the approaches setContentPane(backgroundPanel); or getContentPane().add(backgroundPanel)
You are not using a LayoutManager. So either use a LayoutManager or set the Size of the 'JFrame' and 'JPanel' explicitly using setBounds() method. I would recommend using a LayoutManager.
The JPanel or any Component for that matter does not automatically refresh itself. Once you change the image, you need to call repaint() on your JPanel.
You dont need to create a new JPanel every time you change the image. Just extend the JPanel and override the paintComponent()like you have done. Use the Timer to change the image of that single instance and call repaint() with every change.
The complete example, with hat output you are seeing will help understand the problem better and give you a solution. Please see How to create a Minimal, Complete, and Verifiable example
There are multiple problems here, but first let me answer your question:
You are creating a new JPanel and add it to the Game on every run through. That is wrong, since you add infinite panels to your Game
Also in your if/else you have a wrong condition. You increase the iterator when it is greater 42. You probably mean lesser than 42.
Here is how I would do it:
public class BackgroundPanel extends JPanel {
private int currImage = 0;
private BufferedImage[] backgroundImages;
public BackgroundPanel() {
int numberOfImages = 42;
backgroundImages = new BufferedImage[42];
for(int i = 1; i <= numberOfImages; i++) {
String backgroundFile = "f" + i + ".png";
backgroundImages[i] = ImageIO.read(new File(backgroundFile));
}
}
public void nextImage() {
/*if(currImage <= 42) currImage++;
else currImage = 1;*/
if(currImage++ > 42) currImage = 1;
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(backgroundImages[currImage], 0, 0, getWidth(), getHeight(), null);
}
}
You need to add this panel ONCE to your "Game":
//Somewhere in your Game
private BackgroundPanel backgroundPanel;
...
...
public Game() {
entryWindow();
this.setLayout(null);
backgroundPanel = new backgroundPanel();
backgroundPanel.setSize(getWidth(), getHeight());
add(backgroundPanel);
timerBackground = new javax.swing.Timer(100,this);
timerBackground.stop();
}
Your timer:
if (ae.getSource() == timerBackground) {
backgroundPanel.nextImage();
}
It's easier to put the background on JLabel. It requires only 3 lines of code and works fine! :) Hope it helps for anyone that will have the same problem :)
All you have to do is copy this code, change the name (i have all pictures in a folder called "Images") with any kind of Java supported picture/video/.... (just change the suffix .gif to your file format) and at last the size. Good luck! :)
public JLabel backgroundGIF;
backgroundGIF = new JLabel(new ImageIcon(getClass().getResource("Images/background.gif")));
backgroundGIF.setBounds(0,0,1100,800);
add(backgroundGIF);
Related
This is an animation in which a given number of icons start on the left side of the frame and race across the screen to the right side. Each icon is drawn to its own JPanel that fills a row within a container's single-column GridLayout, and each JPanel races on its own thread. (Threads must be used, even though a Swing Timer might be a better approach.)
A finish line is also supposed to get painted onto the container, but it is not showing up. I have tried setting the Racer's JPanel opacity to false, but this doesn't work. How might I get the other threads to allow the finish line's painting to execute?
The code that isn't working:
gui = new JPanel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(finishLineXPos, 0, finishLineXPos, windowHeight);
}
};
The full code:
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
public class Races2 {
private JFrame frame;
private JPanel gui; // to hold all components
private int finishLineXPos; // x-coordinate of finish line
private Icon racerImg;
private int racerImgWidth;
private int racerImgHeight;
private int numOfRacers;
private ArrayList<Racer> racers;
private Racer winner;
private int windowHeight;
private int windowWidth;
public Races(int num) {
numOfRacers = num;
racerImg = new ImageIcon("races.png");
racerImgWidth = racerImg.getIconWidth();
racerImgHeight = racerImg.getIconHeight();
windowHeight = racerImgHeight * numOfRacers;
windowWidth = racerImgWidth * 20;
finishLineXPos = racerImgWidth * 18; // two icon widths from the right
frame = new JFrame("Off to the Races - by Brienna Herold");
frame.setResizable(false); // prevents window resizing which affects painting
gui = new JPanel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(finishLineXPos, 0, finishLineXPos, windowHeight);
}
};
gui.setLayout(new GridLayout(numOfRacers,1));
gui.setPreferredSize(new Dimension(windowWidth, windowHeight));
// Create and add racers to gui panel
racers = new ArrayList<Racer>();
for (int i = 0; i < numOfRacers; i++) {
Racer racer = new Racer();
gui.add(racer);
racers.add(racer);
}
// Start racers
for (Racer racer : racers) {
Thread racerThread = new Thread(racer);
racerThread.start();
}
frame.add(gui);
frame.pack();
frame.setVisible(true);
}
protected class Racer extends JPanel implements Runnable {
private int lastPosX;
private int posX;
public Racer() {
posX = 0;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
racerImg.paintIcon(this, g, posX, 0);
posX += Math.random() * 20;
}
#Override
public void run() {
// While the race has not been won yet, proceed with race
while (winner == null) {
repaint();
try {
Thread.sleep(100); // slows down racing a bit
} catch (InterruptedException ex) {
ex.printStackTrace();
}
// If racer passes specified x-coordinate, set it as winner
if (posX >= finishLineXPos) {
System.out.println("Winner: " + this.getName());
winner = this;
}
}
}
}
}
Each icon is drawn to its own JPanel that fills a row within a container's single-column GridLayout
First you need to learn how to do custom painting. Start by reading the section from the Swing tutorial on Custom Painting for some basics. A couple of key points:
You need to override the getPreferredSize() method to the component has a preferred size so the layout manager can do its job. If you don't specify the preferred size then the size may be zero so there is nothing to paint.
A painting method is for painting only. You should NEVER modify the state of the component in a painting method since you can't control when Swing will repaint a component. So you need a method like setPositionX(...) to control the image location where it should be painted. Then in the Thread (or Timer) you invoke this method to change the location and invoke repaint() on the component.
A finish line is also supposed to get painted onto the container, but it is not showing up
Well you add all the Racer components to the top of the race panel so those components will cover the line painted on the panel.
You have been given one approach to use racer.setOpaque(false);
Another approach is to override the paint() method. (This is an exception to the general rule to do custom painting in the paintComponent() method). If you read the tutorial link I provided you will see that using this approach will cause the painting of the race panel to be done after all the Racer components have been painted.
frame.setResizable(false); // prevents window resizing which affects painting
It is not necessary for this. The only reason the painting is affected is because you are changing the state of the component in the painting method. See my comment above.
racerImg.paintIcon(this, g, posX, 0);
People generally use the drawImage(...) method to paint an image. There is no reason to create an Icon just to paint the image.
racerImg = new ImageIcon("races.png");
Don't use an ImageIcon to read a file. Using ImageIO.read(...) to read the image. Then just paint the image as described above.
and each JPanel races on its own thread.
That seems like a silly requirement because it brings a different kind of randomness to the race. You already have logic that generates a random distance for the image to move, so why do you need separate Threads for this. Instead you should just have a single Thread and then you iterate through all the Races and invoke the setPositionX() method suggested above. Then all Races will be repainted at the same time with there own random distance change.
Edit:
Having said all the above your code works fine for me with a single change:
posX = 0;
setOpaque(false);
You just need to make the Racer transparent in it's constructor.
As this appears to be a school assignment I guess you have to follow the rules, but you really should understand the problems with the assignment.
As everybody else in this posting has suggested, I still believe the better approach is to have an ArrayList or Racer objects (not components) that you can paint. I gave you a working example of painting Ball objects in your last question on this topic.
So my ultimate goal is to change the design of a JButton from the basic looking blue button to whatever I want, like a circle.
So I create a class called "Button" and made it extend JButton
public class Button extends JButton {
public Button(String text) {
super(text);
this.setContentAreaFilled(false);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("hello");
//Paint Stuff Will Happen Here
}
#Override
public Dimension getPreferredSize() {
return (new Dimension(120, 120));
}
}
My first goal was just to make sure that the paintComponent method was being called, so I put in a debug message. That debug message has never shown.
Basically the paintComponent() method is never called, even though I'm manually calling the "repaint" method for my JFrame.
Despite the fact that the method is not being called, a regular button still shows up on my JFrame, which is really confusing to me.
Here is my JPanel code
public class Scene extends JPanel {
public Scene() {
//Initialize Listeners
Button button = new Button("Hello");
button.setBounds(400, 400, 50, 25);
this.setLayout(null);
this.add(button);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
//Paint Stuff Below
for (int xI = 0; xI < Sprite.allSprites.size(); xI++) {
Sprite sprite = Sprite.allSprites.get(xI);
if (sprite.isVisible) {
g2.drawImage(sprite.image, sprite.rawLocation.x.intValue(), sprite.rawLocation.y.intValue(), null);
}
}
g2.dispose();
}
}
Basically in my JPanel I override the paintComponent method as well so as to paint my various sprites onto the screen, which has worked just fine and is probably irrelevant to the issue.
And finally, this is my JFrame code
public class GameWindow extends JFrame {
private Scene currentScene;
public void initialize(Scene scene) {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(Settings.DEFAULT_WINDOW_SIZE);
this.setResizable(false);
this.setLocation(this.getLocationToCenterScreen());
this.setScene(scene);
}
//Gets the center of the screen with the given window
public Point getLocationToCenterScreen() {
return new Point(Settings.SCREEN_CENTER.x - (this.getSize().width / 2), Settings.SCREEN_CENTER.y - (this.getSize().height / 2));
}
public void setScene(Scene scene) {
this.currentScene = scene;
this.setContentPane(scene);
}
public Scene getCurrentScene() {
return currentScene;
}
}
Nothing really fancy in this code as far as I can tell.
I've set the content pane.
I've made sure each paintComponent() method also includes super.paintComponent(g).
I've set the LayoutManager to null for testing purposes.
I've set the Button's bounds.
As I said, the button actually does show up on the screen. But the debug message never shows.
Also, the button that shows up on the screen looks like the really old Windows buttons from 10 years ago. It's all grey with a black border.
This old button only shows up if I'm using a class that extends JButton.
Anyways, thanks guys! I hope I can get over this weird problem.
First and foremost, as a few other people have said, don't name your class "Button"; that belongs to Swing's predecessor, AWT (Advanced Windowing Toolkit), and is likely to confuse the compiler at best, and get you the wrong "Button" at worst.
That should solve the paintComponent() problem, but in addition, if all you're trying to do is change the feel of the button, then you're overprogramming this.
There are two ways to accomplish this with a JButton.
The first, and probably easiest (for an image), is AbstractButton.setIcon(Icon defaultIcon) An Icon is a type of image, loadable from a BufferedImage with ImageIcon(Image image) and manipulable in the same way. This is probably what you need.
The other method which comes to mind, which is much more broad in scope, is change the Look and Feel of the application. Most of us have several available to our systems, inclusive of the default Java look and feel, and the platform look and feel. I recommend setting it as early as possible; since it's entirely done through static methods, for small projects you might even get away with slipping it into the main method, before anything is even initialized.
Let me know if this doesn't solve your problem, and I wish you luck with the rest of your project!
private void renderLevelBackground(Graphics2D g2) {
Image backgroundImage = model.getBackgroundImage();
g2.drawImage(backgroundImage, 0, 0, null);
}
This is my code, I know that the backgroundImage is loaded properly because if I getWidth/Height it gives me the correct values. Is there anyway to test if it's the image or if it's the method somehow? It just won't draw on my screen despite the fact that I've used the drawImage method many times in this project already, all of which work flawlessly.
Thanks
"Is there ever a reason drawImage won't actually draw an image?"
Possibility, there is no image due to a bad path provided. Use ImageIO.read() which will cause an exception if the image file is not found. For example
public class ImagePanel extends JPanel {
private BufferedImage image;
public ImagePanel() {
try {
image = ImageIO.read(getClass().getResource("/resources/image.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
If the image is not found, it will throw an IO exception.
Possibility, the panel to which you are drawing, has no preferred size (0 x 0), and you are adding it a container wit layout that respects preferred sizes, so the ImagePanel will not be able to show the image. For example
public class ImagePanel extends JPanel {
protected void paintComponent(...) {
...
}
JPanel panel = new JPanel(); // default FlowLayout that respects preferred sizes
panel.add(new ImagePanel());
To fix this, you can override the gerPreferredSize() of the ImagePanel
public class ImagePanel extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
}
Possibility, you are not calling your method within the graphics context of the paint[Component] method.
protected void paintComponent(Graphics 2d) {
super.paintComponent(g);
Grapchics2D g2 = (Graphics2D)g;
renderLevelBackground(g2);
}
Other than that, these are all just guesses, and you should provide some more code code (preferably an MCVE) to help us better help you with the problem.
Possible Alternative to your approach. It seems like (from the little code snippet and it method signature semantics) you want to change the background image, when a level have changed. Consider using setBackgroundImage(BufferedImage image) method in your panel class, when the image is drawn. You can then set the background, whenever need be. Something like
public class BackgroundPanel extends JPanel {
private BufferedImage backgroundImage;
public void setBackgroundImage(BufferedImage image) {
this.backgroundImage = image;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Grpahics2D g2 = (Graphics2D)g;
if (image != null) {
g2.drawImage(backgroundImage, 0, 0, this);
}
}
}
So whenever you call setBackgroundImage, the image will change, and be repainted.
You may need to explicitly tell the JComponent that you are painting onto that it needs to be redrawn with revalidate() and repaint()
So my friend and I are making a very very basic game for fun, its going to be a top down (about 45 degree angle) where there is a lumberjack chopping down trees. Very basic and kinda lame I'm aware but we are just getting into semi-advanced java.
To start we decided we needed sprites for trees but we realized that no matter what it would be a rectangle with white pixels all around the tree, but that would cut out part of the background image. So we wanted to take every pixel that was white (whitespace/negative space), then make those pixels transparent. To do this we looked at a ton of codes and the one we saw the most was the code below, it worked but I don't quite understand it.
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
public class simpleFrame extends JFrame {
JPanel mainPanel = new JPanel() {
ImageIcon originalIcon = new ImageIcon("image.png");
ImageFilter filter = new RGBImageFilter() {
int transparentColor = Color.white.getRGB() | 0xFF000000;
public final int filterRGB(int x, int y, int rgb) {
if ((rgb | 0xFF000000) == transparentColor) {
return 0x00FFFFFF & rgb;
} else {
return rgb;
}
}
};
ImageProducer filteredImgProd = new FilteredImageSource(originalIcon.getImage().getSource(), filter);
Image transparentImg = Toolkit.getDefaultToolkit().createImage(filteredImgProd);
public void paintComponent(Graphics g) {
g.setColor(getBackground());
g.fillRect(0, 0, getSize().width, getSize().height);
g.drawImage(transparentImg, 140, 10, this);
}
};
public simpleFrame() {
super("Transparency Example");
JPanel content = (JPanel)getContentPane();
mainPanel.setBackground(Color.black);
content.add("Center", mainPanel);
}
public static void main(String[] argv) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
simpleFrame c = new simpleFrame();
c.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
c.setSize(700,700);
c.setVisible(true);
}
});
}
}
So what we don't exactly understand is why there are brackets after the declaration of the JPanel in the 6th line then I have no idea how the lines 6-16. Like in between the brackets, then the close bracket has a semi colon after it. I can't figure out how that works, every command line set aside but the structure of declaring the JPanel, JPanel mainPanel = new JPanel() { };
Im trying to implement this so I can import an image to my JPanel class, then make all the negative space transparent, then paint it. I just cant seem to understand the structure of the program and how I could implement it into a class.
My code is the following:
public class Frame extends JPanel {
/*
* This is the JPanel in which I want to add the transparent image to a JPanel
* then add the JPanel object,"Frame", above in a JFrame declared in my
* class above
*/
public Frame() {
JPanel jp = new JPanel();
jp.setSize(300,300);
jp.setVisible(true);
circle = new BufferedImage();
try {circle = ImageIO.read(new File("circle.PNG"));}
catch (IOException ex) {}
}
public void paintComponents(Graphics g) {
super.paintComponents(g);
g.drawImage(circle,0,0,300,300, null);
g.drawRect(50, 50, 50, 50);
}
}
It is only a test code to import an image, make it transparent, and then paint it on a JPanel. We are just trying to understand the best way to do this and the code that I found (the first code block) seems to do the job very well but we can't figure out the best way to implement it to our code.
Thanks in Advance,
Robbie and
Nick
What the code in your example block with the JPanel is doing is declaring the JPanel's class right there--immediately--when it is instantiated. It's sort of coding shorthand. Doing that, they don't have to write a formal new, named class for the JPanel in a new .java file: the entire definition of the class is right there. If you look at it as a Java class definition, I'm sure it will make more sense.
If you take all the stuff out and put it in a formal Java class, inherited from JPanel, it should work. The formal name for what they're doing is called implementing an anonymous class (http://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html). I normally don't do it, because it makes the code much harder to read, but it's permitted.
HTH
The problem comes because I overwrite the paintComponent method of a jPanel, so when I repaint all the objets are hidden till I focus them.
I need to overwrite the paintComponent method cause it's the only one answer I'd found in internet to change the background image of a jFrame.
So firstly I create a jPanel class:
public class JPanelFondoPrincipal extends javax.swing.JPanel {
public JPanelFondoPrincipal(){
this.setSize(800,500);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Dimension tamanio = getSize();
ImageIcon imagenFondo = new ImageIcon(getClass().getResource("/images/fondo_principal.png"));
g.drawImage(imagenFondo.getImage(),0,0,tamanio.width, tamanio.height, null);
setOpaque(false);
}
}
And in my jPanelForm:
private void formWindowOpened(java.awt.event.WindowEvent evt) {
// TODO add your handling code here:
JPanelFondo p = new JPanelFondo();
this.add(p);
validate();
p.repaint();
}
I'd already tried to add all my Objets (labels, textFields...) to a new Panel so I can add it after repaint, and set all the objets visibles manually but everything is still invisible.
Many thanks, I need to finish the app in 6 days and I'm getting crazy by the minute
EDIT: SOLVED THANKS TO CARDLAYOUT
don't add / remove JPanels or its contents on runtime, use CardLayout instead
your JPanelFondo p = new JPanelFondo(); doesn't corresponding somehow with public class JPanelFondoPrincipal extends javax.swing.JPanel {
for better help sooner edit your question with an SSCCE,
Swing programs should override paintComponent() instead of overriding paint().
http://java.sun.com/products/jfc/tsc/articles/painting/
And you should call super.paintComponent(g); first in overriden paintComponent();
public void paintComponent(Graphics g){
super.paintComponent(g);
Dimension tamanio = getSize();
ImageIcon imagenFondo = new ImageIcon(getClass().getResource("/images/fondo_principal.png"));
g.drawImage(imagenFondo.getImage(),0,0,tamanio.width, tamanio.height, null);
setOpaque(false);
}
Here's the proper way to handle painting onto JPanel component.