paintComponent not called when adding custom JComponent - java

Why is paintComponent(Graphics) not called when adding a custom JComponent?
public class Test {
public static void main(String[] args) {
JFrame frame = new JFrame("Paint Component Example");
frame.setPreferredSize(new Dimension(750, 750));
frame.setLocationByPlatform(true);
JPanel panel = new JPanel();
panel.add(new CustomComponent());
frame.add(panel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
public class CustomComponent extends JComponent {
public CustomComponent() {
super();
}
#Override
protected void paintComponent(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(10, 10, 10, 10);
}
}
I know there is no reason for creating a custom component in this instance of but it is an extremely simplified version of another issue I can't figure out.

JPanel panel = new JPanel();
panel.add(new CustomComponent());
The default layout manager of a JPanel is a FlowLayout. A FlowLayout will respect the preferred size of any component added to it. By default the preferred size of a JComponent is (0, 0) so there is nothing to paint so the paintComponent() method never gets called.
Override the getPreferredSize() method of your CustomComponent class to return the preferred size of your component.
Also, don't forget to invoke super.paintComponent(...) at the start of the method.
Read the section from the Swing tutorial on Custom Painting for more information and working examples.

Related

Java: JLabel not getting added on JFrame

Why am I not getting the label on the screen. This is my code :
class Guide extends JComponent {
public static void main(String[] args) {
Guide guide = new Guide();
JFrame frame = new JFrame("Pong Game's Guide");
frame.add(guide);
frame.pack();
frame.setSize(600,500);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
JLabel guideLabel = new JLabel();
guideLabel.setText("GUIDE");
guideLabel.setFont(new Font("Serif",Font.BOLD, 20));
guideLabel.setForeground(new Color(255,0,0));
guideLabel.setBounds(290,25,50,20);
frame.add(guideLabel);
}
public void paintComponent(Graphics g) {
g.setColor(new Color(150,255,150));
g.fillRect(0,0,690,590);
}
}
What am I doing wrong here? Please help.
You’ve made the window visible before adding the label. You will need to revalidate and repaint the container
JFrame by default uses a BorderLayout, which by default only allows a single component to be laid out in each of 5 available positions, which likely to cause issues. Probably better to add it to guide
Try adding the label to the frame, then setting the visibility of the frame to true.

How to make JFrame look and feel aware

When I set a look and feel through the UIManager.setLookAndFeel the frame's content changes its look and feel as expected. E.g.
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
JFrame frame = new JFrame();
Container contentPane = frame.getContentPane();
contentPane.setLayout(new FlowLayout());
contentPane.add(new JButton("Some Button"));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(200, 80);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
But the frame is still painted using the OS default (Windows in my case).
How can I make the frame look and feel aware so that it looks like the e.g. the nimbus look and feel?
Usually look and feel is implemented using the ComponentUI, but a JFrame is not a JComponent so I can't implement an own ComponentUI and set it.
1. Solution
My first thought was to use an undecorated JFrame with a JInternalFrame as its main component.
public class LAFAwareJFrame extends JFrame {
private JInternalFrame lafFrame = new JInternalFrame("", true, true, true, true);
private JDesktopPane desktopPane = new JDesktopPane();
public LAFAwareJFrame() {
super.setUndecorated(true);
desktopPane.add(lafFrame);
lafFrame.setVisible(true);
Container contentPane = super.getContentPane();
contentPane.add(desktopPane);
}
#Override
public void setUndecorated(boolean undecorated) {
throw new UnsupportedOperationException("Can't change the undecorated property for a LAFAwareJFrame");
}
#Override
public void setSize(int width, int height) {
super.setSize(width, height);
lafFrame.setSize(width, height);
}
#Override
public Container getContentPane() {
return lafFrame.getContentPane();
}
#Override
public void setTitle(String title) {
lafFrame.setTitle(title);
}
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
JFrame frame = new LAFAwareJFrame();
Container contentPane = frame.getContentPane();
contentPane.setLayout(new FlowLayout());
contentPane.add(new JButton("Some Button"));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(200, 80);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
But then I have a lot to do with event handling and delegation. E.g when the JInternalFrame gets moved I don't really want to move the internal frame. Instead I want to move the undecorated frame.
2. Solution
Use the JInternalFrame only as a renderer. Like a ListCellRender.
Since all solutions require a lot of work I want to ask you first if there is a better solution. E.g. a library or maybe it is already possible with standard Java and I missed something.
EDIT
I tried to use setDefaultLookAndFeelDecorated but it doesn't work with nimbus and not with motif.

paintComponent overriding to draw on a panel

This is a noob question.
We are being taught applets in class, and I was trying something on my own.
The following is the code
import java.awt.*;
import javax.swing.*;
class controls extends JPanel{
#Override public void paintComponent(Graphics g) {
g.drawOval(50, 50, 50, 50); // <-- draws an oval on the panel
}
}
public class test extends JApplet{
public void init(){
final JPanel stage = new JPanel();
final JPanel controlPanel = new controls();
final JPanel banner = new JPanel();
final JLabel name = new JLabel("Test", JLabel.CENTER);
this.setLayout(new BorderLayout());
banner.setBackground(Color.CYAN);
banner.add(name);
this.add(controlPanel, BorderLayout.WEST);
this.add(banner, BorderLayout.NORTH);
}
}
As far as I understand, paintComponent() need not be called explicitly.
The controls class works well when used alone.
I mean the following code works.
public class test extends JApplet{
public void init(){
JPanel controlPanel = new controls();
this.add(controlPanel);
}
}
I am not able to understand the difference. Why does the same code work in this case, and not in the previous?
Thank you.
Override public Dimension getPreferredSize() (and return a new Dimension) in the controls class. When putting components in WEST the width will be determined by the preferredSize. If you don't override getPreferredSize, the preferred size will be 0. The CENTER will take up the rest of the space, after the WEST, ect is calculated. The second case works because it is in the CENTER of the default BorderLayout

Having trouble adding graphics to JPanel

I have looked online, but I am still having trouble understanding how to add graphics to a JPanel
Here is the code for my panel class:
public class GamePanel extends JPanel {
public GamePanel(){
}
public void paintComponent(Graphics g) {
g.drawString("asd", 5, 5);
}
}
And my main method:
public static void main(String[] args) {
frame.setLayout(new FlowLayout());
frame.getContentPane().setBackground(Color.WHITE);
//i is an instance of GamePanel
frame.add(i);
frame.setPreferredSize(new Dimension(500, 500));
frame.pack();
frame.setVisible(true);
}
Text will only appear in a very tiny section of the screen (this applies to any graphics object I try to draw). What am I doing wrong?
FlowLayout respects preferred sizes of components. Therefore override getPreferredSize to give your JPanel a visible size rather than the default zero size Dimension that the panel currently has after JFrame#pack is called:
class GamePanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g); // added to paint child components
g.drawString("asd", 5, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}
Edit:
To eliminate gap between JPanel and its containing JFrame, set the vertical gap to 0:
frame.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
Two things jump out
Your Game panel has no preferred size, which, by default, makes 0x0. FlowLayout will use this information to make decisions about how best to layout your panel. Because the size is 0x0, the repaint manager will ignore it. Try overriding the getPreferredSize method and return a appropriate size or use a layout manager that does not use the preferred size, like BorderLayout
Your paintComponent method MUST call super.paintComponet

How to fit the rectangle in the frame proportionally?

Try to draw a rectangle with different size, How to fit it in one frame proportionally(assume the frame is fixed)?
public class Draw extends JComponent {
public void paint(Graphics g) {
int width = 100;
int length = 100;
g.drawRect(10, 10, width, length);
}
}
public class DrawRect {
public static void main(String[] a) {
JFrame frame = new JFrame();
frame.setSize(400, 600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Container content = frame.getContentPane();
content.add(new Draw());
}
}
Custom painting is done by overriding the paintComponent(...) method, not the paint() method. This advice is made daily. Search the forum for more information and examples.
If you want to know the space available to the component then you can invoke the getWidth() and getHeight() method. Once you know these values you can determine how big you want to paint your rectangle.
Components should be added to the frame BEFORE the frame is made visible.
You don't need to use the getContentPane() method. Since JDK5 you can just add components directly to the frame and they will be added to the content pane for you.

Categories

Resources