Hello, very good evening! I have a certain problem in java that is already disconcerting me a lot, since I did not think that something that I would think would be "relatively simple", would result in the fact that I have spent 1 day and a half thinking about why the program did not works fine... (Note that I know next to nothing about Java), I'm a complete newbie.
The programming language I'm familiar with is javascript (Pretty familiar actually), but looking now at Java and the use of the awt graphical libraries, I'm already a bit lost... so lost that I have no idea if I understand it. that I am doing is properly organized and structured for what I am doing.
To quickly put in context what I have to do (It's a university task), I have to create a "paint", but simple, which allows me to draw freehand strokes, (As if it were the paint pencil tool), the program must allow you to change the "drawing tool", and must allow you to make primitive shapes, for example a rectangle, a circle and also allow you to change the color of the stroke and fill.
Of course, this adding tools select them change the path and fill color and make different shapes once I get the Graphics2D working, I'll implement them after I get the Graphics2D working...
Here is the code of the different classes that I have already created from the program:
main class (magicBoard):
package magicBoard;
import java.awt.*;
public class MagicBoard {
public static void main(String[] args) {
Board board = new Board("¡Tablero magico!",Color.orange, 800,600);
board.settings();
}
}
Board class:
package magicBoard;
import java.awt.*;
import javax.swing.*;
public class Board extends JFrame{
protected Canvas canvas;
protected String title;
protected Color color;
protected Container container;
protected Dimension screenSize;
Board(String title, Color color, int width, int height){
this.canvas = new Canvas(width, height);
this.title = title;
this.color = color;
this.container = getContentPane();
this.screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
}
public void settings() {
setTitle(this.title);
this.setSize(this.canvas.width , this.canvas.height);
this.canvas.setBackground(this.color);
container.setLayout(new BorderLayout());
container.add(this.canvas, BorderLayout.CENTER);
this.setLocation(
this.screenSize.width / 2 - this.canvas.width / 2,
this.screenSize.height / 2 - this.canvas.height / 2
);
this.show(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
Canvas class:
package magicBoard;
import java.awt.event.*;
import java.util.EventListener;
import javax.swing.*;
public class Canvas extends JPanel{
protected EventListener listeners;
protected Pointer pointer;
protected int width,height;
Canvas(int w, int h){
//Tamaño de la ventana
this.pointer = new Pointer(0,0);
this.width = w;
this.height = h;
//Pocisiones X y Y actuales del cursor en el canvas
listeners = new EventManager(this);
addMouseListener((MouseListener) listeners);
addMouseMotionListener((MouseMotionListener) listeners);
}
}
Pointer class (Beware, this class will have more functions later, I would put the drawing functions in it, for now I implemented these directly in the class that handles the events (as a test), then they will be migrated once they work):
package magicBoard;
import java.awt.*;
public class Pointer {
protected int x,y;
Pointer(int x,int y){
this.x = x;
this.y = y;
}
public void updatePoint(Point p){
this.x = p.x;
this.y = p.y;
}
}
EventManager class:
package magicBoard;
import java.awt.*;
import java.awt.event.*;
public class EventManager implements MouseListener,MouseMotionListener {
protected boolean mouseIsPressed;
protected Canvas canvas;
protected Graphics2D ctx;
public EventManager(Canvas canvas) {
this.mouseIsPressed = false;
this.canvas = canvas;
this.ctx = (Graphics2D) canvas.getGraphics();
System.out.println((Graphics2D) this.canvas.getGraphics());
}
@Override
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
if(this.mouseIsPressed) {
this.ctx.drawLine(this.canvas.pointer.x,this.canvas.pointer.y, p.x,p.y);
this.canvas.pointer.updatePoint(p);
}
}
@Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseClicked(MouseEvent ev) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent ev) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent ev) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(MouseEvent ev) {
this.canvas.pointer.updatePoint(ev.getPoint());
this.mouseIsPressed = true;
}
@Override
public void mouseReleased(MouseEvent ev) {
this.mouseIsPressed = false;
}
}
The problem occurs in the EventManager class, where when trying to assign a new instance of type Graphics2D to the ctx attribute of the EventManager class, it is saved as null, resulting in the following error when the program starts:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at magicBoard.EventManager.mouseDragged(EventManager.java:27)
My question is...
If the attribute of the EventManager class called canvas is not null (Since I checked it by printing it to the console), then... why can't I correctly get a Graphics2D object from this object in the constructor of the class??
Important Note: Just try doing the following in the mouseDragged method and it works! (There is the reference).
Instead of:
public class EventManager implements MouseListener,MouseMotionListener {
protected boolean mouseIsPressed;
protected Canvas canvas;
protected Graphics2D ctx;
public EventManager(Canvas canvas) {
this.mouseIsPressed = false;
this.canvas = canvas;
this.ctx = (Graphics2D) canvas.getGraphics();
System.out.println((Graphics2D) this.canvas.getGraphics());
}
@Override
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
if(this.mouseIsPressed) {
this.ctx.drawLine(this.canvas.pointer.x,this.canvas.pointer.y, p.x,p.y);
this.canvas.pointer.updatePoint(p);
}
}
//Resto de código
Do this:
public class EventManager implements MouseListener,MouseMotionListener {
protected boolean mouseIsPressed;
protected Canvas canvas;
public EventManager(Canvas canvas) {
this.mouseIsPressed = false;
this.canvas = canvas;
}
@Override
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
Graphics2D ctx = (Graphics2D) this.canvas.getGraphics();
if(this.mouseIsPressed) {
ctx.drawLine(this.canvas.pointer.x,this.canvas.pointer.y, p.x,p.y);
this.canvas.pointer.updatePoint(p);
}
}
//Resto de código
However, I would like to have the reference, let's say it like this... Global, so that all the methods can access a single object and not have to create one for each method...
This in javascript could be easily fixed by simply assigning the object in the constructor, but this doesn't seem to work correctly in java or I'm not doing it right... (I also tried making the ctx attribute of type public and it didn't work, it's still null the reference )... any way to make it global without the reference ending up being null so I can use it globally in my methods??
First of all: no idea about Java . But, with a search engine at hand...
First, we consult the documentation:
...umm...
... si no es posible mostrar el componente
. Okay, we're getting our bearings now. Now, let's look at your code...We see that first you create the
Canvas
and then, in a different method (settings( )
), you add it to theContainer
... of course, with all the logic in the world. You can't add it before creating it, right?Now, if we look at your class
Canvas
... more specifically, its constructor :Ok, you create a
EventManager
and pass your own instance to itthis
... Wait a minute! Didn't we see that you first create the instance ofCanvas
and then add it ? ??There you have your problem: at the time you call the constructor of
EventManager
, yourCanvas
is not displayable . And, according to the documentation we saw at the beginning, itcanvas.getGraphics( )
will returnnull
.I'm afraid you'll have to change the logic of that part of your application. I'm sorry I can't be of more help from here, I already said I have no idea about java :-)
You can initialize the variables you require when the mouse enters the Canvas, rather than in the constructor.
Another option is that you get the information at each event. This would be another option:
Another option could be that Canvas itself would handle the events and thus you would not have to require the global variables since you would be inside. Example: