Graphics Not Drawn/Appearing - java

I'm having this problem where an object that I've drawn isn't appearing in the GUI. I know it's being processed because data is being pushed to a log file. However, the graphic isn't appearing.
Here's some of my code:
public static void main(String[] args)
{
JFrame window = new JFrame();
window.setLayout(new BorderLayout());
window.setVisible(true);
}
There's a button and a few other widgets that I've placed here and there. The center pane (BorderLayout.CENTER) is where my DrawnObject is to be displayed.
// Called when button is pushed/clicked
public static void trigger()
{
DrawnObject shape = new DrawnObject();
window.setLayout(new BorderLayout());
window.getContentPane().add(shape, BorderLayout.CENTER);
window.pack;
}
public class DrawnObject extends JComponent()
{
#Override
public Dimension getMinimumSize()
{
return new Dimension(100, 100);
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(500, 500);
}
#Override
public Dimension getMaximumSize()
{
return new Dimension(700, 700);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(10, 10, 10, 10);
}
}
I've tried casting the Graphics object as Graphics2D and using the appropriate draw methods, but that hasn't helped.

Try changing the color...
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(10, 10, 10, 10);
The graphics context color is set to the components background color by default
public class PaintTest01 {
public static void main(String[] args) {
new PaintTest01();
}
public PaintTest01() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new DrawPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawPane extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(30, 30);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(10, 10, 10, 10);
}
}
}
UPDATED
From your updated code in your question, it can't compile...
You create a JFrame named window in the constructor, which is a local variable...
public static void main(String[] args)
{
JFrame window = new JFrame();
window.setLayout(new BorderLayout());
window.setVisible(true);
}
Then you try and add the DrawObject to the window...
public static void trigger()
{
DrawnObject shape = new DrawnObject();
window.setLayout(new BorderLayout());
window.getContentPane().add(shape, BorderLayout.CENTER);
window.pack;
}
But because window is undefined, you example can't compile.
The only way that this would compile is if you had a static variable at the class level called window, which in that case, it should be producing a NullPointerException, unless you've initialised that variable
public class MyDrawing {
public static JFrame window = new JFrame();
This would mean you have two frames, one you created in the constructor and one your create as a static level class field. This won't work, because they are different instances

have to return PreferredSize from public class DrawnObject extends JComponent(), otherwise returns Dimension(0, 0);
Top-Level containers have got implemented BorderLayout, then window.add(shape, BorderLayout.CENTER); is proper code line and JComponent should be layed correctly
use pack() instead of invalidate(), this code line doesn't works, to invoke something for container layed by BorderLayout or GridLayout (e.i. ???), nor for container based on JComponent,JComponent havent implemented any LayoutManager in API, have to return PreferredSize
for better help sooner post an SSCCE

Try adding your DrawnObject to the windows content pane, and also don't forget to set a layout. Using a null layout is bad practice (your layout is set to null if you call on invalidate).
window.getContentPane().setLayout(new BorderLayout());
window.getContentPane().add(shape, BorderLayout.CENTER);
window.pack();
window.setVisible(true);
Also, try to skip the invalidate().

Related

drawing on new thread (canvas is good but not working on jpanel correctly)

I was drawing my game on Canvas, all was god, but I changed it into a JPanel, but now its not working correctly, here are the codes, you can just copy them and you'll see where is the problem (I have a menu and after clicking on the button it should create new thread and there i want to draw, the problem in JPanel is that the button is able to see, its blinking and i can press it, in canvas it was fine, there wasn't any button). I solved it, that after clicking on the button I setted him unvisible (button.setVisible(false)), but these codes are just examples and in my game I have more buttons, so its not practical because I need them to see after the game ends. I think I just forgot an important method in JPanel, thx for help, codes:
//Main class representing menu
public class Sandbox extends JFrame{
Panel p = new Panel();
public static void main(String[] args) {
new Sandbox();
}
public Sandbox() {
setLayout(null);
setPreferredSize(new Dimension(200, 200));
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
final JButton but = new JButton("Button");
but.setBounds(0, 0, 50, 50);
but.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
p.start();
}
});
add(p);
add(but);
pack();
setVisible(true);
}
}
//Drawing on Canvas -> working well
public class Panel extends Canvas implements Runnable {
Thread t;
public Panel() {
setSize(new Dimension(200, 200));
setVisible(false);
}
public void start() {
t = new Thread(this);
t.start();
setVisible(true);
}
public void draw() {
BufferStrategy b = getBufferStrategy();
if(b == null) {
createBufferStrategy(3);
return;
}
Graphics g = b.getDrawGraphics();
g.setColor(Color.red);
g.fillRect(0, 0, 200, 200);
g.dispose();
b.show();
}
#Override
public void run() {
while(!t.isInterrupted()) {
try {
draw();
t.sleep(200);
} catch (InterruptedException ex) {}
}
}
}
//Drawing on JPanel -> here i can press the button after first click on it
public class Panel extends JPanel implements Runnable {
Thread t;
public Panel() {
setSize(new Dimension(200, 200));
setVisible(false);
}
public void start() {
t = new Thread(this);
t.start();
setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(0, 0, 200, 200);
}
#Override
public void run() {
while(!t.isInterrupted()) {
try {
repaint();
t.sleep(200);
} catch (InterruptedException ex) {}
}
}
}
Not sure I understand exactly what you are trying to do but you do have a couple of problems:
the setVisible(true) method should be invoked AFTER you have added all the components to the frame and packed the frame.
by default the content pane of a JFrame uses a BorderLayout. You code is adding two components to the CENTER of the BorderLayout, but a BorderLayout only allows you to add one component to the CENTER, so only the last component added will be displayed.

