This is my first java project and I am trying to draw a simple rectangle on my JPanel inside my JFrame. Been trying to solve this issue with the help of the same topics on stackoverflow but still no success.
The exception I get when I run the program is java.lang.NullPointerException. From my understanding I can not draw on the JPanel itself? which is created in mainWindow.
Main:
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
GameBoard game = new GameBoard();
mainWindow view = new mainWindow(game);
mainModel model = new mainModel();
mainController cont = new mainController(model, view, game);
cont.controllerInit();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
View:
public class mainWindow{
public JFrame frame;
public JPanel panel;
GameBoard game = new GameBoard();
frame = new JFrame();
frame.getContentPane().setBackground(SystemColor.control);
frame.setBounds(100, 100, 728, 435);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(game);
frame.getContentPane().setLayout(null);
panel = new JPanel();
FlowLayout flowLayout = (FlowLayout) panel.getLayout();
panel.setBounds(166, 44, 550, 349);
frame.getContentPane().add(panel);
frame.setVisible(true);
}
Game:
public class GameBoard extends JPanel{
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
g.drawRect(200, 200, 200, 200);
}
}
Never, ever call paintComponent directly, no external source has any reason to do so. Also, what do you thing would happen if you passed it null?
You should start by having a look at Performing Custom Painting and Painting in AWT and Swing to get a better understand of how paint in Swing works.
The Swing API basically uses a delegate model, where the system delegates responsibility of the paint of each component to the component. This is achieved by the system calling the components paint method, which in-turn calls (among a few others) paintComponent.
Swing also uses a passive rendering approaching, meaning that painting occurs at the discretion of the paint system. You component is notified of the need when its paint method is called. This may occur at any time.
In order for a component to be painted, it must first be added to container which is realised on the screen (has a native peer), in most cases, this means that the component hierarchy needs to resolve to some kind of window based class, like JFrame.
So, the answer to your question is:
Read the above documentation (and get a better understanding of how the API works)
Add your GameBoard to a container which can be resolved to a window based class
Never call paint or paintComponent directly
Reflection....
private mainWindow view;
private mainModel model;
public GameBoard(mainModel m, mainWindow v)
{
view = v;
model = m;
}
To me, this makes no sense. There is no reasonable reason why GameBoard needs a reference to mainWindow. GameBoard is, in of itself, a "view". If anything, the only thing you "should" be passing to GameBoard (assuming you're trying to use a MVC) is a controller
Related
I'm trying to draw a line in a JFrame, but line isn't drawn.
I tried to use the method setOpaque(true) for contentPane, lblNewLabel and l but nothing changed. I also tried call repaint(); outside this class but the situation is still the same. Here's the code:
public class DrawingClass extends JFrame
{
private JPanel contentPane;
public DrawingClass(int n, int s, int p) {
Line l= new Line();
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setBounds(700, 300, 480, 640);
contentPane = new JPanel();
contentPane.setOpaque(true);
setResizable(false);
setContentPane(contentPane);
contentPane.setLayout(null);
JLabel lblNewLabel = new JLabel("");
lblNewLabel.setIcon(new ImageIcon("image.png"));
lblNewLabel.setBounds(0, 0, 480, 640);
contentPane.add(lblNewLabel);
l.setBounds(0,0,480,640);
contentPane.add(l);
repaint();
}
class Line extends JPanel
{
public void paintComponent(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(10, 10, 15, 12);
}
}
}
I expect a little line on the top left of the JFrame, above the background wallpaper, but nothing happen. It shows only the wallpaper.
There are several errors in your code:
You're extending JFrame but you're not changing its behavior, so why are you doing that? JFrame is a rigid component, so it's never a good idea to extend from it, build your GUI based on JPanels instead. See: Extends JFrame vs. creating it inside the program
Don't explicitly set the size of the JFrame, call pack() on it and instead override getPreferredSize from the JPanel, see: Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
You don't need to call setOpaque(...) in this case.
Don't use a null-layout, it might lead to strange errors, because null Layout is Evil and frowned upon
We don't have access to your image so we cannot test the ImageIcon and it's also not related to your question. However you should load your images as resources
Don't explicitly set the bounds of each element, this is related to point (4) and you should use a Layout Manager or combinations of them to get your desired GUI.
Don't call repaint() that way, it has no effect, it is supposed to repaint your UI when there's a change in it. However there is no change at the start of your program.
You're breaking the paint-chain by not calling super.paintComponent(...) inside your paintComponent(...) method. Check the Tutorial on Custom Painting in Swing so that you learn how to do it properly
And be careful, as paintComponents(...) (With a trailing s) is different from paintComponent(...) (Look at your title)
So, after doing all of the above changes, we get to this simple program:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class DrawingClass {
private JPanel contentPane;
private JFrame frame;
public static void main(String args[]) {
SwingUtilities.invokeLater(() -> new DrawingClass().createAndShowGUI());
}
public void createAndShowGUI() {
frame = new JFrame(getClass().getSimpleName());
Line line = new Line();
frame.add(line);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
class Line extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(10, 10, 15, 12);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(480, 640);
}
}
}
Which produces the following output:
I'm trying to understand what actually paints components in Swing. I read this article about painting in AWT and Swing and now tried to write the following simple program:
//A simple wrapper to understan how paint() works
public class MyButton extends JButton{
/**
* Default serialVersionUID
*/
private static final long serialVersionUID = 1L;
private final JButton jButton;
public MyButton(JButton jButton) {
this.jButton = jButton;
}
#Override
public void paint(Graphics g){
jButton.paint(g);
}
}
But when I try to add MyButton to frame
JFrame frame = new JFrame("Hello swing");
JPanel panel = new JPanel();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.add(new MyButton(button));
frame.add(panel);
it renders nothing
But after deleting
#Override
public void paint(Graphics g){
jButton.paint(g);
}
it renders the empty button:
QUESTION: Why does it behave that way? Why does the delegating cause rendering to fail?
First of all when you post a question you should post a proper SSCCE that demonstrates the problem. We can't copy/compile random lines of code. Until a problem is solved, you don't know what part of the code is causing the problem.
Why does the delegating cause rendering to fail?
My guess would be that the size of the button is (0, 0) so there is nothing to paint.
When you get rid of the custom paint method, then the real button can be painted because it does have a size because the layout manager has done its job.
public class Demo extends JFrame{
public static void main(String[] args)
{
JPanel panel = new JPanel();
getContentPane().setLayout(new BorderLayout());
panel.add(new JButton("Test"));
this.getContentPane().add(panel, BorderLayout.CENTER);
this.setSize(200,200);
this.setVisible(true);
}
}
If you want to add UI Components do it like that, don't use paint in any way.
If you want to paint for example a rectangle follow this tutorial: https://docs.oracle.com/javase/tutorial/uiswing/painting/
Your paint method does not draw the MyButton object, but instead draws the JButton which is member of your class. The problem now is, that this Button has not been added to the panel and so it's drawn on nothing. By removing your paint method, super.paint(g) is called because your class has no paint method and so your button, but not the member JButton is drawn.
I hope you understand what I am trying to explain to you.
I have a class with two JFrames and am trying to draw a line on a particular frame .
I tried the code below but it only appears in the first frame which is the success frame.
It also appears above all the other components of the success frame thus making all other
components invisible. It does not appear in the comp Frame.
How do I correct this.
Here is the code I have so far :
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
public class lineGUI{
public static void main(String []args){
Success s=new Success();
s.setVisible(true);
}
}
class Success extends JFrame{
JPanel alas =new JPanel();
JFrame comp =new JFrame();
public Success(){
JPanel panel=new JPanel();
getContentPane().add(panel);
setSize(450,450);
JButton button =new JButton("press");
panel.add(button);
comp.setSize(650,500);
comp.setTitle("View Report");
JRootPane compPane=comp.getRootPane();
Container contePane=compPane.getContentPane();
contePane.add(alas);
ActionListener action =new ActionListener(){
public void actionPerformed(ActionEvent e){
if (e.getSource()==button){
comp.setVisible(true);
}
}
};
button.addActionListener(action);
JButton button2=new JButton("access");
alas.add(button2);
}
public void paint(Graphics g) {
comp.paint(g);
Graphics2D g2 = (Graphics2D) g;
Line2D lin = new Line2D.Float(100, 100, 250, 260);
g2.draw(lin);
}
}
You've got some crazy code up there. Suggestions:
Don't draw directly in a JFrame, but in a the paintComponent method of an object derived from JComponent such as JPanel or JComponent itself.
Your drawing directly in another component's paint(...) method is not kosher at all. Why not simply share the data between classes, and use the data (the ints) to draw where desired.
You would rarely want to have a GUI display more than one JFrame at a time. Usually one window is the main window (the JFrame), and it often owns any other windows which would be dialog windows such as JDialogs.
Read the graphics tutorials to learn the correct way to do Swing Graphics
Two things:
If you want to draw in the "comp" frame, then you should extend that frame explicitly to overload its paint method. Right now you're overloading the paint method of "Success" frame. The line comp.paint(g) is using the paint method of comp (a standard JFrame) to draw on the Graphics object of the "Success" frame. You probably want to make that into super.paint(g) instead, then put this paint function into it's own JFrame and create comp from that.
http://pastebin.com/ZLYBHpmj
(Sorry, first post, couldn't figure out how to get Stackoverflow to quit complaining about format)
I want to paint the contents of a JFrame onto another frame. Currently, I only get it to work if the JFrame is visible.Is there a way to paint a hidden JFrame?
Additional info:In my project I need to be able to rotate and scale windows. I do not want to write my own window-api, so I thought I might be able to just paint JFrames or similar container classes in a rotated way (which the Graphics2D-API supports perfectly well). It would be awesome to be able to use standard JFrames for that, but a custom frame extending a JFrame would also be OK..
public class JFTest extends JFrame {
private JFrame frameToPaint = null;
public static void main (String[] args) {
new JFTest ();
}
public JFTest () {
// some basic initialization
super ("Container");
setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
setExtendedState (JFrame.MAXIMIZED_BOTH);
add (new JPanel () {
#Override public void paintComponent (Graphics g) {
super.paintComponent (g);
// painting the child frame's contents onto this frame
if (frameToPaint != null) frameToPaint.getRootPane().paintAll (g);
}
});
setVisible (true);
// initializing some test-frame that will get painted onto the container
frameToPaint = new JFrame ("child");
frameToPaint.setSize (200, 100);
frameToPaint.add (new JLabel ("test"));
frameToPaint.addComponentListener (new ComponentAdapter() {
#Override public void componentResized (ComponentEvent e) { repaint (); }
#Override public void componentHidden (ComponentEvent e) { repaint (); }
});
// magic line. an invisible frame will not get painted! why??
frameToPaint.setVisible (true);
}
}
Hint 1: JFrame's setDefaultLookAndFeelDecorated(false)/setUndecorated(true) might be of use for a window without caption and borders;
Hint 2: as setGlassPane/setLayeredPane/setOpaque(false) might be of use for a second "layer".
I want to get the graphical contents of a frame without having to make the frame visible for the user
The Screen Image class should help. Although I think it will only work for the "content pane" of the frame and not the entire frame (with the title bar and borders) unless you use a decorated frame.
1) you have to use proper LayoutManager, not setSize() or setBounds()
2) if is there null LayoutManager used then Container returns any size after setVisible(true);
3) if is there used proper LayoutManager, then Container return its Size after call pack();, in other hands this container couldn't be visible on the screen ( meaning setVisible(true); )
4) JComponents must to returns PrefferedSize for example
The problem is, I am unable to make it a dimension with 800x600. In other words, when I run the program, the frame is so small that I can not do anything with it.
How can I make the frame larger?
I have set the preferred size already ans set the canvas bounds.
Then what is the problem?
public class GameCanvas extends Canvas
{
private BufferStrategy buffer = null;
public GameCanvas()
{
setBounds(0, 0, 800, 600);
setIgnoreRepaint(true);
addKeyListener(new KeyInputHandler());
requestFocus();
}
public void addNotify()
{
super.addNotify();
this.createBufferStrategy(2);
buffer = this.getBufferStrategy();
setBounds(0, 0, 800, 600);
}
}
public class GameGuiFrame extends JFrame
{
private JPanel panel = new JPanel();
private GameCanvas canvas = new GameCanvas();
public GameGuiFrame()
{
this.setName("My Game");
this.pack();
this.setResizable(false);
this.setVisible(true);
panel = (JPanel) this.getContentPane();
panel.setPreferredSize(new Dimension(750,500));
panel.setLayout(null);
panel.add(canvas);
}
}
public class GameManager
{
public static void runGameLoop()
{
GameGuiFrame container = new GameGuiFrame();
container.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
}
public class Main
{
public static void main(String [] args)
{
GameManager.runGameLoop();
}
}
Try packing the Frame after you set the preferred size of the content pane.
Not related to your question but based on the code you posted it looks like you've copied some old AWT code and are trying to use it in a Swing application.
I would suggest you only use Swing components. There is no need to use a Canvas with a BufferStrategy. Just use a JPanel it is double buffered by default. The code snippet you copied is old and that is not the way it is done in Swing.
Don't use a null layout. Swing was designed to be using with layout managers. Then the pack() method will be able to do its job properly.
There is no need to use a WindowListener to close the frame. These days people just use:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Also, the frame should be made visible AFTER components have been added to the frame.
Generally you should be use Key Bindings, not a KeyListener to listen for key events in a Swing application.
I suggest you look at the Swing tutorial for more information about the above concepts.
You call to pack() will set the frame (and components within it) to their preferred size. However, you haven't specified a preferred size. I would suggest removing your two calls to setBounds() and calling setBounds() within the main method instead of pack().