Reputation: 444
I want my custom Swing component CellLabel extends JLabel
to paint certain things (X) always, and certain other elements (Y) only if its parent - (a JPanel
holding a grid of CellLabel
s) - tells it to. The painting of (Y) depends on the state of the neighbouring cells in the grid, and only the JPanel
can get this information and decide if and how (Y) should be painted for every child CellLabel
.
How can I externalize (into a method with parameters that allow me to describe how to paint (Y) exactly) the painting behavior so that the parent can decide whether it wants a CellLabel
child to paint (Y) or not?
Every Swing tutorial out there tells me I should overwrite the paintComponent
method when creating a custom component; however, in my application, the component can't decide for itself if and how (Y) should be painted since it lacks the necessary information.
I tried to write a CellLabel::paintY(int offset)
method that I could call from the parent once it has decided how and if to paint (Y):
class CellLabel extends JLabel {
void paintComponent(Graphics g) {
super.paintComponent(g);
// Paint elements (X) which should always be painted, independent
// of external state
}
void paintY(int[] params){
Graphics2D g2 = (Graphics2D) this.getGraphics();
// Perform some calculation with params that decide how painting is done
// (*) Actually paint elements (Y) by using the g2 graphics context
}
}
However, it seems I can't get a hold of the Graphics
context outside of the predefined paint
methods that convention tells me to overwrite. I get the following exception at (*) (when actually trying to paint with g2):
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at CellLabel.paintY(CellLabel.java:77)
at AutomatonUI.createAndShowGUI(AutomatonUI.java:50)
at AutomatonUI.access$000(AutomatonUI.java:22)
at AutomatonUI$1.run(AutomatonUI.java:29)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:744)
at java.awt.EventQueue.access$400(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:697)
at java.awt.EventQueue$3.run(EventQueue.java:691)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:714)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
I have a solid amount of experience coding business logic and number-crunching, but I'm quite new to Swing (and UI programming) in general, so please excuse me (and educate me) if I'm doing something horribly wrong.
Upvotes: 1
Views: 323
Reputation: 5496
A better approach might be to separate your logic from the Swing GUI library. For example, defining a CellContainer
class that contains Cell
objects, which can be updated before each render (where in the update you can update Cell
s from CellContainer
however you wish). You could then have CellContainerPanel
and a CellLabel
class that extend the appropriate Swing class which take a reference to CellContainer
and Cell
each which just draw the state that their references make available.
Upvotes: 2
Reputation: 3537
IIRC, your paintComponent function needs to call super.paintComponent(g);
The last time I wrote a program with swing, i declared a static graphics object in a class that everything could access (this is probably a horrible thing to do, but it was a workaround which helped me to complete my task). Instead of storing the information inside of a two dimensional array, store "information" objects inside the array. Those information objects (object x, for example) can contain all the information about the cell itself, as well as a "draw(g)" method. That draw method can dictate how this cell is drawn. In the paintComponent function, you can now call
//where g is the graphics object
for (array x : array of arrays) {
for (informationObject y : x) {
y.draw(g);
}
}
Now you can do all the calculations inside the object itself, instead of having to deal with obtaining external information and doing calculations outside. (Then, the code will be easier to debug as well)
Upvotes: 0