I am working on the basics of a graphics program in swing and java2D to practice. I am having a problem wherein I cannot show my images. I have divided my code into 4 classes so that when the program gets larger it's easier to manage.
The idea is that I have very little in the Main, that Frame initializes my first screen, that the screens can all be subdivided into their own classes, TitleScreen being one of these, and PullImage does all of the work of buffering and printing images which bothered me.
When I run this I get an empty window and no errors, so I cannot figure out where the problem is.
Please and Thank you for your help.
Main
package com.game.pack;
import javax.swing.JFrame;
public class Main extends JFrame {
private static final long serialVersionUID = 1L;
public final static void main(String[] args)
{
new Frame().initialize();
new TitleScreen().openScreen();
}
}
Frame
package com.game.pack;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Frame extends JFrame{
private static final long serialVersionUID = 1L;
public final void initialize()
{
JFrame frame = new JFrame("Game");
JPanel panel = new JPanel();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800,600);
panel.setSize(800,600);
frame.setLayout(null);
panel.setLayout(null);
frame.setLocationRelativeTo(null);
this.getContentPane().add(panel);
panel.setVisible(true);
frame.setVisible(true);
}
public final void close()
{
dispose();
}
}
TitleScreen
package com.game.pack;
public class TitleScreen {
public void openScreen()
{
new PullImage().printARGB("icons/titleBG.png",800,600,0,0);
new PullImage().printARGBFromSheet("icons/titleButtons.png",
200, 125, 400, 200, 200, 40, 0, 0);
while (1!=2)
{
}
PullImage
package com.game.pack;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
public class PullImage {
public void printARGB(String source, int sizeX, int sizeY, int locX, int locY)
{
Image Icon = new ImageIcon(source).getImage();
BufferedImage BuffedImage = new BufferedImage(sizeX, sizeY, BufferedImage.TYPE_INT_ARGB);
Graphics graphics = BuffedImage.getGraphics();
graphics.drawImage(Icon,locX,locY,null);
}
public void printARGBFromSheet(String source, int sizeX, int sizeY, int locX, int locY, int width, int height, int sheetLocX, int sheetLocY)
{
Image Icon = new ImageIcon(source).getImage();
BufferedImage BuffedImage = new BufferedImage(sizeX,sizeY,BufferedImage.TYPE_INT_ARGB);
Graphics graphics = BuffedImage.getGraphics();
graphics.drawImage(Icon, locX, locY, locX+width, locY+height, sheetLocX, sheetLocY, sheetLocX+width, sheetLocY+height, null);
}
}
One problem lies here:
public final void initialize()
{
this.getContentPane().add(panel);
}
This is setting the content pane of your frame to the panel, not the JFrame you created. Essentially you're not adding it to the actual visible window. Just replace it with
frame.getContentPane().add(panel);
Related
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);.
heres what my code looks like
import java.awt.*;
import javax.swing.*;
public class Test {
public static void main(String[] args) {
JFrame frame = new JFrame();
MyDrawPanel shape = new MyDrawPanel();
frame.getContentPane().add(shape);
frame.setSize(500,500);
frame.setVisible(true);
}
}
class MyDrawPanel extends JPanel{
public void paintComponent (Graphics g) {
g.setColor(Color.ORANGE);
g.fillRect(20, 50, 100, 100);
}
}
When I run it, the only thing that shows up is the frame, not the actual shape. Is there something I'm missing?
Note that this answer does not answer your direct question of why your posted code doesn't work, because while your code has problems, it should still display the square. But having said that, this post is meant to offer some suggestions on "better" practices:
Avoid magic values and magic numbers
Use #Override annotations for any method that you think is an override
The paintComponent method is protected, not public
Call the super's method in your override
Best to override getPreferredSize of the JPanel if you need to fix its size
Start the Swing GUI on the Swing event thread for thread-safety
Avoid hard-coding your graphic drawing positions, especially if you're thinking of animating it at a later date
This is a better representation of your code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.*;
public class Test2 extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private static final Color RECT_COLOR = Color.ORANGE;
private static final int RECT_WIDTH = 100;
private static final int INIT_X = 20;
private static final int INIT_Y = 50;
private int rectX = INIT_X;
private int rectY = INIT_Y;
public Test2() {
// TODO any initialization code goes here
}
// override annotation
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// avoid magic values and numbers
g.setColor(RECT_COLOR);
g.fillRect(rectX, rectY, RECT_WIDTH, RECT_WIDTH);
}
// best way to set size safely
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
Test2 mainPanel = new Test2();
JFrame frame = new JFrame("Test2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
// be sure to start the GUI on the event thread
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
I am realy confused about this. I making the game "Microtrip". It's mobile game, but I am making it for PC. I have a background class who extends JPanel and it just draws a rectangle who starts from 0, 0 and have the size of the screen(my game is fullscreen). I have a main menu class who extends JPanel too. I want to to add everything for the main menu there. Then I add everything into my GameFrame class who extends JFrame. I have main class who just calls The GameFrame class. Here's my code:
Background class:
import java.awt.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class Background extends JPanel{
int width, height;
Color backgroundBlue;
public Background(int width, int height){
this.width = width;
this.height = height;
backgroundBlue = new Color(25, 159, 229);
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(backgroundBlue);
g.fillRect(0, 0, this.width, this.height);
}
}
MainMenu class:
import javax.swing.*;
#SuppressWarnings("serial")
public class MainMenu extends JPanel{
Background background;
public MainMenu(int WW, int WH){
//WW = window width
//WH = widow height
this.setLocation(0, 0);
this.setSize(WW, WH);
background = new Background(WW, WH);
this.add(background);
}
}
GameFrame class:
import main_menu.MainMenu;
import main_menu.*;
import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import javax.swing.JFrame;
import java.awt.event.*;
#SuppressWarnings("serial")
public class GameFrame extends JFrame{
private GraphicsDevice vc;
private MainMenu mainMenu;
//private Background background;
public GameFrame(){
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
mainMenu = new MainMenu((int)screenSize.getWidth(), (int) screenSize.getHeight());
/*background = new Background((int)screenSize.getWidth(), (int) screenSize.getHeight());*/
GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
vc= e.getDefaultScreenDevice();
this.setTitle("Mictrotrip");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBounds(0, 0, (int)screenSize.getWidth(), (int)screenSize.getHeight());
this.add(mainMenu);
//this.add(background);
this.setLocationRelativeTo(null);
this.setUndecorated(true);
this.setResizable(false);
vc.setFullScreenWindow(this);
addKeyListener(new KeyListener(){
public void keyPressed(KeyEvent e){
if(e.getKeyCode() == KeyEvent.VK_ESCAPE){
System.exit(0);
}
}
public void keyReleased(KeyEvent e){
}
public void keyTyped(KeyEvent e){
}
});
}
}
and my Main class:
public class Main {
public static void main(String[] args) {
new GameFrame();
}
}
When I run it, it just shows a small rectangle at the top center.
I did the following experiment:
I ignored the mainmenu class and directly added the background in the JFrame(the commented lines in GameFrame) and everything worked fine. Why?
I read all similar questions, but no one of them helped me.
If you want background to fill main menu, then the problem is with MainMenu -- you're letting the JPanel use the default FlowLayout, and FlowLayouts will not respect a components size (so Jamal's solution will not work) but rather its preferred size. If you want background to fill the main menu, then give MainMenu a BorderLayout and then add your background component BorderLayout.CENTER:
public MainMenu(int WW, int WH){
// WW = window width
// WH = widow height
// this.setLocation(0, 0);
// this.setSize(WW, WH);
setLayout(new BorderLayout());
background = new Background(WW, WH);
this.add(background, BorderLayout.CENTER);
}
Still trying to grasp how classes and methods work in Java. To experiment, I tried to create a graphics class, with a void draw box method inside. Then, I try to call that method in the main method to try to draw those boxes. I'm getting "cannot be resolved to variable" errors which I believe means the main class can't see my other class for some reason?
Boxymain.java:
import java.awt.*;
import javax.swing.JFrame;
public class Boxymain extends Canvas {
public static void main(String[] args){
BoxyMethod c = new BoxyMethod();
c.drawBox(window, Color.RED, 200, 300);
JFrame win = new JFrame("Boxy Main");
win.setSize(800,600);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Boxymain canvas = new Boxymain();
win.add(canvas);
win.setVisible(true);
}
}
BoxyMethod.java:
import java.awt.*;
import javax.swing.JFrame;
public class BoxyMethod {
public void drawBox(Graphics window, Color c, int x, int y){
window.setColor(c);
window.fillRect(x, y, 100, 100);
window.setColor(Color.WHITE);
window.fillRect(x+10,y+10,80,80);
}
}
Error text: "window cannot be resolves to a variable."
The error message is telling you exactly what is wrong. You're passing in a window variable into the drawBox method, but you don't declare or initialize such a variable in the main method before doing so, and so this cannot be done in Java.
BoxyMethod c = new BoxyMethod();
// *** window variable below is used but never declared prior to use
c.drawBox(window, Color.RED, 200, 300);
More importantly though, you're not doing Swing drawing correctly.
Instead, you should create a class that extends JPanel, give it a paintComponent(Graphics g) method override, and draw in that method. Then place that JPanel in a JFrame and display the JFrame. Please check out the Performing Custom Painting Swing graphics tutorial for more detail on how to do Swing graphics.
As an aside, do not follow that tutorial that you've linked to as it is 30 years out of date.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.*;
public class BoxyTest {
private static void createAndShowGui() {
JFrame frame = new JFrame("Boxy Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new BoxyPanel(200, 300));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class BoxyPanel extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private int myX;
private int myY;
public BoxyPanel(int myX, int myY) {
this.myX = myX;
this.myY = myY;
}
#Override // so my JPanel will be big enough to see
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
// call super method so that the JPanel can do housekeeping painting
super.paintComponent(g);
g.fillRect(myX, myY, 100, 100);
g.setColor(Color.WHITE);
g.fillRect(myX + 10, myY + 10, 80, 80);
}
}
I am trying to build a simple basic to a game for my class in Game Engine Architecture. But my JFrame just wont show anything.
My code is currently structured like this:
Implementation.java (This is some arbitrary implementation of the Engine-package I'm creating)
public class Implementation {
public static void main(String[] args){
World w = new World("Hej", "M:\\workspace\\SP6\\pics\\tulips.jpg",1024,768);
}
}
World.java
public class World extends JFrame{
private static final long serialVersionUID = 1L;
private SpritePanel spritePanel;
private JPanel bottom;
private int width;
private int height;
public World(String windowCaption, String bgPath, int width, int height){
super(windowCaption);
spritePanel = new SpritePanel(bgPath);
add(spritePanel, BorderLayout.CENTER);
System.out.println(spritePanel);
bottom = new JPanel();
bottom.add(new JLabel("Hej"));
add(bottom, BorderLayout.SOUTH);
Dimension size = new Dimension(width,height);
setSize(size);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);
validate();
repaint();
}
SpritePanel.java
public class SpritePanel extends JPanel {
private static final long serialVersionUID = 1L;
private ImageIcon background;
private ArrayList<Sprite> sprites = new ArrayList<Sprite>();
public SpritePanel(String bgPath){
background = new ImageIcon(bgPath);
setLayout(null);
Dimension size = new Dimension(background.getIconWidth(), background.getIconHeight());
setPreferredSize(size);
setMaximumSize(size);
setMinimumSize(size);
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(background.getImage(), 0, 0, this);
System.out.println("painted panel");
}
}
So the basic action flow at the moment (from what I can see):
I create a new World object in the Implementation
The World-constructor is called with the given parameters
The window caption is set (this works)
I create a new SpritePanel object with the given bgPath
The SpritePanel-constructor is called and sets the ImageIcon to the image that exists in the given path
The SpritePanel is added to the frame in BorderLayout.CENTER
I add a new JPanel to the frame in BorderLayout.CENTER
I set size and stuff
I pack and set the frame to visible
I validate and repaint
The thing is the paintComponent methods in the JPanel and SpritePanel doesn't seem to get called. As you can see I added a System.out.println in the paintComponent for SpritePanel and that line is never executed.
Another thing I noticed is that the pack seems to know that the components are there. Because if I comment the three lines
spritePanel = new SpritePanel(bgPath);
add(spritePanel, BorderLayout.CENTER);
System.out.println(spritePanel);
The windows size when I run the program is reduced to the size of the "bottom"-JPanel. I can't see the JLabel that I added to the panel but the window size is the size of it. So the pack() method seems to be finding the dimensions of my components.
If I comment the three lines that adds the bottom panel as well the window size is reduced to no height and the width it gets from the standard icons.
I've been trying different things to get it to work but to no avail. I have programmed with Swing before and made working programs but I just can't seem to find the problem here.
Help very much appreciated.
Something may be wrong with your with your String path. What you should do though, instead o reading a file, you should be reading a URL, loading from the class path. At time of deployment, you'll find out that the file path won't work, so it's better to package your image as an embedded resource, putting the image in the class path. I had no problems when doing this. Here's what I did.
Change the path to a path relative to my class path
World w = new World("Hej", "/resources/stackoverflow5.png", 1024, 768);
Changed loading from a file to loading from a URL from my class path
background = new ImageIcon(SpritePanel.class.getResource(bgPath));
Put the image in resources package in my class path
Everything works fine
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Implementation {
public static void main(String[] args) {
World w = new World("Hej", "/resources/stackoverflow5.png", 1024, 768);
}
}
class World extends JFrame {
private static final long serialVersionUID = 1L;
private SpritePanel spritePanel;
private JPanel bottom;
private int width;
private int height;
public World(String windowCaption, String bgPath, int width, int height) {
super(windowCaption);
spritePanel = new SpritePanel(bgPath);
add(spritePanel, BorderLayout.CENTER);
System.out.println(spritePanel);
bottom = new JPanel();
bottom.add(new JLabel("Hej"));
add(bottom, BorderLayout.SOUTH);
Dimension size = new Dimension(width, height);
setSize(size);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);
validate();
repaint();
}
class SpritePanel extends JPanel {
private static final long serialVersionUID = 1L;
private ImageIcon background;
//private ArrayList<Sprite> sprites = new ArrayList<Sprite>();
public SpritePanel(String bgPath) {
background = new ImageIcon(SpritePanel.class.getResource(bgPath));
setLayout(null);
Dimension size = new Dimension(background.getIconWidth(), background.getIconHeight());
setPreferredSize(size);
setMaximumSize(size);
setMinimumSize(size);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(background.getImage(), 0, 0, this);
System.out.println("painted panel");
}
}
}
Side Notes
No need to setSize(). You already pack(). It better to just pack anyway.
Put the pack() before the setLocationRelativeTo(). If you pack() after, you'll notice your frame won't be in the desired location.
Run your Swing apps from the Event Dispatch Thread (EDT) like this
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new World("Hej", "/resources/stackoverflow5.png", 1024, 768);
}
});
}
If you want to make the frame fill the screen, don't set the size. Different machines have different screen sizes. Instead use setExtendedState(JFrame.MAXIMIZED_BOTH);
Also, your setSize() won't work anyway because you're calling pack() after.
My problem was that I hade overridden the getWidth() and getHeight() methods in my World-class.
But look to peeskillets response for how to make things better!