I don't understand why the picture painted on my background isn't resized on JFrame resize.
Basically I create a JFrame and a JPanel inside this frame.
Then I set my background picture (animated gif) with the method "setBackground" that creates an object of another class. In this new class called "ImagePanel" I draw the picture and paint another smaller picture over the background picture. The result is ok unless I resize the JFrame.
The background picture "follows" the resize, while the smaller picture remains at the beginning point. I draw both the pictures with the paintComponent(), and I set the dimension of the pictures on the base of the Width and Height of the JFrame, but it doesn't seem to work.
I added an action listener too, to listen to the resize on the JFrame. When it listens the resize it calls "repaint()", but it doesn't work (I also tried "revalidate()" both on the JFrame and the JPanel without success).
It seems that when you enlarge the JFrame almost covering all the screen it has an update, on a fixed point the smaller picture is "updated" but in a wrong way, and only if the JFrame is very large.
Here is the code. The "GameWindow" class:
import java.awt.BorderLayout;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GameWindow extends JFrame {
private JPanel mapPicture = new JPanel();
public GameWindow(){
super("WELCOME!!!");
setSize(768, 768);
addComponentListener(new ResizeListener(this));
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
setBackground();
setVisible(true);
}
private void setBackground() {
mapPicture = new ImagePanel("map.gif", this);
add(mapPicture, BorderLayout.CENTER);
mapPicture.setLayout(new BorderLayout());
}
public class ResizeListener implements ComponentListener {
private JFrame parentFrame;
public ResizeListener(JFrame parentFrame) {
this.parentFrame = parentFrame;
}
public void componentHidden(ComponentEvent e) {}
public void componentMoved(ComponentEvent e) {}
public void componentShown(ComponentEvent e) {}
public void componentResized(ComponentEvent e) {
repaint();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new GameWindow2();
}
});
}
}
And here is the "ImagePanel" class:
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImagePanel extends JPanel {
private ImageIcon background;
private JFrame parentFrame;
private ImageIcon smallerImage = new ImageIcon(getClass().getResource("car.png"));
public ImagePanel(String imgPath, JFrame parentFrame) {
this.parentFrame = parentFrame;
background = new ImageIcon(getClass().getResource(imgPath));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(background.getImage(), 0, 0, getWidth(), getHeight(), this);
g2d.drawImage(smallerImage.getImage(), 119*((parentFrame.getWidth()/768)), 172*((parentFrame.getHeight())/768), 32*((parentFrame.getWidth())/768), 32*((parentFrame.getHeight())/768),this);
}
}
I don't understand why the smaller picture isn't redrawn with the new proportions.
PS: I can't use a layout manager to set the position of the second picture, because I will have a lot of objects (in the future) to put in a casual way on the map (and possibly animate them).
it is because you are using integer arithmetic and dividing before multiplying :119*(parentFrame.getWidth()/768)
instead you floating point arithmetic and cast to an int : (int)(119.0*(parentFrame.getWidth()/768.0))
Related
I'm trying to create a super simple component, and it's not appearing.
Component class:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
public class Player extends JComponent{
public Player()
{
}
public void paint(Graphics g)
{
g.setColor(Color.green);
g.fillRect(40,40,150,150);
}
}
Panel Class im adding it to:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import javax.swing.JPanel;
public class Game extends JPanel{
public Game()
{
this.setBackground(Color.yellow);
this.setPreferredSize(new Dimension(500,500));
Player p = new Player();
this.add(p);
}
}
And the JFrame:
import javax.swing.JFrame;
public class Launcher {
public static void main(String[] args) {
JFrame frame = new JFrame("Key Collector Demo");
frame.add(new Game());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
}
}
The only thing showing up is the yellow background.
JFrame and JPanel are working fine; this problem consistently happens to me when building jcomponents. What am I missing?
Any help would be greatly appreciated!
Coordinates that you are using to draw a component are defined in the space of that component.
If you do this:
public void paint(Graphics g)
{
g.setColor(Color.green);
System.out.println(getSize());
g.fillRect(40,40,150,150);
}
You will see that at the moment it is attempted to get drawn its size is 1x1. So drawing it from 40,40 obviously takes it out of the visible area of the component.
Now change the method to:
public void paint(Graphics g)
{
g.setColor(Color.green);
System.out.println(getSize());
setSize(45, 45);
g.fillRect(40,40,150,150);
}
Now you can see small filled rectangle. This is because you forced the size to be 45x45 and now there are 5 pixels to show in the space of the component while the remaining part is still out of the area.
Do not assume the Player panel to have a fixed size.
For a first test your paint method could look like this:
public void paint(Graphics g) {
g.setColor(Color.green);
g.fillRect(0,0,getWidth(),getHeight());
}
The next problem is that the component probably has no size or position. Add a LayoutManager to your panel and add the component:
public Game() {
this.setBackground(Color.yellow);
this.setPreferredSize(new Dimension(500,500));
this.setLayout(new BorderLayout());
Player p = new Player();
this.add(p, BorderLayout.CENTER);
}
With that, your player might take the full space and you see green instead of yellow. Play around with the LayoutManager and the player's preferredSize.
I am trying to do a very simple thing.. set the background color on the JPanel inside my JFrame. I have not used swing very much so I am still learning. However, I have read up on doing something as basic as setting the background color quite a bit, and I do not know why what I have is not working.
I have my JFrame set up in my Main class.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class Main extends JFrame {
private static Screen screen;
private static int WIDTH = 600;
private static int HEIGHT = 600;
public Main() {
screen = new Screen();
setTitle("Asteroid");
setSize(WIDTH, HEIGHT);
setLayout(new BorderLayout());
add(screen, BorderLayout.CENTER);
setBackground(Color.BLACK);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
#Override
public void paint(Graphics g) {
}
public static void main(String[] args) {
new Main();
}
}
And then I have my JPanel set up in a Screen class
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JPanel;
public class Screen extends JPanel {
private static int WIDTH = 600;
private static int HEIGHT = 600;
private Dimension screen = new Dimension(WIDTH, HEIGHT);
public Screen() {
setSize(screen);
setBackground(Color.BLACK);
setOpaque(true);
}
}
I am not sure why this is not working properly.
The problem is that you #Override paint method (you should not) of your JFrame. In addition you leave it empty, without calling the super paint method. So, if you simply add super.paint(g); to your #Override you will see that background is painted without problems.
However, when you want to do custom painting, you should #Override paintComponent(Graphics g) method, and again, start by calling super.paintComponent(g);.
To provide some background info that might help, I am creating the game pong and I decided to add an escape/pause menu (when you press escape on the keyboard a menu pops up with some settings), I looked around and found that the best way to do this is to use JLayeredPane and add another JPanel on top. However, when I added my 'painter' class to the JLayeredPane, the paint(Graphics g) method stopped getting called (it worked fine when I just added it to the JFrame).
import javax.swing.*;
public class Game extends JFrame {
public static Painter painter;
public Game() {
setSize(500, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLayeredPane lp = getLayeredPane();
painter = new Painter();
add(lp);
}
public static void main(String[] args) {
Game frame = new Game();
frame.setVisible(true);
painter.repaint();
}
}
And here is my Painter class
import java.awt.*;
import javax.swing.*;
public class Painter extends JPanel {
public void paint(Graphics g) {
System.out.println("Working");
super.paint(g);
}
}
Instead of add(lp);, I originally tried lp.add(painter); in which case the paint method never got called. By doing add(lp) I get an IllegalArgumentException for adding container's parent to itself.
Here is mcve of using LayeredPane :
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
public class Game extends JFrame {
public Game() {
setSize(250, 150);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLayeredPane lp = getLayeredPane();
//assign layout manager. otherwise you need to set content
//bounds (not recommended)
lp.setLayout(new BorderLayout());
Painter painter = new Painter();
lp.add(painter,1);
}
public static void main(String[] args) {
Game frame = new Game();
frame.setVisible(true);;
}
}
class Painter extends JPanel {
#Override
protected void paintComponent(Graphics g) { //do not override paint
super.paintComponent(g);
g.drawString("Working",50,50);
}
}
I'm creating a simple window with a background image by Swing java library.
The problem is :background image appear only when you resize window.
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.LayoutManager;
import java.awt.MediaTracker;
import java.awt.event.ActionListener;
import javax.swing.*;
import java.awt.Toolkit;
public class StartWindow {
JFrame frame;
private JButton button;
private JButton button2;
public void CreateStartWindow() {
frame = Window.createwindow();
Container container = frame.getContentPane();
JpanelStart panel = new JpanelStart();
container.add(panel);
this.button = new JButton("Start");
this.button2 = new JButton("Classifica");
panel.add(button);
panel.add(button2);
}
public void addActionListener(ActionListener al) {
this.button.addActionListener(al);
this.button2.addActionListener(al);
}
public void chiudi() {
frame.dispose();
}
}
class JpanelStart extends JPanel {
private Image img;
private String path_img="img/sfondo.jpg";
public JpanelStart(){
img = Toolkit.getDefaultToolkit().createImage(path_img);
loadImage(img);
}
private void loadImage(Image img) {
try {
MediaTracker track = new MediaTracker(this);
track.addImage(img, 0);
track.waitForID(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g){
setOpaque(false);
g.drawImage(img,0, 0, null);
super.paintComponent(g);
}
}
Window
public class Window extends JFrame {
public static JFrame createwindow() {//fare singleton
JFrame frame = new JFrame("Battaglia navale");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(640, 640);
frame.setVisible(true);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
frame.setLocation(((int)dim.getWidth()-(int)frame.getWidth())/2,
((int)dim.getHeight()-(int)frame.getHeight())/2);
return frame;
}
;
}
If possible, call setVisible on the frame after you've added the components to it, otherwise you'll need to use revalidate and repaint.
Don't call super.paintComponent after you've painted something, as it's likely to clear Graphics context
Don't change the state of any component from with the paintComponent method, call setOpaque is bad from within the paint method is a bad idea, as the Graphics context has already being prepared assuming that the component was opaque
As has already being suggest, you should be passing this as the last parameter to drawImage, especially because of the way you load the image. Personally, I prefer to use ImageIO to load images, as it provides more details when the image fails to load....
In this line...
g.drawImage(img,0, 0, null);
... you are passing null as the ImageObserver, so your component is not told to repaint when the image is loaded. You ought to pass this.
I recently started with Java GUIs a few weeks ago and I have a difficulty with alignment.
Basically, I'm trying to have two Panels with a different background image (top Bar and content) and I want to align them one after another.
The problem is, that I can't use BorderLayout.NORTH and BorderLayout.SOUTH, because the background image loses his original size and gets very tiny.
How can I align them correctly, without losing the original size?
Here's my code:
package main;
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageTest {
public static void main(String[] args) {
ImageFrame frame = new ImageFrame("topBar.png", "contentImage.png");
frame.setSize(640,480);
frame.setVisible(true);
}
}
class ImagePanel extends JPanel {
private Image img;
public ImagePanel(String img) {
this(new ImageIcon(img).getImage());
}
public ImagePanel(Image img) {
this.img = img;
}
public void paintComponent(Graphics g) {
g.drawImage(img, 0, 0, null);
}
}
class ImageFrame extends JFrame {
public ImageFrame(String topBar, String body) {
setLayout(new BorderLayout());
ImagePanel topPanel = new ImagePanel(topBar);
ImagePanel bodyPanel = new ImagePanel(body);
add(topPanel, BorderLayout.NORTH);
add(bodyPanel, BorderLayout.SOUTH);
pack();
}
}
There are a number of issues that popup out at me
You're not calling super.paintComponent. This is very important and can not be understated
You really should be using ImageIO to load your images. Ala from the fact it supports a wider range of image formats, it also loads images concurrent and throws useful exceptions when something goes wrong
You're not supplying any preferredSize values. This is used by the layout manager to decide how best to layout your component. Remember though, this are only hints and layout managers are well within there rights to ignore them
Check out Reading/Loading an Image for more details on ImageIO
..the background image loses his original size and gets very tiny.
That is because the preferred size for a panel is 0x0 until components are put in it.
There are at least two ways to solve this:
Add content to the panels.
Override getPreferredSize() to return the Dimension of the image.
The first is optimal, but I'll show how to do the 2nd (less code).
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageTest {
public static void main(String[] args) {
ImageFrame frame = new ImageFrame();
//frame.setSize(640,480);
frame.pack();
frame.setVisible(true);
}
}
class ImagePanel extends JPanel {
private Image img;
public ImagePanel(String img) {
this(new ImageIcon(img).getImage());
}
public ImagePanel(Image img) {
this.img = img;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
// a panel IS an ImageObserver, so use it here.
g.drawImage(img, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
int w = img.getWidth(this);
int h = img.getHeight(this);
return new Dimension(w,h);
}
}
class ImageFrame extends JFrame {
public ImageFrame() {
setLayout(new BorderLayout(2,2));
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
ImagePanel topPanel = new ImagePanel(new BufferedImage(200,20,BufferedImage.TYPE_INT_RGB));
ImagePanel bodyPanel = new ImagePanel(new BufferedImage(200,100,BufferedImage.TYPE_INT_RGB));
add(topPanel, BorderLayout.NORTH);
add(bodyPanel, BorderLayout.SOUTH);
pack();
}
}