I have this example of a java jpanel:
import java.awt.*;
import javax.swing.*;
public class JavaGame extends JFrame {
public JavaGame() {
setTitle("Game");
setSize(500,500);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics g) {
g.drawString("Hello World!",75,75);
}
public static void main(String[] args) {
new JavaGame();
}
}
So, we just define the method paint, and we create new JavaGame object, and it just calls paint. I don't get two things:
new JavaGame() - shouldn't we assign a object name like obj = new JavaGame()?
Shouldn't we call the method paint like obj.paint()?
I have basic understanding of OOP, but this piece of code confuses me. Could someone please explain it to me?
new JavaGame() - shouldn't we assign a object name like obj = new JavaGame()?
This gets to the important distinction between objects and reference variables. What is most important here is creating a JavaGame object, which you do by new JavaGame(). Since this object displays the JFrame in its constructor, then simply creating the object is all that is needed to create the display, and you don't need to assign the object to any variable.
Note that objects don't have names, but rather, some variables do. For instance, if you created a Foo object, and assigned it to a bar variable:
Foo bar = new Foo();
But then assigned the same object to a baz variable:
Foo baz = bar;
Both bar and baz refer to the exact same Foo object. What then is the object's name? Again, object names don't exist and are meaningless.
Shouldn't we call the method paint like obj.paint()?
As noted by MightyPork (1+ to him), the paint method is called by the JVM, and should not be directly called by you. You can suggest that it be called by calling repaint(), but you should almost never call it, or the paintComponent(...) method, directly.
Other issues:
When painting in Swing, don't forget to call the super paint method within your own override. Otherwise the component will not do its own house-keeping painting, and you will break the painting chain, meaning components held by the painted container might not be painted correctly or at all.
In general, you will want to not want to draw directly within a JFrame, since a JFrame is responsible for many key components, and if you messed something up, the GUI could be messed up.
Much better would be to draw in a JPanel's paintComponent method, and then display that JPanel within your JFrame.
Check out the tutorials for all the gory but very important details:
Lesson: Performing Custom Painting: introductory tutorial to Swing graphics
Painting in AWT and Swing: advanced tutorial on Swing graphics
Edit You ask:
So, when use API, we just "fill" with code the methods in the class, and the JVM will know when to call these methods?
Most often Swing graphics is "passive". Meaning you set your component up to be drawn a certain way, but you don't actively draw it yourself. Rather the JVM (the Java Virtual Machine -- the code that runs your Java program) does the drawing for you. If you want to do animation, often you'll change some positional variables, for instance xPosition and yPosition int variables, and then use those variables within your JPanel's paintComponent(Graphics g) method. So if you change the values held by these variables within a Swing Timer, and then call repaint() after the values have changed, the JVM will (usually) repaint your component, using the new values.
A more complex example that shows drawing in a JPanel, and shows a very simple animation:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
// draw in a JPanel, not a JFrame
public class SimpleAnimation extends JPanel {
private static final int OVAL_WIDTH = 40;
private static final int TIMER_DELAY = 50;
private static final int PREF_W = 800;
private static final int PREF_H = PREF_W;
private int xPosition = 0;
private int yPosition = 0;
public SimpleAnimation() {
// start my timer here
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
// call the super method so that the JPanel
// can do its own house-keeping graphics first
super.paintComponent(g);
// I do this to so that my graphics are smooth
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.red);
// use xPosition and yPosition to place my oval
g2.fillOval(xPosition, yPosition, OVAL_WIDTH, OVAL_WIDTH);
}
// so our GUI will be big enough to see
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
// class used by the Swing Timer to drive animation
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// change xPosition and yPosition
xPosition++;
yPosition++;
// and call repaint
repaint();
}
}
// a method to be called in our Runnable
private static void createAndShowGui() {
SimpleAnimation mainPanel = new SimpleAnimation();
JFrame frame = new JFrame("SimpleAnimation");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
// this is used to make sure that all Swing code
// is started on the Swing event thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
You create the object new JavaGame() without assigning it to any variable, and that's it.
Swing render and event threads kick in and take care of the rest of the app's life.
I'm not sure why the Garbage Collector doesn't pick it up and end it, but I've done it this way for a long time and it works fine.
You don't need an object name. When you use new, the constructor will be called, which creates the frame.
Paint gets called whenever it needs to be. Check out this answer.
Related
I am trying to write a simple 2d animation engine in Java for visualizing later programming projects. However, I am having problems with the window refresh. On running, the frame will sometimes display a blank panel instead of the desired image. This begins with a few frames at a time at apparently random intervals, worsening as the program continues to run until the actual image only occasionally blinks into view. The code for processing each frame is run, but nothing in the frame is actually displayed. I believe the problem may come from my computer more than my code (certainly not from bad specs though), but am not sure. Help much appreciated.
Three classes. Code here:
package animator;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.applet.AudioClip;
public class APanel extends JPanel
{
public APanel(int l, int h){
setPreferredSize(new Dimension(l,h));
setLocation(80, 80);
setVisible(true);
setFocusable(true);
}
public Graphics renderFrame(Graphics g){
return g;
}
public void paintComponent(Graphics g) {
requestFocusInWindow();
renderFrame(g);
}
}
package animator;
import java.awt.*;
public class Animator extends APanel
//extending the APanel class allows you to code for different animations
//while leaving the basic functional animator untouched
{
public static final int SCREEN_X = 700;
public static final int SCREEN_Y = 700;
int frameNum;
public Animator() {
super(SCREEN_X, SCREEN_Y);
frameNum = 0;
}
public Graphics renderFrame(Graphics g) {
frameNum++;
g.drawString(""+frameNum,5,12);
return g;
}
}
package animator;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.Timer;
public class Runner {
int framerate = 30;
Animator a = new Animator();
JFrame j = new JFrame();
public Runner(){
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
j.add(a);
start();
j.setSize(a.getPreferredSize());
j.setVisible(true);
}
public void start() {
Timer t = new Timer(1000/framerate, new ActionListener() {
public void actionPerformed(ActionEvent e){
j.getComponent(0).paint(j.getComponent(0).getGraphics());
//The following line of code keeps the window locked to a preferred size
// j.setSize(j.getComponent(0).getPreferredSize());
}
});
t.start();
}
public static void main(String[] args){
Runner r = new Runner();
}
}
There are some serious mistakes in your code which could be the cause or a factor of your problem...
j.setSize(a.getPreferredSize()); is irrelevant, simply use JFrame#pack, you get better results as it takes into account the frame decorations
j.setSize(j.getComponent(0).getPreferredSize()); use JFrame#setResizable and pass it false instead...
NEVER do j.getComponent(0).paint(j.getComponent(0).getGraphics()); this! You are not responsible for the painting of components within Swing, that's the decision of the RepaintManager. Just call j.repaint()
super(SCREEN_X, SCREEN_Y);...just override the getPreferredSize method and return the size you want.
setLocation(80, 80); irrelevant, as the component is under the control of a layout manager
setVisible(true); (inside APanel)...Swing components are already visible by default, with the exception of windows and JInternalFrames
And finally...
public void paintComponent(Graphics g) {
requestFocusInWindow();
renderFrame(g);
}
There is never any need for this method to be made public, you NEVER want someone to be able to call it, this will break the paint chain and could have serious ramifications in the ability for the component to paint itself properly.
You MUST call super.paintComponent before performing any custom painting, otherwise you could end up with all sorts of wonderful paint artifacts and issues
Never modify the state of a component from within a paint method...while it "might" not be an immediate issue calling requestFocusInWindow(); within your paintComponent could have side effects you don't know about...
Instead, it should look more like...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
renderFrame(g);
}
I want to start building my own customized JComponent's for a project at work. I have a simple example below that should just create a ball on the screen. (I found most of it on the internet) but it does provide a decent starting point. My question is why does this code not show the ball in my form? What have I done wrong?
Also what would be all of the basic methods that SHOULD be provided for a custom JComponent?
Code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class testBall {
public static void main(String[] args) {
new testBall();
}
public testBall() {
JPanel testPane = new JPanel();
testPane.setBackground(Color.white);
testPane.setLayout(new GridBagLayout());
testPane.add(new MyBall(30,30,10));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(testPane);
frame.pack();
frame.setSize(500, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class MyBall extends JComponent
{
private static final long serialVersionUID = 1L;
public MyBall() { }
public MyBall(int x, int y, int diameter)
{
super();
this.setLocation(x, y);
this.setSize(diameter, diameter);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(0, 0, 100, 100);
}
}
Where I could find a list of all of the methods that should be overridden in a JComponent class? (I know there are ones that should always be included in JComponent.)
If I make an instance of this component in a class and need to change the color of the circle would I just call there repaint() method from that class?
You are adding the MyBall to testPane (which has a GridBagLayout) without specifying any constraints. (That is, your call to add() has no second parameter.) The default constraints are most likely not what you want. Try using BorderLayout for your test pane, as this uses BorderLayout.CENTER as the default, which is probably reasonable in your case:
testPane.setLayout(new BorderLayout());
This causes the ball to show up for me.
As for your second question, I think your class is fine as defined. The main method you want to implement is paintComponent() as you have. Sometimes it becomes necessary to override the get min/max/preferred size methods, but it really just depends on what you want the component to do. JComponent is not abstract, so you don't have to override anything if you don't want to. It provides a lot of functionality out of the box such as keyboard focus, pluggable look-and-feel, accessibility, etc. As long as you don't want to change any of that stuff, just leave it as is. Implement paintComponent() and the various get*Size() methods and be done with it. (You just kind of have to pick through the methods in the JavaDoc to see what is appropriate to override.)
The other option is to extend a subclass of JComponent, if there as a class that does something similar to what you want to do. For example, JPanel is often a good starting point for impelmenting your own container.
You were probably looking for something more concrete than 'it depends', but right now, if all you want is to draw a ball, then simply override the methods that deal with the rendering of the JComponent (paintCompentent(), and the get*Size() methods).
As a side note, you really should be using SwingUtilities.invokeLater() to create your Swing components on the Swing thread. See the section entitled "Swing's Threading Policy" at http://docs.oracle.com/javase/6/docs/api/javax/swing/package-summary.html.
Made some tweaks around your java class, the only change i did was add your new MyBall directly to the content pane of the JFrame, try to run this and you will see a red circle on your jframe
public class TestBall {
public static void main(String[] args) {
new TestBall();
}
public TestBall() {
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.getContentPane().add(new MyBall(30,30,10));
frame.pack();
frame.setSize(500, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class MyBall extends JComponent
{
private static final long serialVersionUID = 1L;
public MyBall() { }
public MyBall(int x, int y, int diameter)
{
super();
this.setLocation(x, y);
this.setSize(diameter, diameter);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(0, 0, 100, 100);
}
}
Constructor: we just set location to the points, which are passed throught constuctor parameters. This is the place, in which this component will be located. The same way we set the size (width x height).
paintComponent: here we just paint some oval over passed graphics object.
The other part is just the test, which shows, that the component is correctly created and shown.
As i understand (and i am java noob), when i resize a window or change its content paintComponent() method should be called automatically. It redraws everything, so when i override it with an empty method, nothing should be redrawn...but it is. Why? Probably i am missing something. What exactly is redrawn by paintComponent(), everything? Or some backgrounds or smth?
import java.awt.*;
import javax.swing.*;
public class TextFrame extends JFrame {
public TextFrame(String text, String fontName) {
super("Show Font");
setSize(725, 150);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TextFramePanel sf = new TextFramePanel(text, fontName);
JButton ok = new JButton("i hate disappearing");
sf.add(ok);
add(sf);
setVisible(true);
}
public static void main(String[] arguments) {
if (arguments.length < 1) {
System.out.println("Usage: java TextFrame message font");
System.exit(-1);
}
TextFrame frame = new TextFrame(arguments[0], arguments[1]);
}
}
class TextFramePanel extends JPanel {
String text;
String fontName;
public TextFramePanel(String text, String fontName) {
super();
this.text = text;
this.fontName = fontName;
}
public void paintComponent(Graphics comp) {
//super.paintComponent(comp);
/*Graphics2D comp2D = (Graphics2D)comp;
comp2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font(fontName, Font.BOLD, 18);
FontMetrics metrics = getFontMetrics(font);
comp2D.setFont(font);
int x = (getSize().width - metrics.stringWidth(text)) / 2;
int y = getSize().height / 2;
comp2D.drawString(text, x, y);
System.out.println("vlad");*/
}
}
I suggest you read up on the official docs for Custom Painting: http://docs.oracle.com/javase/tutorial/uiswing/painting/index.html, more importantly under the Section A Closer Look at the Paint Mechanism.
Here's the part that's directly related to your question:
[...] the paintComponent method is where all of your painting code should be placed. It is true that this method will be invoked when it is time to paint, but painting actually begins higher up the class heirarchy, with the paint method (defined by java.awt.Component.) This method will be executed by the painting subsystem whenever you component needs to be rendered. Its signature is:
public void paint(Graphics g)
[...] The API does nothing to prevent your code from overriding paintBorder
and paintChildren, but generally speaking, there is no reason for you
to do so. For all practical purposes paintComponent will be the only
method that you will ever need to override.
So, when you're declaring:
public void paintComponent(Graphics comp) {}
you're not actually doing nothing. That's because the painting, as stated from the docs, does not begin with paintComponent(), but rather with paint(), which is called much earlier.
Now, if you declare it like this:
public void paint(Graphics g){}
then nothing will be redrawn after resizing and etc, no matter what code you have in your paintComponent(), because the beginning of the paint hierarchy has just been defined as an empty routine.
"...so when i override it with an empty method, nothing should be redrawn...but it is. Why?"
So it looks like you're running this program from the command line. If you are running the program, and it's still painting the stuff inside the paintComponent method (even after you commented out its content), most likely you ran the program with compiling again. So the program is still running from the same .class file.
When I run the program as is, I see nothing but a button, no painted words.
C:\stack>java TextFrame Hello World
" What exactly is redrawn by paintComponent(), everything? Or some backgrounds or smth?"
paintComponent just paint the component itself, which is just the JPanel in your case. It doesn't paint any other components added to it. That painting is delegated by its paintComponent method. Every JComponent has its own paintComponent method. So if you're wondering why the button still appears, it's because the call to paintComponent in the JPanel only affects the JPanel, not other child components add to it.
I'm trying to make an application that runs an animation. To do this I've got a Jframe that contains my subclass of Jpanel in which the animation runs. Here are my two classes:
Firstly, here's my driver class:
import javax.swing.*;
public class Life {
public static void main(String[] args){
JFrame game = new JFrame("Life");
game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.setSize(500, 500);
MainPanel mainPanel = new MainPanel();
game.setContentPane(mainPanel);
game.setVisible(true);
}
}
Secondly, here's my subclass of Jpanel:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MainPanel extends JPanel implements ActionListener{
int i = 0;
int j = 0;
public MainPanel(){
super();
}
public void paintComponent(Graphics g){
j++;
g.drawLine(10,10, 20 + i, 20 + i);
Timer t = new Timer(1000, this);
t.start();
}
#Override
public void actionPerformed(ActionEvent actionEvent) {
i++;
repaint();
}
}
Notice that the variable i is incremented each time actionPreformed is called and the variable j is called each time paintComponent is called. What winds up happening is that i starts to be much larger than j and the line drawn by paintComponent seems to grow at a faster and faster rate.
Here are my questions:
Why does this happen?
How can I sync things up so that the line gets redrawn each every 1000 ms?
Given what I'm trying to do, is my approach wrong? Should I be doing things differently?
Thanks in advance.
Don't start a Swing Timer from within a paintComponent method.
This method should do your painting, only your painting and nothing but painting. It should contain absolutely no program logic. Understand that you have very limited control over this method since you cannot predict when or if it will be called, how often it will be called. You can't even call it yourself or be guaranteed that when you suggest it be called via repaint() that it will in fact be called.
Also this method must be fast, as fast possible since anything that slows it down, be it object creation or reading in files will reduce the perceived responsiveness of your GUI, the last thing that you want to see happen.
The solution is to separate the program logic out of that method and into better methods such as your constructor. Repeating code should be in a Swing Timer.
Edit:
You state:
I just did that to test things out. One more question: What happens if paintComponent, or some thread on which the work in paintComponent depends, takes longer than 1000 ms (or whatever it is) to do its work? The only thing I can think of is having paintComponent paint the progress of the animation so far, rather than waiting for the animation to reach the next step(if that makes any sense). Thoughts?
You should never have code in paintComponent that takes that long or even 10's of milliseconds. If there's a risk of something like that happening, then do the drawing in a background thread and to a BufferedImage, and then on the Swing event thread show the BufferedImage in paintComponent method using the Graphics#drawImage(...) method.
A few minor additions to #HFoE's essential insights:
A public start() method is a handy way to ensure that your view is completely constructed before starting.
Fields have well-defined default values, and they should be private.
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
Override getPreferredSize() and pack() the enclosing Window.
Revised code:
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Life {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
private final JTabbedPane jtp = new JTabbedPane();
#Override
public void run() {
JFrame game = new JFrame("Life");
game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MainPanel mainPanel = new MainPanel();
game.setContentPane(mainPanel);
game.pack();
game.setVisible(true);
mainPanel.start();
}
});
}
private static class MainPanel extends JPanel implements ActionListener {
private Timer t = new Timer(100, this);
private int i;
private int j;
#Override
public void paintComponent(Graphics g) {
g.drawLine(10, 10, 20 + i, 20 + i);
}
public void start() {
t.start();
}
#Override
public void actionPerformed(ActionEvent actionEvent) {
i++;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
}
}
A Timer by defaults keep on running. Only when you call setRepeats( false ) it will stop.
So the following lines
Timer t = new Timer(1000, this);
t.start();
in your paintComponent method means that after a few repaints you will have a number of Timer instances running, explaining why i increases that much faster then j.
The solution is of course to move your Timer outside the paintComponent method, and stick to one Timer instance.
Further remarks (which weren't said by the others, not gonna repeat their very useful advise):
Never override the paintComponent method without calling the super method
You shouldn't expose the ActionListener interface. Just use an ActionListener internally
If I have a JPanel object that I can't modify, is there a way I can modify the paintComponent method of it without using injection?
One approach I was thinking of was getting the JPanel's Graphics object, passing it to paintComponent(), performing operations on this Graphics object, and finally painting that in my custom JPanel. The problem with this, is I need to be able to do this every time the original JPanel's paintComponent() is called.
I don't need to replace what's in paintComponent(), I just need to add on to it.
For example:
JFrame frame = null;
for (Frame f : JFrame.getFrames()) {
if (((JFrame) f).getTitle().equals("Title")) {
JPanel panel = null;
// ... Cycle through all components and get the one that's a JPanel
// I want to use ColorConvertOp to make panel greyscale
}
}
One approach would be to use the Decorator Pattern to wrap the existing class. Your decorator can then implement paintComponent to first delegate to the original component and after that paint on top of it. For this approach you need to actually gain control over the creation of the components, or you need to replace them after the component hierarchy has been created (using getComponents() of the parent container to find the components to be altered).
I think one possibility is to use a GlassPane and position it exactly over your JPanel (maybe let it follow the panel using listeners if the panel changes its location). Then you can simply draw your stuff in the glasspane and it will be overlayed.
Of course this is not really elegant... But I don't see any possibility to change the paintComponents behaviour of an already existing instance without injection. (Proof me wrong, Java geeks of this world! :P)
I guess this would complete #Durandal's answer:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestPanels {
private static final boolean GRAY_SCALE = true;
protected void initUI() {
final JFrame frame = new JFrame(TestPanels.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel unmodifiablePanel = new JPanel(new GridBagLayout());
JLabel label = new JLabel("Some unmodifiable test label");
unmodifiablePanel.add(label);
unmodifiablePanel.setBackground(Color.GREEN);
JPanel wrappingPanel = new JPanel(new BorderLayout()) {
private ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
private BufferedImage image;
#Override
public void paint(Graphics g) {
if (!GRAY_SCALE) {
super.paint(g);
return;
}
BufferedImage bi = getImage();
if (bi != null) {
Graphics big = bi.createGraphics();
super.paint(big);
big.dispose();
bi = op.filter(bi, null);
g.drawImage(bi, 0, 0, null);
}
}
protected BufferedImage getImage() {
if (image == null) {
if (getWidth() > 0 && getHeight() > 0) {
image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
}
} else if (image.getWidth() != getWidth() || image.getHeight() != image.getHeight()) {
image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
}
return image;
}
};
wrappingPanel.add(unmodifiablePanel);
frame.add(wrappingPanel);
frame.setSize(200, 200);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestPanels().initUI();
}
});
}
}
You can turn the GRAY_SCALE flag to false, to see how it renders normally.
If you had the ability to modify the construction of your class, you could extend that class and then call super.paintComponent(g) in your extended class. Example:
public class NewPanel extends OldPanel{
public void paintComponent(Graphics g){
super.paintComponent(g);
// Insert additional painting here
}
}
In this case you will need a new class in order to build onto the existing class's painting.
What this does is execute everything that was done in the parent class's painting, and give you the option to do more (which seems to be what you're looking for).
EDIT
BUT... given that you don't have access to this, your options get more limited. If the panel uses a null layout manager, you could add a child jpanel who paints over the parent (a layout manager would restrict the amount of area the child could paint over though). This is a long shot.
You can also use reflection to but about the only option you've got (besides byte code injection). This seems about equally ugly as byte code injection - here's a decent overview of what you'd be looking to do: Java reflection: How do I override or generate methods at runtime?
My personal preference is to decompile the class, modify it the way you want, recompile it and insert it back into the original jar. This will probably void some warrenties, licenses and get you in other trouble... but at least it's maintainable.