Note that this explanation assumes that we are using one JPanel inside a JFrame. (We're not using a ContentPane either since don't see the point of them.)
So our graphics look like this:
And in order to draw on a JPanel, we actually make a subclass of JPanel, commonly called DrawingPanel, DrawPanel or GraphicsPanel.
Using a DrawingPanel class then permits us to write our own paintComponent(Graphics g) {} method that does all the drawing and allows us access to the very useful drawing methods of the Graphics and Graphics2D objects (commonly named g and g2).
There are three ways of doing this Choose one:
window.setSize(900, 700);
(where window is the name of the JFrame object).
Dimension fullScreen = Toolkit.getDefaultToolkit().getScreenSize(); window.setSize(fullScreen);** This way is better:
window.setExtendedState(JFrame.MAXIMIZED_BOTH);
int scrW = window.getWidth();
and int scrH = window.getHeight();
window.pack();
to arrange them
in the smallest space available. If you use pack(), then setSize() is ignored.
Sample code
JFrame window = new JFrame();
window.setTitle("Title of JFrame");
window.add(panel);
window.setSize(900,800); // or window.pack();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setLocationRelativeTo(null);
window.setVisible(true);
If you are drawing on the JPanel, you cannot base your measurements on the size of the JFrame. The JFrame includes the borders and the area at the top which has the title, close button, etc.
If you want to draw a horizontal line across the middle or the screen, you cannot do window.getHeight()/2
In this image, the circle is drawing in the exact centre of the JPanel or JFrame. You can see that these are two different locations. Using the JFrame coordinates for drawing will not give you the results that you desire.
This means that we need to be able to specify the JPanel size.
JPanel panel = new DrawingPanel();
panel.setPreferredSize(new Dimension(800,700));
Yes, this requires a Dimension object, not two ints.window.setResizable(false);
(optional)window.pack();
If you've followed these steps, then your JPanel is exactly the number of pixels that you want it to be.
More Sample code (Since I'm extending JPanel, I'll put the code in the constructor)
class DrawPanel extends JPanel{
int panW = 600;
int panH = 400;
DrawPanel() {
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(panW, panH));
}
@Override
public void paintComponent(Graphics g) {
g.drawLine(0, panH/2, panW, panH/2);
...
}
If you look at the TicTacToe program, you'll see how we adjust the graphics to scale as the JFrame is resized
Explanation of how it works
frame.setSize(600, 620);
initGraphics()
which finds the new size of the JPanel and sets our instance variables.void initGraphics() { jpanW = this.getSize().width; jpanH = this.getSize().height; blockX = (int)((jpanW/GRID)+0.5); blockY = (int)((jpanH/GRID)+0.5); }
blockX = 40;
Problem If you have a lot of squares, then there is a rounding error that accumulates and you see large borders at the bottom and right sides.
Example: JPanel must always be square
This is actually a common situation. You make something that only works properly in a square JPanel, but you want to be able to drag it bigger or smaller. See the following code for how to implement it:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/**********************************************************************
* Adding the componentListener and square resizing to the JFrame *
* crashes the program. So do it in the JPanel. *
**********************************************************************/
public class SquareResizeOnly extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SquareResizeOnly();
}
});
}
SquareResizeOnly(){
this.add(new DrawPanel());
this.setTitle("This always resizes to a square when dragged");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setVisible(true);
}
class DrawPanel extends JPanel implements ComponentListener {
int panSize=400;
DrawPanel() {
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(panSize, panSize));
this.addComponentListener(this);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g); //clear screen and repaint using background colour
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int r = 20; //circle radius
g.setColor(Color.GREEN.darker());
g.drawLine(0, panSize/2, panSize/2, panSize/2);
g.drawLine(panSize/2, panSize/2, panSize/2, panSize);
g.setColor(Color.BLUE);
g.drawOval(panSize/2-r/2, panSize/2-r/2, r, r);
}
/* Resizing the screen triggers a Component Event. Listen for it */
@Override
public void componentResized(ComponentEvent e) {
//find size of JPanel
int w = this.getWidth();
int h = this.getHeight();
panSize = (w>h)? h:w; //take the smallest number
//resize the JPanel to the newly determined size
this.setPreferredSize(new Dimension(panSize, panSize));
//run the pack() method in the outer class.
SquareResizeOnly.this.pack();
this.repaint();
}
public void componentHidden(ComponentEvent e) {}
public void componentMoved (ComponentEvent e) {}
public void componentShown (ComponentEvent e) {}
}
}
Same program without inner class for JPanel (compare with above)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SquareResizeOnly extends JPanel implements ComponentListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SquareResizeOnly();
}
});
}
//global variables
JFrame window;
int panSize=400;
//constructor
SquareResizeOnly(){
//setup JPanel
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(panSize, panSize));
this.addComponentListener(this);
//setup JFrame
window = new JFrame();
window.add(this);
window.setTitle("This always resizes to a square when dragged");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.pack();
window.setVisible(true);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int r = 20; //circle radius
g.setColor(Color.GREEN.darker());
g.drawLine(0, panSize/2, panSize/2, panSize/2);
g.drawLine(panSize/2, panSize/2, panSize/2, panSize);
g.setColor(Color.BLUE);
g.drawOval(panSize/2-r/2, panSize/2-r/2, r, r);
}
/* Resizing the screen triggers a Component Event. Listen for it */
@Override
public void componentResized(ComponentEvent e) {
//find size of JPanel
int w = this.getWidth();
int h = this.getHeight();
panSize = (w>h)? h:w; //take the smallest number
this.setPreferredSize(new Dimension(panSize, panSize));
window.pack();
this.repaint();
}
public void componentHidden(ComponentEvent e) {}
public void componentMoved (ComponentEvent e) {}
public void componentShown (ComponentEvent e) {}
}