I want do draw a rectangle in my JFrame window, but I'am always getting a nullpointer error..
Why is it happening? what is the best (correct) way to draw graphics like rectangles, gradients, etc or something like falling snow in swing?
This is the Exception:
Exception in thread "Thread-0" java.lang.NullPointerException
at gui.Window.run(Window.java:24)
at gui.Window$1.run(Window.java:34)
at java.lang.Thread.run(Unknown Source)
And source:
public class Window extends JFrame implements Runnable {
private boolean run = true;
public Window() {
super.setSize(500, 500);
super.setTitle("MY GUI");
super.setDefaultCloseOperation(EXIT_ON_CLOSE);
super.setContentPane(new Container());
}
#Override
public void run() {
Graphics g = super.getContentPane().getGraphics();
while (this.run) {
g.setColor(new Color(0, 0, 0, 255));
g.fillRect(0, 0, 200, 200);
}
}
public static void main(String[] args) {
new Thread(new Runnable() {
#Override
public void run() {
Window window = new Window();
window.run();
}
}).start();
}
}
Error line 24: g.setColor(new Color(0, 0, 0, 255));
Why is it doing that?
The code you posted makes no sense.
First of all, every interaction with Swing components (except calls to repaint()) must be done in the event dispatch thread.
Second, there is no point in running an infinite loop that would constantly paint the same thing on a Graphics.
Third, that's not how it works. You can't get the Graphics associated to a component and paint on it. Instead, you must override the paintComponent(Graphics) method of a Swing component, wait for swing to call this method, and use the provided Graphics argument to paint whatever you want. If you want to change what is being painted, then you need to call repaint() on this element. Don't do that with JFrame. Create a subclass of JComponent or JPanel, and add an instance of the subclass to a JFrame, and then make this JFrame visible:
public class CustomComponent extends JComponent {
#Override
public void paintComponent(Graphics g) {
// paint here
}
#Override
public Dimension getPreferredSize() {
// return preferred size here
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.add(new CustomComponent());
f.pack();
f.setVisible(true);
}
});
}
}
getGraphics will return null if the component is not visible.
To make your Window visible you have to call setVisible(bool).
You also have to be careful using threads with Swing.
Related
I am attempting to make a simple traffic light frame. This is being made by the main frame TrafficBox:
public class TrafficBox extends JFrame {
public TrafficBox() {
setLayout(new BorderLayout(100,100));
add(new TrafficLight(Color.GREEN), BorderLayout.NORTH);
add(new TrafficLight(Color.ORANGE), BorderLayout.CENTER);
add(new TrafficLight(Color.RED), BorderLayout.SOUTH);
setBounds(100, 100, 800, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TrafficBox();
}
});
}
which then as seen, adds in 3 TrafficLight components which are based on JPanel, and have the following code:
public class TrafficLight extends JPanel {
private final int BALL_DIAMETER = 100;
private Color color;
public TrafficLight(Color color) {
//setSize(BALL_DIAMETER, BALL_DIAMETER);
this.color = color;
new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
repaint();
}
}).start();
}
public void paintComponent(Graphics g) {
g.setColor(color);
g.fillOval(0, 0, BALL_DIAMETER, BALL_DIAMETER);
}
This does sort of what I want, it draws all 3 circles as expected although a majority of the North(green) and south(red) lights are being cut off. I assume this is because the north/south spot are much smaller than the center.
I've tried using setSize(); to set the size of the panels to the size of the circles, however that does not work. Is there a way I can make it so that the full circle will be visible?
So, most layout managers will need some "hints" to be provided by the components in order to know how they want to be laid out.
You will need to override the getPreferredSize and return a size which best meets your needs, for example...
public class TrafficLight extends JPanel {
private final int BALL_DIAMETER = 100;
//...
public Dimension getPreferredSize() {
return new Dimension(BALL_DIAMETER, BALL_DIAMETER);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent();
g.setColor(color);
g.fillOval(0, 0, BALL_DIAMETER, BALL_DIAMETER);
}
Also, paintComponent doesn't ever need to be public no-one else should be calling it and you should call super.paintComponent() before performing any custom painting.
I'd also recommend maybe using GridLayout for this
I'd also argue that TrafficLight doesn't need a Timer of it's own and the should be controlled externally, but that's me
setBounds(100, 100, 800, 600);
Is best avoided, use pack() to size the window to the preferred size of the content and setLocation to position it
Simple fix, needed to be using setPreferredSize() rather than setSize.
Hi I have made an actionlistener and I want to call a paintComponent method when you click the button?
I have googled it but with no luck.
Here is the actionlisetener,
graf.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
graf();
}
And here is the method,
public static void paintComponent (Graphics g) {
int width = Integer.parseInt(xinwindow.getText());
int hight = Integer.parseInt(yinwindow.getText());
g.setColor(Color.black);
g.drawLine((width/2)- 1, 0, (width/2) +1 , hight);
}
How to call it?
Any help would be appreciated.
Override the paintComponent method of a JComponent object you want to paint.
JComponent c = new JComponent() {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = Integer.parseInt(xinwindow.getText());
int hight = Integer.parseInt(yinwindow.getText());
g.setColor(Color.black);
g.drawLine((width/2)- 1, 0, (width/2) +1 , hight);
}
}
And add
c.revalidate();
c.repaint();
after handling the click in actionPerformed.
You should start with some examples to construct a GUI.
static is for a global, a once occuring instance; one per class. Try to remove all, just the program's entry point:
public static void main(String args) {
JFrame appWindow = new MyFrame();
SwingUtilities.invokeLater(() -> appWindow->setVisible(true));
}
public class MyFrame extends JFrame {
private MyPanel panel;
public MyFrame() {
panel = new MyPanel();
add(panel);
panel.addClickListener(evt -> panel.repaint(50L));
}
}
public class MyPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.RED);
g2.drawRectangle(40, 40, getWidth() - 80, getHeight() - 80);
}
}
The mechanism is as follows:
A click is handled on the event dispatching thread of swing.
Here it says to repaint the panel in 50 ms.
A bit later repaintComponent is called from the swing framework in the conjunction of erasing the background and painting the child components.
In paintComponent the Graphics parameter for all newer java versions actually is a Graphics2D which has many nice methods.
I'm following along in a (dated) Java book, and this project is supposed to "animate" a circle moving across the screen. However, when the program is run, the circle remains in one spot. My code looks identical to the book's. Am I forgetting something? Am I calling repaint() at the wrong time?
public class Animation
{
JFrame f;
int x, y;
public static void main(String [] args)
{
Animation a = new Animation();
a.go();
}
public void go()
{
f=new JFrame();
myPanel p=new myPanel();
f.getContentPane().add(p);
f.setSize(300, 300);
f.setVisible(true);
for(int i=0; i<=50; i++)
{
p.repaint();
x++;
y++;
}
}
class myPanel extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(x, y, 40, 40);
}
}
}
So, immediately, two things jump out at me.
It's possible that the loop which is updating the values is NOT running on the Event Dispatching Thread, which could be leading to dirty read/writes
repaint can consolidate requests to reduce the amount of work placed on the Event Queue/Event Dispatching Thread. Since there is not "artificial" delay in the loop, it's possible that all your requests are been reduced to a single update pass by the RepaintManager
The first thing I would do is isolate the responsibility for the management of the position of the oval, because in your current code, it could be updated from anywhere, which is just a mess
class MyPanel extends JPanel {
private Point posy = new Point(0, 0);
public Point getPosy() {
return posy;
}
public void move() {
Point posy = getPosy();
posy.x++;
posy.y++;
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(posy.x, posy.y, 40, 40);
}
}
Next, I'd ensure that the context of the UI is been modified from within the EDT...
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Animation a = new Animation();
a.go();
}
});
}
and finally, I'd make use of Swing Timer to act as a pseudo loop for the animation...
public void go() {
f = new JFrame();
MyPanel p = new MyPanel();
f.getContentPane().add(p);
f.setSize(300, 300);
f.setVisible(true);
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
p.move();
}
});
timer.start();
}
There are a number of reasons for using a Swing Timer in this context. First, the "ticks" are executed ON the EDT, making it safe to update the UI from within and it won't block the UI while it's "waiting" between ticks
I would recommend having a look at:
Painting in AWT and Swing which will give you a better understanding into how painting actually works
Concurrency in Swing as it will give you a heads up into some of the pitfalls in trying to do more then one thing within the GUI
How to Use Swing Timers because they are really useful for this kind of thing
I'd like to draw something in JFrame, so i decided to use below code but although it works truly but i don't want to have any ActionListener.
public class Draw
{
public static void main(String[] args)
{
final JPanel jPanel = new JPanel();
JFrame jFrame = new JFrame("Drawing Window");
JButton jButton = new JButton("draw");
jPanel.add(jButton);
jFrame.add(jPanel);
jFrame.setBounds(0, 0, 500, 500);
// first place
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// second place
jButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
Graphics graphics = jPanel.getGraphics();
graphics.drawLine(0,0,100,100);
graphics.drawOval(0,0,100,100);
}
});
}
}
as you can see i added the below code inside of ActionPerfomed method:
Graphics graphics = jPanel.getGraphics();
graphics.drawLine(0,0,100,100);
graphics.drawOval(0,0,100,100);
now i want to put it in the first place ( the comment place in the code ) but i will get an error, and if i put it in the second place, i will get no error but it does not draw any things.
It seems that it is necessary to put the drawing methods inside actionPerformed, my question is why? and is there any other way?
graphics.drawLine(0,0,100,100);
graphics.drawOval(0,0,100,100);
These statements need to be moved to an overridden paintComponent(Graphics) method of the jPanel.
The getPreferredSize() method of the panel should also be overridden to return a dimension of 500x500, then, instead of:
jFrame.setBounds(0, 0, 500, 500);
Simply call:
jFrame.pack();
One other option for you is to make the method(paintComponent or paint):
public void paintComponent(Graphics graphics){
graphics.drawLine(0,0,100,100);
graphics.drawOval(0,0,100,100);
}
and call the repaint from actionListener as shown..
public void actionPerformed(ActionEvent action){
repaint();
}
If it is usefull for you you can transform it as you want....
I am new to Java....I studied that we can add two things on frame... I added button and in response by clicking on button I want rectangle as output....but i don't understand that..Why i am not getting output.....
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class customizedgui5 implements ActionListener {
JButton button;
JFrame frame;
public static void main(String[] args) {
customizedgui5 hi = new customizedgui5();
hi.go();
}
public void go() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("click me");
button.addActionListener(this);
myclass a = new myclass();
frame.getContentPane().add(button, BorderLayout.CENTER);
frame.getContentPane().add(a, BorderLayout.SOUTH);
frame.setSize(100, 100);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
frame.repaint();
frame.revalidate();
}
}
class myclass extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.orange);
g.fillRect(20, 50, 100, 100);
}
}
I would start by taking a look at Performing Custom Painting.
The main problem in your code is you are getting NullPointerException when you click the button because the reference of frame is null.
It is null because you've shadowed it in the constructor (basically, declared another variable of the same name within the constructor)...
// I'm null..
JFrame frame;
public void go() {
// Not the same as frame above...
JFrame frame = new JFrame();
You are also going to not see any changes because of a number of reasons...
The myclass panel has no size. With BorderLayout, this won't be "too" much of a problem, but...
You've drawing outside of the visible range of the panel. The rectangle you are painting won't appear because it is being painted outside of the width and height of the panel.
The rectangle will appear before you press the button as paintComponent will be called to update the state of the panel once it's made visible on the screen...
The first thing you need to is provide some kind of size hints to the BorderLayout. Try adding...
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 150);
}
To myclass.
You also don't need to repaint the frame, what you really want to repaint is the instance of myclass. Try updating customizedgui5 so that a becomes a instance variable (like frame...
//...
myclass a;
//...
public void go() {
//...
a = new myclass();
//...
}
public void actionPerformed(ActionEvent event) {
a.repaint();
}
Now, the rectangle will still be shown the moment that the panel is made visible on the screen. Sure you could try setting it invisible, but this will affect the layout of the frame, hiding your component to start with, so, instead, we need some kind of flag we can trip so we know when to paint the rectangle. This is easily achieved by using a simple boolean variable, for example...
class myclass extends JPanel {
private boolean paintRect;
public void setPaintRect(boolean paint) {
paintRect = paint;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 150);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (paintRect) {
g.setColor(Color.orange);
g.fillRect(20, 50, 100, 100);
}
}
}
Then in you actionPerformed method, you just need to set the flag...
public void actionPerformed(ActionEvent event) {
a.setPaintRect(true);
}
You may also want to take a read through Code Conventions for the Java Programming Language. It will make it easier for people to read your code...
When you click on your button, you're calling the method actionPerformed(ActionEvent event)
Take a look at what you did there. Currently, you repaint and re-validate the frame. If you want to add a rectangle to your frame, you need to do so by adding a new component to the frame that will draw the rectangle.
You could add another instance of your myclass JPanel which paints a rectangle like so:
public void actionPerformed(ActionEvent event) {
frame.getContentPane().add(new myclass(), BorderLayout.NORTH);
frame.repaint();
}
This would add your custom rectangle-drawing panel to the North section of your BorderLayout. If you want to add the rectangle "on top of" your button, you should embed your button within a JPanel, then add the rectangle-drawing panel to that instead of your main JFrame