I was taught in my class and also seen in this book (Big Java Early Objects) to include draw instructions in a class extending JComponent as:
public class Component extends JComponent {
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Rectangle r = new Rectangle(0,0,20,10);
g2.draw(r);
}
}
However, someone pointed out that the first line in the paintComponent method should be: super.paintComponent(g);
Based on my limited knowledge, I believe it's calling JComponent's version of the method (now overridden). Why does this need to happen? What happens if I just ignore this statement as I've been doing until now?
Why does this need to happen? What happens if I just ignore this statement as I've been doing until now?
A component is responsible for painting itself completely. The default painting may be different for each LAF, so by invoking super.paintComponent() you make sure you get the default painting which will basically just be the background.
If you don't invoke this method you have the potential for painting artifacts to occur. Then may not always occur, but you don't want to waste time debugging.
Read the API for the paintComponent() method of JComponent. Among other things it states:
Further, if you do not invoker super's implementation you must honor the opaque property, that is if this component is opaque, you must completely fill in the background in a non-opaque color. If you do not honor the opaque property you will likely see visual artifacts.
So at a minimum you need:
g.fillRect(0, 0, getWidth(), getHeight());
to make sure the background is cleared before you start your custom painting.
Related
I am currently programming a game which includes lots of graphical elements. Currently the code looks like this:
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(ImageLoader.gui_iconbar, xToRel(480), yToRel(810), xToRel(960), yToRel(270), null);
g.drawImage(ClientVariables.getSplashart(Main.selectedCharacter, 0), xToRel(495), yToRel(825), xToRel(135), yToRel(240), null);
//... (don't get confused with xToRel, it just makes changes to the drawing position if you move the camera)
}
The problem is: there are TONS of these g.draw(whatever); lines. I want to split them in multiple classes but just making 4 classes and copy paste gameFrame.add(GraphicclassXY); doesn't work, logically. Does anyone know how to fix this issue? (It's not a problem if the solution includes making changes from jlabel to canvas for example)
Create logical non-component classes (that don't extend JLabel, JPanel, JFrame, JComponent or other Swing component), give them a public void draw(Graphics g) method, give the class that does the drawing instances of your logical classes, perhaps within some sort of collection, and iterate through the collection and call draw on your instances within the paintComponent method.
I'm working on project, which uses custom Layout Manager. My goal is rotate all components by 180°.
I have some screen which consists of JFrame. I also have some LayoutHandler, which defines some layers of layout. Each layer is instance of LayerLayout which implements LayerManager2 from java.awt
The flow works probably like this:
I have JFrame, which represents the display. This JFrame has some default contentpane. The application set to this contentpane Layout which is represented by LayerHandler:
graphicalDisplayJFrame.getContentPane().setLayout(new LayerLayout(layerHandler));//graphicalDisplayJFrame is JFrame which represents the display
The layerHandler just somehow manage the layers which are LayerLayouts which implemetns LayerLayout2 from java.awt. So it looks like layout of layouts or whatever it is. I'm not much experienced with creating custom layouts.
There I have a situation which shows creating of JFrame.
graphicalDisplayJPanel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.rotate(Math.PI, anchorx, anchory); //rotation 180°
}
#Override
public void paintChildren(Graphics g) {
super.paintChildren(g);
Graphics2D g2d2 = (Graphics2D) g;
g2d2.rotate(Math.PI, anchorx, anchory);
}
};
graphicalDisplayJFrame = new JFrame(); //creation of JFrame
graphicalDisplayJFrame.setContentPane(graphicalDisplayJPanel); //setting JFrame to apply rotation
The first part should make the rotation 180°, but it does not, even when i call repaint on graphicalDisplayJFrame. BUT when I add this:
JLabel hwLabel = new JLabel("Hello world.");
hwLabel.setVisible(true);
graphicalDisplayJFrame.add(hwLabel);
I cause strange thing. The JFrame and all of the components on it is accidentaly rotated by 180°. But its only static behaviour, because there are some text components on display and when there is changed the text on them its again rendered in non rotated way.
So the question is why this could happens?
If i divide it in two parts:
a) Why the Graphics2D on JFrame does not affect the children components on its content pane. Why it only works when I add the JLabel which has no sense here but it was just experiment which caused what I wanted (= repaint with right rotation).
b) I should probably somehow apply the rotation => something like overriding the paint method on each components which are added on the contentPane of JFrame. But how should can I do it? Becouse upper described layout handler just manage a bunch of layouts (layers which are probably custom layouts implementig LayoutManager2 from java.awt) and it is just feeding the components which are put on the layouts and put them on content pane. So the question should probably be where could be the place where I want to implement my custom paining which means apply the rotation (which is this part of code g2d.rotate(Math.PI, anchorx, anchory); //rotation 180°).
I hope it understandable, I made pretty bit effort to understand where could be problem and trying find out the solution, but its behave really strange. So I hope someone with more experience with this could help me out off this struggling and show me the way.
You are applying the rotation at very strange points. Graphics2D.rotate() applies only to things painted after the rotate call, but your paint-overides rotate after calling super.
As for why adding a label causes rotation, hard to tell without seeing the entire code. Probably an effect of partial repaint.
You should not modify essentials like the graphics translation or rotation of the graphics passed to your paint methods. It has the potential to affect the parent components drawing in unexpected way and the effect can easily change depending on JRE version. Instead create a new graphics to pass down to your child components:
#Override
public void paintChildren(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
try {
g2d.rotate(Math.PI, anchorx, anchory);
super.paintChildren(g2d);
} finally {
g2d.dispose();
}
}
This method leaves the original Graphics untouched, at least allowing the parent component to render normally. Rotating the graphics is still tricky and will probably have unintended side effects in the rendering process, but at least these effects are now carefully limited to the children of the rotating component.
So I'm trying to learn Java graphics and this bit of code has me perplexed. I don't understand a couple things here:
Why is paintComponenet used twice for the name of the method and as
we call a method from super (JPanel)?
What is Graphics g, isn't it just a reference variable for an object
of Graphics since we don't set it equal to = new Graphics();?
Why does the method name in my class have to be paintComponent to
call upon the method paintComponent from JPanel or super?
The method in my class paintComponent takes the parameter of a
Graphics object but when does paintComponent even get called and
when is the parameter of Graphics inserted?
Essentially I need someone to explain this code to me.
//note this is all in a class that extends JPanel, my JPanel is later placed in
//a JFrame which is run through main
public void paintComponent(Graphics g)
{
int width = getWidth();
int height = getHeight();
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(10, 10, 200, 200);
g.setColor(Color.BLUE);
g.drawRect(10, 10, 200, 200);
}
Why is paintComponenet used twice, for the name of the method and as we call a method from super (JPannel)
It's not "used" twice. It is overridden once, but you want to call the parent (JPanel) class's super method so that you're sure that it does its own house-keeping painting, including painting its children and clearing out any dirty bits from the screen.
What is Graphics g, isn't it just a reference variable for an object of Graphics since we don't set it equal to = new Graphics();
It's a Graphics parameter. You don't set it = new Graphics() because the JVM does this for you. It calls the method behind the scenes when needed, and provides the parameter.
Why does the method name in my class have to be paintComponent to call upon the method paintComponent from JPannel or super
It has to override the super's method so that the JVM calls the right method when it wants to draw the GUI.
The method in my class paintComponent takes the parameter of a Graphics object but when does paintComponent even get called and when is the parameter of Graphics inserted.
Again, it is called by the JVM when either your program wants to repaint the GUI, such as when you call repaint() or when the operating system wants to repaint a window such as if a window is minimized and restored.
You really really want to read the graphics tutorials:
Lesson: Performing Custom Painting: introductory tutorial to Swing graphics
Painting in AWT and Swing: advanced tutorial on Swing graphics
1) Why is paintComponenet used twice, for the name of the method and as we call a method from super (JPannel)
Here the line super.paintComponent(...), means we want the JPanel to be drawn the usual Java way first (this usually depends on the opaque property of the said JComponent, if it's true, then it becomes the responsibility on the part of the programmer to fill the content area with a fully opaque color. If it is false, then the programmer is free to leave it untouched. So in order to overcome the hassle assoicated with this contract, super.paintComponent(g) is used, since it adheres to the rules, and performs the same task, depending upon whether the opaque property is true or false).
2) What is Graphics g, isn't it just a reference variable for an
object of Graphics since we don't set it equal to = new Graphics();
and
4) The method in my class paintComponent takes the parameter of a
Graphics object but when does paintComponent even get called and when
is the parameter of Graphics inserted
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)
javax.swing.JComponent extends this class and further factors the paint method into three separate methods, which are invoked in the following order:
protected void paintComponent(Graphics g)
protected void paintBorder(Graphics g)
protected void paintChildren(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
Part of my Code is this paint method:
public void paint(Graphics g) {
super.paint(g);
refreshSize();
paintSquares(g);
if (drawGrid) {
drawGrid(g);
}
}
how can I use a buffer to stop it from flickering?
You can reduce flicker by overwriting the update(Graphics g) method with an empty implementation (update's default implementation renders the background). If you do this, you must ensure you paint the entire area of the component.
(Note: Overwriting update only really helps on a Frame - other components will still flicker because the containing Frame)
That said, do yourself a favor and switch to Swing.
Edit: Digging around a bit, the background rendering occurs in the update() implementation of java.awt.Container (Frame being a subclass of Container ofc). Combined with backbuffer rendering (that you would need to handle yourself, e.g. a BufferedImage) you could get to what Swing automatically delivers with AWT.
I have placed a JPanel object inside a JScrollPane and scrolling works as expected. By overriding paintComponent() I have attempted to do custom painting within the JPanel object. However when the JPanel object is placed within the JScrollPane the JPanel no longer paints properly (instead shows only its background color).
Because my application demands the JPanel to be updated constantly, a separate thread is constructed to repaint the JPanel at a specific interval.
The following excerpts of code show my current project:
a) paintComponent() from my JPanel (This method has been cut down to only the painting, the actual paint will be a constantly updating BufferedImage supplied from another thread instead of this big pink static box):
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
//Render Frame
// 'RXDisplayCanvas' is the JPanel.
Graphics2D G2D = (Graphics2D)RXDisplayCanvas.getGraphics();
G2D.setColor(Color.PINK);
//800 and 600 are arbitrary values for this example, real values are calculated at runtime. The value calculation code is verified to work (as its used elsewhere in a similar scenario)
G2D.fillRect(0, 0, 800, 600);
G2D.dispose();
}
b) The 'updater' thread that periodically repaints the frame:
#Override
public void run() {
long MaxFrameTime;
long Time;
while(isVisible()){
// 'FPSLimit' is a integer value (default to 30)
MaxFrameTime = Math.round(1000000000.0 / FPSLimit);
Time = System.nanoTime();
try{
SwingUtilities.invokeAndWait(new Runnable(){
#Override
public void run() {
// 'RXDisplayCanvas' is the JPanel.
RXDisplayCanvas.repaint(); //When using this, the JPanel does not display correctly.
//RXDisplayCanvas.paintImmediately(0, 0, RXDisplayCanvas.getWidth(), RXDisplayCanvas.getHeight()); When using this, the JPanel renders correctly but flickers.
}
});
}catch(InterruptedException | InvocationTargetException e){}
Time = System.nanoTime() - Time;
if(Time < MaxFrameTime){
try{
Thread.sleep(Math.round((MaxFrameTime - Time)/1000000.0));
}catch(InterruptedException ex){}
}
}
}
I have taken into account that repaint() does not cause a immediate repainting of the screen but the issue lies with the incorrect rendering of the screen. When the program is left alone, it merely renders the background color of the JPanel until the JScrollPane is scrolled in which it renders correctly for one frame before the next repaint() call paints the incorrect display.
When switching repaint() out for paintImmediately() (in excerpt b) the frame rendered correctly but heavy flickering was present where it constantly alternated between painting the background color and painting the pink box. I have tried adding and removing layout managers, disabling the repaint manager as well enabling and disabling the 'double buffered' flag for both components in which all resulted in one of the two behaviors mentioned above (rendering only background or flickering).
Can anyone aid me on this issue?
N.B: I am well aware of Java's variable naming convention, since this is a private project I am choosing to start variable names with capital letters because I think it looks nicer, please do not post comments regarding this.
1) I am not sure about this:
public void paintComponent(Graphics g){
super.paintComponent(g);
// 'RXDisplayCanvas' is the JPanel.
Graphics2D G2D = (Graphics2D)RXDisplayCanvas.getGraphics();
..
G2D.dispose();
}
I would recommend doing:
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D G2D = (Graphics2D)g;
G2D.setColor(Color.PINK);
G2D.fillRect(0, 0, 800, 600);
}
Note how I have omitted the getGraphics, and use the current passed in graphic context of paintComponent.
Also note I do not call g2d.dipose() as this causes problem it should only be done on Graphics that you create Component.getGraphics() but in your case you shouldn't even be creating the Graphics context as it already has been created and passed in to the paintComponent method. (see this similar question)
2) No need for SwingUtilities.invokeXXX block for repaint() as it is thread safe. But especially no need for SwingUtilities.invokeAndWait (as this is a blocking call and wait until all pending AWT events gets processed and run() method completes) which is not good and may also be adding to the onscreen visual artefacts you see.
3) I have tried adding and removing layout managers, disabling the repaint manager as well enabling and disabling the 'double buffered' flag for both components in which all resulted in one of the two behaviours mentioned above (rendering only background or flickering). undo all that as I cannot see how that affected the painting.
It would be even more helpful if I had an SSCCE which illustrates the unwanted behaviour. As I could attempt to reproduce your error but I most likely wont be able to (due to the specific conditions that apply to your app that may be causing these visual artefacts)