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.
Related
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.
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.
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)
How can I use a image as background in a JPanel if the paint () method is already used for other purposes? (I'm tried to draw over a image in a panel).
Here is my code to draw as a pencil, but I don´t know how to add the image as background ?
#Override
public void paint(Graphics g) {
if (x >= 0 && y >= 0) {
g.setColor(Color.BLACK);
g.fillRect(x, y, 4, 4);
}
}
Thanks Diego
Suggestions:
Don't draw in the JPanel's paint(...) method but rather use it's paintComponent(...) method. There are several reasons for this, one being that if you use the paint(...) method, then you are also responsible for drawing the JPanel's borders and child components and at risk of messing up the rendering of these guys. Also you lose Swing's automatic double buffering.
First call the parent class's super method before calling any other code in the method. This will allow the JPanel to refresh its background and do any graphics housekeeping that may need to be done.
Next draw your background image using g.drawImage(...),
Then do your pencil drawing.
Hovercraft Full Of Eels gave good advice on one direction to take. Here is another.
Display the image in a (ImageIcon in a) JLabel.
When it comes time to paint:
Call createGraphics() on the BufferedImage to gain a Graphics2D object.
paint the lines or other visual elements to the graphics instance.
dispose of the graphics instance.
Call repaint() on the label.
E.G. as seen in this answer.
I have the following problem in swing.
I'm implementing basic drawing operations (lines, shapes). When I'm moving mouse with pressed left button, I need to repaint current shape. So I clear the screen and repaint already drawn shapes and currently being drawn one.
Shapes are drawn in paint() method and on mouse move event I call repaint() (paint() is called automatically). The problem is that the screen is blinking strongly on each repaint and it looks really ugly. Please tell me, what I'm doing wrong? Thanks.
I think what you are looking for is double buffering.
Shapes are drawn in paint()
Custom painting should be done in the paintComponent() method and make sure you invoke super.paintComponent() as the first line.
Also custom painting is done on a JPanel (or JComponent), not on the JFrame directly.
I had flickering or blinking problem. I solved it using the following code.
public void update(Graphics g) {
paint(g);
}
#Override
public void paint(Graphics g) {
//super.repaint();
if (myimg != null) {
g.drawImage(myimg, 0, 0, this);
}
//update(g);
}
You don't need to clear the screen, you just call repaint() then it's enough. If you have to clear the screen, it'll blink if you don't use synchronization, because the painting job is done in a separate thread.