Skywalker
Skywalker

Reputation: 39

JFrame pixels don't add up. Maybe the screen is not compatible with how JFrame works?

So I tried to get a 640 by 640 pixel screen and fill it with a grid of squares (20x20). But when I make every sqare 32 by 32 pixels, the screen of 640x640 is too small (which is weird, because 32x20=640).

Code Class Game:

package main;

import javax.swing.JFrame;

public class Game extends JFrame{
    
    private GameScreen gameScreen;

    public Game() {
        setSize(640, 640);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        
        gameScreen = new GameScreen();
        add(gameScreen);
    }

    public static void main(String[] args) {
        Game game = new Game();
    }
}

Code Class GameScreen:

package main;

import java.awt.Color;
import java.awt.Graphics;
import java.time.Year;
import java.util.Random;
import javax.swing.JPanel;

public class GameScreen extends JPanel{
    
    private Random random;
    
     public GameScreen() {
         random = new Random();
     }
     public void paintComponent(Graphics g) {
         
         super.paintComponent(g);
         
         for (int x = 0; x < 20; x++) {
             for (int y = 0; y < 20; y++) {
                g.setColor(getRandColor());
                g.fillRect(x*32, y*32, 32, 32);
             }
         }
     }
     private Color getRandColor() {
         int r = random.nextInt(256);
         int g = random.nextInt(256);
         int b = random.nextInt(256);
         return new Color(r, g, b);
     }
}

Problems:

  1. The screen cuts off about half a square in width and about 1.2 squares in height. Why? Does it depend on the resolution of my screen?
  2. Often, when i run the class Game the screen opens blank with no squares at all. This changes kinda randomly. Then it works, when I reposition the cursor, and stops working when i run it again... (I work with eclipse).
  3. How does anything get drawn at all when the paintComponent(Graphics g) method is never called in the code?

(Code from youtube tutorial by "Kaarin Gaming". His code works just fine)

Upvotes: 0

Views: 186

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347184

  1. The screen cuts off about half a square in width and about 1.2 squares in height. Why? Does it depend on the resolution of my screen?

You seem to be in the illusion that frame decorations will be wrapped around/outside of the content.

The problem is, this isn't how it works. The size of a window must include the decorations. This means the available space to the component/content is the window size - decorations insets, for example...

Basic size

import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test {
    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.setSize(640, 640);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            FontMetrics fm = g2d.getFontMetrics();
            int width = getWidth();
            int height = getHeight();
            String text = width + "x" + height;
            int xPos = (width - fm.stringWidth(text)) / 2;
            int yPos = ((height - fm.getHeight()) / 2) + fm.getAscent();
            g2d.drawString(text, xPos, yPos);
            g2d.dispose();
        }

    }
}

So, what's the solution? Make use of the layout management APIs.

One of the things a component can do is provide sizing hints that allow the layout manager, which can be used to adjust the size of the component and by extension, the window, for example:

PreferredSize

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private int cellWidth = 32;
        private int cellHeight = 32;

        private int columnCount = 20;
        private int rowCount = 20;

        public TestPane() {
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(cellWidth * columnCount, cellHeight * rowCount);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            for (int x = 0; x < columnCount; x++) {
                for (int y = 0; y < rowCount; y++) {
                    g2d.setColor(getRandColor());
                    g2d.fillRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight);
                }
            }
            g2d.dispose();
        }

        private Random random = new Random();

        private Color getRandColor() {
            int r = random.nextInt(256);
            int g = random.nextInt(256);
            int b = random.nextInt(256);
            return new Color(r, g, b);
        }
    }
}

Firstly, by using getPreferredSize, the child component can tell a parent container how much space it needs to be displayed under normal circumstances.

Secondly, by using JFrame#pack, will allow the window to determine the size it needs to be, so the content pane's preferred size is preserved and then the window decorations applied around it. So, now the "window" is bigger then the content.

  1. Often, when i run the class Game the screen opens blank with no squares at all. This changes kinda randomly. Then it works, when I reposition the cursor, and stops working when i run it again... (I work with eclipse).

This suggests that the window was made visible before the component was added to the container. Swing is lazy. When you add or change a component, you are often expected to call revalidate and repaint on the parent container in order to get the layout and paint passes to run.

Another problem is, it's possible that the component might have been added to the container from a thread other then the Event Dispatching Thread.

  1. How does anything get drawn at all when the paintComponent(Graphics g) method is never called in the code?

If a component's size is 0x0, it won't be painted, Swing is smart. See the previous point as to while this might happen.

Upvotes: 1

Related Questions