This question already has answers here:
paintComponent not painting onto JPanel
(2 answers)
Closed 5 years ago.
I'm making a game in Java and first I didn't use a JPanel which caused flickering on repaint() and so I decided to use it. I'm not sure how to implement it in my current code. When I tried to do so all I got was a window that was as small as it gets. My original Window class code:
public class Window extends JFrame {
private double stepLen;
public Window(double stepLen) {
this.stepLen = stepLen;
this.setSize(800, 600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.setTitle("Frogger");
this.setLayout(null);
getContentPane().setBackground(Color.black);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
int x = (dim.width - this.getSize().width)/2;
int y = (dim.height - this.getSize().height)/2;
this.setLocation(x, y);
JLabel goal = new JLabel();
goal.setText("|=========|");
goal.setForeground(Color.WHITE);
goal.setFont(new Font("Seif", Font.PLAIN, 20));
add(goal);
goal.setBounds(325, -10, 600, 50);
setFocusable(true);
requestFocusInWindow();
this.setVisible(true);
}
This code works and it creates a window.
Main class:
Window window = new Window(50);
And then I tried to do it this way:
I have separate GameFrame (JFrame) and GameCanvas (JPanel) classes.
The Frame looks like this:
public class GameFrame extends JFrame{
private double stepLen;
public GameFrame() {
this.stepLen = 50;
this.setSize(800, 600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.setTitle("Frogger");
this.setLayout(null);
this.setVisible(true);
this.getContentPane().setBackground(Color.black);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
int x = (dim.width - this.getSize().width)/2;
int y = (dim.height - this.getSize().height)/2;
GameCanvas gcanvas = new GameCanvas();
this.add(gcanvas);
this.pack();
this.setLocation(x, y);
}
}
}
And the GameCanvas class
public class GameCanvas extends JPanel {
public GameCanvas() {
setDoubleBuffered(true);
JLabel goal = new JLabel();
goal.setText("|=========|");
goal.setForeground(Color.WHITE);
goal.setFont(new Font("Seif", Font.PLAIN, 20));
this.add(goal);
goal.setBounds(325, -10, 600, 50);
this.getPreferredSize();
this.setVisible(true);
this.repaint();
}
Camickr is correct - go up vote and mark his answer as correct, this is only here to save him from pulling out what little hair he has remaining
You're using a null layout, without taking over its responsibility
Failed to provide sizing hints to for the component to allow the layout manager (which you're no longer using) to do it's job
This are all common mistakes, to which there are countless answers already provided
GameFrame
public class GameFrame extends JFrame {
private double stepLen;
public GameFrame() {
this.stepLen = 50;
this.setSize(800, 600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.setTitle("Frogger");
// Well, there's your problem...
//this.setLayout(null);
// Don't do this here...
this.setVisible(true);
this.getContentPane().setBackground(Color.black);
// Simpler way to achieve this
//Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
//int x = (dim.width - this.getSize().width) / 2;
//int y = (dim.height - this.getSize().height) / 2;
GameCanvas gcanvas = new GameCanvas();
this.add(gcanvas);
this.pack();
//this.setLocation(x, y);
setLocationRelativeTo(null);
setVisible(true);
}
}
GameCanvas
public class GameCanvas extends JPanel {
public GameCanvas() {
// Pointless
//setDoubleBuffered(true);
JLabel goal = new JLabel();
goal.setText("|=========|");
goal.setForeground(Color.WHITE);
goal.setFont(new Font("Seif", Font.PLAIN, 20));
this.add(goal);
// Pointless
//goal.setBounds(325, -10, 600, 50);
// Pointless
//this.getPreferredSize();
// Pointless
//this.setVisible(true);
// Pointless
//this.repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
public void paintComponent(Graphics g) {
int firstRoad = 5;
int i = 0;
int max = 10;
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g2);
g2.setColor(Color.WHITE);
g2.drawRect(5, 30, 75, 40);
while (i < max) {
g2.setColor(Color.WHITE);
g2.setStroke(new BasicStroke(3));
if (i % 2 == 0) {
g.setColor(Color.WHITE);
g.drawRect(3, firstRoad + 50 * i, 793, 50);
//g.fillRect(3, firstRoad + 50 * i, 793, 50);
} else {
g2.setColor(Color.WHITE);
g2.drawRect(3, firstRoad + 50 * i, 793, 50);
}
i++;
}
}
}
So, the way I was taught in my AP Computer Science class is to set your frame size and other frame characteristics in your main. Here is an example:
import javax.swing.JFrame;
public class theSetupClass{
public static void main(String[] args){
JFrame theGUI = new JFrame();
theGUI.setSize(300,400); //Sets the frame size to 300 by 400
theGUI.setTitle("Example");
theGUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theComponentClass component = new theComponentClass(); //Create new theComponentClass
frame.add(component);//Add theComponentClass to theGUI
frame.setVisible(true);
}
}
The code above creates the JFrame and adds the following class to it.
import java.awt.*;
import javax.swing.*;
public class theComponentClass extends JComponent{
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
Rectangle r = new Rectangle(10,10,this.getWidth()-10,this.getHeight()-10);
//Creates a rectangle that is 10 pixels away from all sides of the frame
g2.fill(r); //Draws and fills the rectangle
}
}
I hope that you find this helpful!
Related
Here is a minimal code to see the bug:
import javax.swing.*;
import java.awt.*;
public class Main1 extends JFrame {
static Main1 main;
public Main1() {
super("app");
}
public static void main(String[] args) {
main = new Main1();
main.setBounds(300, 300, 800, 500);
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setVisible(true);
Graphics g = main.getGraphics();
for(int i = 0; i < 100; i++){
g.setColor(new Color(255, 0, 0));
g.fillRect(0, 0, 800, 500);
}
}
}
If i use 100 in the "for" cycle, the frame appears not to be colored, but 200 loops is enough to color it.
I want to make an application where frames change rarely, but this feature ruins the quality of code because I have to make a number of dummy frames.
public static void main(String[] args) {
main = new Main1();
main.setBounds(300, 300, 800, 500);
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setVisible(true);
Graphics g = main.getGraphics();
for(int i = 0; i < 100; i++){
g.setColor(new Color(255, 0, 0));
g.fillRect(0, 0, 800, 500);
}
}
This is not how you do Swing graphics. Getting a Graphics object by calling .getGraphics() on a component gives you a short-lived unstable and sometimes null object. For instance, it takes some time for the created JFrame to render, and if you call getGraphics() and try to use it prior to rendering, the object may be null, and certainly won't wokr.
Instead paint within a JPanel's paintComponent method using the Graphics object given by the JVM as per the tutorials:
public class MainPanel extends JPanel {
public MainPanel {
setPreferredSize(new Dimension(800, 500)));
setBackground(new Color(255, 0, 0)); // if you just want to set background
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// use g here do do your drawing
}
}
and then use it like so:
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new MainPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
Tutorial: Lesson: Performing Custom Painting
And yes, if you want to drive a simple animation, use a Swing Timer to help drive it like so:
public class MainPanel extends JPanel {
private int x = 0;
private int y = 0;
public MainPanel {
setPreferredSize(new Dimension(800, 500)));
setBackground(new Color(255, 0, 0)); // if you just want to set background
// timer code:
int timerDelay = 15;
new Timer(timerDelay, ()-> {
x += 4;
y += 4;
repaint();
}).start();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// use g here do do your drawing
g.setColor(Color.BLUE);
g.drawRect(x, y, 20, 20);
}
}
To create my first 2D game in Java, I thought of using the JFrame's getContentPane(), updating it with the new view every 50ms.
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// ...
frame.setVisible(true);
// ...
Container area = frame.getContentPane();
Graphics pen = area.getGraphics();
pen.clearRect(0, 0, area.getWidth(), area.getHeight()); // Remove previous drawing
pen.drawString("Text", 50, 50);
// ...
area.repaint();
But it doesn't work; the window doesn't change.
As kiheru already said, use paintComponent(Graphics g) for custom painting. Here is an example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class Example {
int i = 0;
public Example() {
JFrame frame = new JFrame();
frame.getContentPane().add(new DrawingPanel());
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
ActionListener actionListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
frame.getContentPane().repaint();
}
};
Timer timer = new Timer(500, actionListener); //500 = Every 500 milliseconds
timer.start();
}
class DrawingPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // Removes previous graphics
Random r = new Random(); //Randomizer
//Random x- and y-coordinates
int x = r.nextInt(400);
int y = r.nextInt(400);
//Random rgb-values
int red = r.nextInt(255);
int green = r.nextInt(255);
int blue = r.nextInt(255);
//Random width and height
int width = r.nextInt(100);
int height = r.nextInt(100);
g.setColor(new Color(red, green, blue)); //Setting color of the graphics
g.fillRect(x, y, width, height); //Filling a rectangle
}
}
public static void main(String[] args) {
new Example();
}
}
research for double buffering or for fast image drawing using VolatileImage type for directly rending into graphics card. In your case if you use double buffering the code will be:
private static BufferedImage bufferedImage = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
private static Graphics2D gBuff = bufferedImage.createGraphics();
public static void main(String[] args)
{
JFrame frame = new JFrame()
{
#Override
public void paint(Graphics g)
{
g.drawImage(bufferedImage,0,0,this);
}
};
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
gBuff.setColor(Color.WHITE);
gBuff.fillRect(0, 0, frame.getWidth(), frame.getHeight()); // Remove previous drawing
gBuff.setColor(Color.BLACK);
gBuff.drawString("Text", 50, 50);
// ...
frame.setVisible(true);
// ...
}
Currently, I have to JComponents contained in a JPanel with a vertical box layout.
This way, I can have the first component centered, as shown below, and have the bottom component (which is quite long) below. However, since the bottom component is very long I wanted to add a slider just for that specific component. This way, the user can see all of the bottom component with the upper component remaining centered. However, my code below doesn't fix anything and the scrollbar never even works. The only information about GPComponent and GPinfinity you need to know is they override the preferredSize, minimumSize, maximumSize, and paintComponent methods (they extend JComponent).
JFrame frame = new JFrame();
JPanel panel = new JPanel();
GPComponent gp = new GPComponent(n, k);
GPinfinityComponent gpi = new GPinfinityComponent(n, k);
Box box = new Box(BoxLayout.Y_AXIS);
panel.add(Box.createVerticalGlue());
panel.add(gp);
panel.add(Box.createVerticalGlue());
JScrollPane thePane = new JScrollPane(gpi, JScrollPane.VERTICAL_SCROLLBAR_NEVER,JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
panel.add(thePane);
frame.pack();6
frame.add(panel, BorderLayout.CENTER); // just to be clear
frame.setVisible(true);
final int FRAME_WIDTH = 600;
final int FRAME_HEIGHT = 600;
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle("GP("+n+", "+k+")");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Also: the maximumSize=minimumSize=preferredSize for both components
For the circular one the dimensions are (350, 350) and for the other the dimensions are (5000, 150).
You state:
...and for the other the dimensions are (5000, 150).
If this is the component that is supposed to show the scrollbars, the Java is telling you otherwise, that it is in fact much shorter than you suppose it to be. I wonder if you're setting size instead of preferredSize. You actually should not be setting either but rather should override getPreferredSize() and have it return a dimension appropriate for the component.
For more detailed help, consider creating and posting a minimal example program.
Edit
For example, my MCVE:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import javax.swing.*;
#SuppressWarnings("serial")
public class PreferredSizeEg extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 600;
public PreferredSizeEg() {
JPanel centerPanel = new JPanel(new GridBagLayout());
centerPanel.add(new CenterImagePanel());
JScrollPane scrollpane = new JScrollPane(new LongImagePanel(),
JScrollPane.VERTICAL_SCROLLBAR_NEVER,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
setLayout(new BorderLayout());
add(centerPanel, BorderLayout.CENTER);
add(scrollpane, BorderLayout.PAGE_END);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private class LongImagePanel extends JPanel {
private static final int LI_PREF_W = 5000;
private static final int LI_PREF_H = 150;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int index = 0;
int spriteWidth = 50;
while ((index) * spriteWidth < getWidth()) {
Color c = index % 2 == 0 ? Color.green : Color.red;
g.setColor(c);
int x = 2 + index * spriteWidth;
int y = 2;
int width = getHeight() - 4;
int height = width;
g.fillOval(x, y, width, height);
index++;
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(LI_PREF_W, LI_PREF_H);
}
}
private class CenterImagePanel extends JPanel {
private static final int CIP_PREF_W = 200;
private static final int CIP_PREF_H = CIP_PREF_W;
public CenterImagePanel() {
setBorder(BorderFactory.createLineBorder(Color.BLACK));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.green);
int x = 5;
int y = x;
int width = getWidth() - 2 * x;
int height = getHeight() - 2 * y;
g.fillOval(x, y, width, height);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(CIP_PREF_W, CIP_PREF_H);
}
}
private static void createAndShowGui() {
PreferredSizeEg mainPanel = new PreferredSizeEg();
JFrame frame = new JFrame("PreferredSizeEg");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Which displays as:
I have a custom JPanel, which the paintComponent method is overridden to paint an image.
I want to insert several of these custom panels vertically centered in a container. To do this I created a jpanel with BoxLayout.X_AXIS as layout manager.
This works great and shows what I want, but I would like to add margins between the custom panels.
The EmptyMargins are just ignored, and the tricky part is that I can't (or would not like to...) add struts or boxes between them because I need to get each custom panel from a loop which takes all components of the container and cast them into CustomPanel.
See the problem ? If I add struts between the panels there will be a cast exception and EmptyBorders aren't working... Any ideas welcome!
Note : I'm open to other layout manager propositions ! ;-)
Here is the code :
public class StackExemple {
public StackExemple() {
JFrame frame = new JFrame();
frame.setPreferredSize(new Dimension(600, 300));
JPanel container = new JPanel();
container.setPreferredSize(new Dimension(600, 300));
container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS));
CustomPanel customPanel1 = new CustomPanel();
CustomPanel customPanel2 = new CustomPanel();
CustomPanel customPanel3 = new CustomPanel();
container.add(customPanel1);
container.add(customPanel2);
container.add(customPanel3);
frame.getContentPane().add(container);
frame.pack();
frame.setVisible(true);
//Loop which takes the custompanels
for(Component comp : container.getComponents()) {
CustomPanel panel = (CustomPanel)comp;
//DO SOMETHING
System.out.println("Hello World");
}
}
private class CustomPanel extends JPanel{
private BufferedImage image;
public CustomPanel() {
setPreferredSize(new Dimension(100, 100));
setMinimumSize(getPreferredSize());
setMaximumSize(getPreferredSize());
setBorder(BorderFactory.createEmptyBorder(0,50,0,0));
setBackground(Color.RED);
// try {
// image = ImageIO.read(ClassLoader.getSystemClassLoader().getResource("Ressources/img.png"));
// } catch (IOException ex) {
// System.out.println("Ooops... ");
// }
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
// int x = (this.getWidth() - image.getWidth()) / 2;
// int y = (this.getHeight() - image.getHeight()) / 2;
// g.drawImage(image, x, y, null);
}
}
}
Borders are correct, have to getBackground from parent for LineBorders
override Min / Max / PreferredSize for BoxLayout
BoxLayout accepting Min / Max / PreferredSize by default
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class StackExemple {
public StackExemple() {
JFrame frame = new JFrame();
JPanel container = new JPanel();
container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS));
CustomPanel customPanel1 = new CustomPanel(Color.blue);
CustomPanel customPanel2 = new CustomPanel(Color.red);
CustomPanel customPanel3 = new CustomPanel(Color.green);
container.add(customPanel1);
container.add(customPanel2);
container.add(customPanel3);
frame.getContentPane().add(container);
frame.pack();
frame.setVisible(true);
for (Component comp : container.getComponents()) {
CustomPanel panel = (CustomPanel) comp;
System.out.println("Hello World");
}
}
private class CustomPanel extends JPanel {
private BufferedImage image;
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 80);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 160);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(400, 320);
}
public CustomPanel(Color c) {
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(10, 10, 10, 10),
BorderFactory.createLineBorder(Color.black, 1)));
//setBorder(BorderFactory.createCompoundBorder(
//BorderFactory.createLineBorder(Color.black, 1),
//BorderFactory.createEmptyBorder(10, 10, 10, 10)));
setBackground(c);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
StackExemple stackExemple = new StackExemple();
}
});
}
}
The underlying reason that the border appears to not be respected is that the panel by default is opaque, that is it guarantees to fill each pixel of its area with a fully solid background color. The area covered by the border is part of the panel's area, so must be filled with the panel's background as well.
As you seem to be doing custom painting anyway, you might consider to report its opaqueness as false and only paint the background (and/or the background image) inside the bordered area:
// in constructor
setOpaque(false);
#Override
protected void paintComponent(Graphics g) {
// take over background filling inside the border
Insets insets = getInsets();
g.setColor(getBackground());
g.fillRect(insets.left, insets.top,
getWidth() - insets.left - insets.right,
getHeight() - insets.top - insets.bottom);
super.paintComponent(g);
// for a background image, you would need to take the insets
// into account as well
// int x = (this.getWidth() - image.getWidth()) / 2;
// int y = (this.getHeight() - image.getHeight()) / 2;
// g.drawImage(image, x, y, null);
}
I'm doing some exercise to understand Java and Swing API. Why do I have a nullPointerException in the Disegno constructor? I want to print the coordinates of the two rectangles, but they seem not to be initialitied.
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Disegno extends JFrame{
Disegno(){
this.setSize(500, 500);
this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
MyPanel aba = new MyPanel();
this.setContentPane(aba);
this.setVisible(true);
System.out.println(aba.rect.blue.x + "-" + aba.rect.blue.y);
System.out.println(aba.rect.yellow.x + "-" + aba.rect.yellow.y);
}
public static void main(String[] args){
new Disegno();
}
}
class MyPanel extends JPanel{
JPanel up, down;
RectArea rect;
MyPanel(){
this.setLayout(new BorderLayout());
up = new JPanel();
this.add(up, BorderLayout.NORTH);
up.setBackground(Color.red);
up.setVisible(true);
down = new JPanel();
down.setBackground(Color.green);
this.add(down, BorderLayout.SOUTH);
down.setVisible(true);
rect = new RectArea();
this.add(rect, BorderLayout.CENTER);
this.setVisible(true);
}
}
class RectArea extends JPanel{
Rectangle blue, yellow;
boolean check = false;
RectArea(){
super();
this.setVisible(true);
}
public void initRect(){
blue = new Rectangle(0, 0, 100, 100);
yellow = new Rectangle(this.getWidth(), this.getHeight(), 100, 100);
System.out.println("ok");
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
if(check == false){
this.initRect();
check = true;
}
System.out.println(this.getWidth() + "-" + this.getHeight());
g.setColor(Color.blue);
g.fillRect(blue.x, blue.y, blue.width, blue.height);
g.setColor(Color.yellow);
g.fillRect(yellow.x - yellow.width, yellow.y - yellow.height, yellow.width, yellow.height);
}
}
Others have helpfully suggested ways to detect and avoid the NullPointerException. Unfortunately, you can't rely on when your implementation of paintComponent() will be called. Instead,
Determine the required geometry as a function of the current widow's size; resize the window in the example below to see how yellow seems to stick to the bottom right corner.
Because MyPanel contains no components of its own, you should override getPreferredSize(), as #nIcE cOw shows here.
Use pack() to size the enclosing Window.
Build on the event dispatch thread.
Addendum: I can't understand why you override the method getPreferredSize().
Subclasses of JComponent override getPreferredSize() so that pack() can size the Window "to fit the preferred size and layouts of its subcomponents." That way you don't have to worry if the user has a different font, for example. MyPanel just draws geometric shapes, so you're the boss on preferred size. As discussed here, a demo may use setPreferredSize() for convenience, but you should understand the limitations of doing so.
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/q/11376272/230513
*/
public class Disegno extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Disegno();
}
});
}
Disegno() {
this.setSize(500, 500);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
MyPanel aba = new MyPanel();
this.add(aba);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
class MyPanel extends JPanel {
private JPanel up, down;
private RectArea rect;
MyPanel() {
super(new BorderLayout());
up = new JPanel();
up.setBackground(Color.red);
this.add(up, BorderLayout.NORTH);
rect = new RectArea();
this.add(rect, BorderLayout.CENTER);
down = new JPanel();
down.setBackground(Color.green);
this.add(down, BorderLayout.SOUTH);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
}
class RectArea extends JPanel {
private Rectangle blue = new Rectangle(0, 0, 100, 100);
private Rectangle yellow = new Rectangle(0, 0, 100, 100);
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println(this.getWidth() + " x " + this.getHeight());
g.setColor(Color.blue);
g.fillRect(blue.x, blue.y, blue.width, blue.height);
g.setColor(Color.yellow);
int dx = getWidth() - yellow.width;
int dy = getHeight() - yellow.height;
g.fillRect(dx, dy, yellow.width, yellow.height);
}
}
}
You never called rect.initRect(); anywhere, that's why you getting error related to NullPointerException at those System.out.println() lines. Why you using panelObject.setVisible(true) inside each class, first add them to the JPanel and simply call setVisible(...) on the JFrame that will do. Here watch your modified code with the said thingies, working as expected :
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Disegno extends JFrame{
Disegno(){
this.setSize(500, 500);
this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
MyPanel aba = new MyPanel();
this.setContentPane(aba);
this.setVisible(true);
System.out.println(aba.rect.blue.x + "-" + aba.rect.blue.y);
System.out.println(aba.rect.yellow.x + "-" + aba.rect.yellow.y);
}
public static void main(String[] args){
new Disegno();
}
}
class MyPanel extends JPanel{
JPanel up, down;
RectArea rect;
MyPanel(){
this.setLayout(new BorderLayout());
up = new JPanel();
this.add(up, BorderLayout.NORTH);
up.setOpaque(true);
up.setBackground(Color.red);
down = new JPanel();
down.setOpaque(true);
down.setBackground(Color.green);
this.add(down, BorderLayout.SOUTH);
rect = new RectArea();
rect.initRect();
this.add(rect, BorderLayout.CENTER);
}
}
class RectArea extends JPanel{
Rectangle blue, yellow;
boolean check = false;
RectArea(){
super();
setOpaque(true);
}
public void initRect(){
blue = new Rectangle(0, 0, 100, 100);
yellow = new Rectangle(this.getWidth(), this.getHeight(), 100, 100);
System.out.println("ok");
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
if(check == false){
this.initRect();
check = true;
}
System.out.println(this.getWidth() + "-" + this.getHeight());
g.setColor(Color.blue);
g.fillRect(blue.x, blue.y, blue.width, blue.height);
g.setColor(Color.yellow);
g.fillRect(yellow.x - yellow.width, yellow.y - yellow.height, yellow.width, yellow.height);
}
}
If you would write a System.out.println() inside the intiRect() you will know, the lines which are giving you errors are being called before the paintComponent(...) method itself. So it appears to me, that you have to take that logic out of the paintComponent(...) method and keep it somewhere else, or else remove those lines, if you don't need them.
You are trying to access aba.rect.blue and aba.rect.yellow in your Disegno constructor, however these are not initialized until RectArea.paintComponent is called, so it throws an NPE.
Are you sure that your code gave a NullPointerException .......??
Because when i ran your code, it worked fine...
Output:
ok
484-442