Visibility of JFrame by adding JPanel in Netbean

I have two classes: one is JFrame and second is JPanel. When i add JPanel object in JFrame class it makes not error but did not show JPanel result. It only shows blank JFrame.
This is my JPanel class:
public class grafix extends JPanel {
#Override
public void paintComponent(Graphics g){
super.paintComponents(g);
Graphics2D g2= (Graphics2D) g;
Rectangle r = new Rectangle(15,10,200,300);
g2.draw(r);
g2.setColor(Color.blue);
g2.fillOval(50, 50, 30,30);
g2.drawString("Hello World", 120, 50);
}
}
And this is my JFrame Class:
public class JFrame extends javax.swing.JFrame {
public JFrame() {
initComponents();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
grafix gf = new grafix(); // object created of JPanel class
frame.getContentPane().add(gf);// by adding object
frame.pack();
frame.setVisible(true);
}
});
}
}
No error being mentioned in Netbean but it did not show any drawing but frame only.
The graphics are shown but your frame is too small to show the panel graphics. Your Grafix panel component is using the default preferred size of 0x0 to the frame reveals nothing. Override getPreferredSize in the class to allow the correct size to be set when the frame is packed
#Override
public Dimension getPreferredSize() {
return new Dimension(250, 400);
}

Add JPanel to contentPane within actionListener

I'm trying to add a JPanel to my JFrame within an actionListener method, but it appears only after the second click on the button. This is a portion of my code where panCours is a JPanel and ConstituerData the targeted JFrame :
addCours.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
panCours.setBounds(215, 2, 480, 400);
panCours.setBorder(BorderFactory.createTitledBorder("Saisir les données concernant le cours"));
ConstituerData.this.getContentPane().add(panCours);
}
});
I don't understand why it doesn't appear as soon as I click on the button. Any explanation and help about how to fix this ?
You'll need to add a call to repaint(); (as well as probably revalidate();) to get the JPanel to show immediately. A basic example demonstrating your problem (and the solution) below;
public class Test {
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
JButton button = new JButton("Test");
button.setBounds(20, 30, 100, 40);
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
JPanel panel = new JPanel();
panel.setBackground(Color.red);
panel.setBounds(215, 2, 480, 480);
frame.add(panel);
frame.revalidate(); // Repaint here!! Removing these calls
frame.repaint(); // demonstrates the problem you are having.
}
});
frame.add(button);
frame.setSize(695, 482);
frame.setVisible(true);
}
}
The above said, (as suggested by others) it's only right that I recommend against the use of a null layout in future. The swing layouts are a little awkward to begin with, but they will help you a great deal in the long run.
the answer can be found in the following snippet:
you need to revalidate() the contentPane, not repaint the frame. you can add any panel you want to the contentpane like this. if you declare contentPane as a private field you dont need your getContentPane() call. contentPane is global so it can be reffered to directly from anywhere within the class.
be careful about NullPointerExeptions which can be thrown if you refer to it before initialising.
public class testframe extends JFrame {
private JPanel contentPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
testframe frame = new testframe();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public testframe() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = new JPanel();
setContentPane(contentPane);
JButton btnNewButton = new JButton("New button");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JPanel a = new JPanel();
contentPane.add(a);
contentPane.revalidate();
}
});
contentPane.add(btnNewButton);
}
}

How to change the dimension of a component in a JFrame

Suppose I have a JPanel in a JFrame. When I invoke a method that changes the preferred size of that JPanel, it does not change.
The code looks something like this:
public class SomePanel extends JPanel{
public SomePanel(){
setPreferredSize( new Dimension( 390, 40 ) );
setBackground( Color.BLACK );
}
public void expand(){
setPreferredSize( new Dimension( 390, 200 ) );
}
public static void main( String args[] ){
JFrame frame = new JFrame();
frame.setSize( 450, 500 );
frame.setLayout( new FlowLayout() );
SomePanel somePanel = new SomePanel();
frame.add( somePanel );
frame.setVisible( true );
somePanel.expand();
}
}
Is there something that I have to do first? I have tried so check the size of the JPanel when expand() is invoked. The height of the JPanel before and after setting the preferred size remains at 40.
I have also tried to use a Dimension variable, and that did not work either.
Dimension dimension;
public SomePanel(){
dimension = new Dimension( 390, 40 );
...
}
public expand(){
dimension.setSize( 390, 200 );
setPreferredSize( dimension );
}
Add frame.pack(); after somePanel.expand(); in your main() method. It will be done.
You need to invalidate the container hierarchy to make it re-layout the components.
Simply call invalidate followed by revalidate on the component you have changed.
Here's a small example...
public class TestComponentHierarcy {
public static void main(String[] args) {
new TestComponentHierarcy();
}
public TestComponentHierarcy() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(new Test());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Test extends JPanel {
private Dimension size = new Dimension(10, 10);
public Test() {
setLayout(new GridBagLayout());
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
size.width += 10;
size.height += 10;
invalidate();
revalidate();
}
});
}
#Override
public Dimension getPreferredSize() {
return size;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}
}
}
+1 to all.
I usually use a combination of:
revalidate() and pack(). (see #GagandeepBali and #StanislavL answer here for more reasoning as to my choice of revalidate()) as for pack() this allows for my JFrame to be sized to fit the contents.
do not call setPreferredSize rather override getPreferredSize of JPanel.
also do not call setSize(..) on JFrame use correct LayoutManager which adjusts to all added components size and than simply call pack() before setting JFrame visible.
And lastly but not stressed enough warp creation and manipulation of Swing components in SwingUtilities.invokeXXX block / Event Dispatch Thread
Here is an example I made:
Basically a JPanel which overrides getPreferredSize and has a method setPanelSize(int w,int h) which changes variables in JPanel instance to return new Dimensions for getPreferredSize. after that I call revalidate() and pack() on JFrame to refelect changes:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public Test() {
initComponents();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
private void initComponents() {
final JFrame frame = new JFrame();
frame.setTitle("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final MyPanel myPanel = new MyPanel();
final JButton changeSizeButton = new JButton("Change size to 300x300");
changeSizeButton.addActionListener(new ActionListener() {
boolean resized = false;
#Override
public void actionPerformed(ActionEvent ae) {
if (resized) {
myPanel.setPanelSize(200, 200);
resized = false;
changeSizeButton.setText("Change size to 300x300");
} else {
myPanel.setPanelSize(300, 300);
resized = true;
changeSizeButton.setText("Change size to 200x200");
}
frame.revalidate();
frame.pack();
}
});
frame.add(myPanel);
frame.add(changeSizeButton, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
}
class MyPanel extends JPanel {
private int width, height;
public MyPanel() {
super(true);
width = 200;
height = 200;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public void setPanelSize(int width, int height) {
this.width = width;
this.height = height;
}
}

Why does setting the layout to BorderLayout mean the paintComponent is never called

In the following example program, if you set useBorderlayout to true, the paintComponent method is never called - why?!
import javax.swing.*;
import java.awt.*;
public class PaintComponentTest extends JPanel {
private final boolean useBorderLayout;
public PaintComponentTest(boolean useBorderLayout){
this.useBorderLayout = useBorderLayout;
initialiseComponents();
}
public void initialiseComponents(){
setOpaque(true);
setBackground(Color.RED);
if(useBorderLayout){
//this appears to be the offending line:
setLayout(new BorderLayout());
}
final JPanel panel = new JPanel();
panel.setOpaque(true);
panel.setBackground(Color.GREEN);
add(panel, BorderLayout.CENTER);
}
#Override
public void paintComponent(Graphics g){
System.out.println("PaintComponentTest.paintComponent");
super.paintComponent(g);
}
public static void main(String [] args){
final boolean useBorderLayout = (args.length == 1 && Boolean.parseBoolean(args[0]));
System.out.println("Running with"+(useBorderLayout?"":"out")+" BorderLayout as layout manager...");
SwingUtilities.invokeLater(new Runnable(){
public void run(){
final JFrame frame = new JFrame("BorderLayout/PaintComponent test");
frame.setPreferredSize(new Dimension(200, 200));
frame.getContentPane().setLayout(new BorderLayout());
final PaintComponentTest componentTest = new PaintComponentTest(useBorderLayout);
frame.getContentPane().add(componentTest);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}
}
Because it doesn't need to. The PaintComponentTest class is a JPanel that has one green JPanel as content. When the BorderLayout is set, the green panel takes up all the space in panel and the PaintComponent method is not needed.
Add this method to your code and you should see it happen:
#Override
public void paintChildren(Graphics g){
System.out.println("PaintComponentTest.paintChildren");
super.paintChildren(g);
}
Because the nested panel covers all the component. Damaged region (to be repainted) is past to the children because the child bounds cover all the damaged region.

Categories

Resources