Here is simple example of drawing an oval.
public class SwingPainter extends JFrame{
public SwingPainter() {
super("Swing Painter");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
getContentPane().add(new MySwingComponent());
setSize(200, 200);
setVisible(true);
}
public static void main(String[] args) {
new SwingPainter();
}
class MySwingComponent extends JComponent {
public void paintComponent(Graphics g) {
System.out.println("paintComponent");
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(10, 10, 50, 50);
}
#Override
protected void paintBorder(Graphics g) {
System.out.println("Paint border");
super.paintBorder(g);
}
#Override
protected void paintChildren(Graphics g) {
System.out.println("Paint children");
super.paintChildren(g);
}
}
}
But in debug mode or adding some info to console before drawing (as in example), you can see that swing draws components twice.
paintComponent
Paint border
Paint children
paintComponent
Paint border
Paint children
I cannot understand why it happens, but I think it can affect performance in a difficult GUI.
The article Painting in AWT and Swing: Additional Paint Properties: Opacity suggests why: "The opaque property allows Swing's paint system to detect whether a repaint request on a particular component will require the additional repainting of underlying ancestors or not." Because you extend JComponent, the opaque property is false by default, and optimization is not possible. Set the property true to see the difference, as well as the artifact from not honoring the property. Related examples may be found here and here.
I agree with Konstantin, what you need to remember, the repaint manager is responding to any number of requests when the application starts, this typically includes the initial paint request when the window is shown and resized (there's two).
Try this. Wait till the application is running and resize the window. I'm sure you'll get more then a couple of repaint requests ;)
This works fine to me. In fact, even in debug mode the output was:
paintComponent
Paint border
Paint children
Please, bear in mind that in AWT and Swing components there are many methods (paint, paintBorder, paintChildren, paintComponent, repaint, and others) that are called via call-back, whenever the GUI engine finds suitable. That may vary from JVM to JVM or even from different execution sessions. They can also be triggered from the interaction to your program (if you minimize/maximize, for example). Or they may not, at all.
Related
Hello I i'm having random flicker every couple of seconds when running my program, it has a single image moving across screen. I'm using Graphics paint() method and repaint() in the thread's run() method. Here are the relevant parts of the code, i'll post entire code if necessary. Btw, pawns is an arraylist loaded with pawn objects, originally I had 5 threads for 5 images moving across but I tried with only 1 image and it still flickers so it's not that.
private BufferedImage helicopter;
helicopter = ImageIO.read(new File("white.png"));
public void paint(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
for(count=0; count<pawns.size(); count++){
g.drawImage(helicopter, pawns.get(count).getX(), pawns.get(count).getY(), null);
}
g.setColor(Color.BLACK);
g.drawLine(350, 0, 350, 600);
}
public void run() {
while(true) {
randSleep = (int)(Math.random()*100);
randMove = (int)(Math.random()*2);
pawn.setX(pawn.getX()+randMove);
try{
Thread.sleep(40);
}
catch(Exception e) {
e.printStackTrace();
}
repaint();
}
}
On components with complex output, repaint() should be invoked with
arguments which define only the rectangle that needs updating, rather
than the no-arg version, which causes the entire component to be
repainted.
Swing's implementation of paint() factors the call into 3 separate
callbacks: paintComponent() paintBorder() paintChildren() Extensions
of Swing components which wish to implement their own paint code
should place this code within the scope of the paintComponent() method
( not within paint()).
source: Painting in AWT and Swing: Good Painting Code Is the Key to App Performance
You should notice in the source quoted and linked that repaint() (no arguments) will call update(), which by default clears the background before drawing. I suspect that this is the source of the flicker, when the component is cleared before calling paint().
If you are using Swing components you should not implement your own double buffering but instead use the functionality provided by Swing.
First try, calling a repaint with arguments to avoid update clearing the entire background. Or write an override for the update method. If that does not solve the problem next try putting your drawing code in the paintComponent method of a Swing component.
I try to optimize the drawing of my application. Currently i have implemented an animation as well as several GUI components. Some are separated exactly, some overlap each other. Currently i face a problem within the overlapping of swing components. A part of the GUI, which overlaps my animation, needs to draw a lot of Strings and - into an Jlist placed - common swing components.
In result the overlapping GUI becomes redrawn as often as the animation gets an update. I tried using quite a lot of different methods to make sure, whats drawn in front of each other. Things like GlassPane, Jlayeredpane. Unfortunately in any of these tries, the overlapping Menus paintcomponent method, which only need to become called as the user interacts with them, gets called frequently due to animation and causes a quite high cpu usage.
Ive tried to position the Menus in a lower order within the Layeredpane, i.e.:
getLayeredPane().add(map, JLayeredPane.DEFAULT_LAYER);
getLayeredPane().add(mapController, JLayeredPane.PALETTE_LAYER);
getLayeredPane().add(settings, JLayeredPane.PALETTE_LAYER);
getLayeredPane().add(painter, JLayeredPane.POPUP_LAYER);
During the paint process of the painter i tried to modify the area - i.e.:
#Override
protected void paintComponent(Graphics g) {
g2 = (Graphics2D)g;
g2.setRenderingHints(DefaultResources.getRenderQuality());
g2.clip(JMain.getInstance().getMapBounds());
...}
Well - as soon as the painter component !isOpague(); All components underneath become redrawn. Unfortunately if i do place the menus in a higher order, they as well need to become redrawn with any animation update.
Does somebody has any idea, how to avoid a permanent redraw of overlapping components with an animated component?
The only solution i have seen was using heavyweight containers. Unfortunately the relative positioning has also shown a behavior during moving purposes, which aren't appropriate.
Thanks for any kind of advice!!
Well, its pretty obvious that if you have overlapping non-opaque components all of them will be repainted on any changes in one of them unless you optimize your animation repaint calls to some specific rectangles, so there won't be any useless operations.
Let me describe how Swing works a bit more - all of the paintings you do in paint, paintComponent and other methods (which get called on each component repaint) are done onto subimages of a single image that holds a "cached" version of the whole frame interface.
Now imagine that you change something in your UI (add/remove/repaint component) - that final image (or atleast a small part of it that contains your component) must be properly updated. To do that in case your component is NOT opaque - all of sub-components will be repainted with your component bounds as a repaint rect to create a proper background for your component. If your component is opaque - it will be the only one repainted, but it also has to fill the whole bounds rect on its own, otherwise you will see awful painting artifacts behind your component on each repaint.
To summ up - to avoid pointless repainting of overlapping components there are a few approaches:
Optimize animation repaint calls to areas you actually need to repaint
Use opaque components, unless you want to paint something transparent in your component
Optimize all overlapping components painting operations so repaints won't take much time
There still might be more optimization approaches depending on your specific case, but you will have to find them on your own, since that is impossible without seeing the whole picture.
You can also find a lot of useful information about optimizations in Swing in this book: Filthy Rich Clients
Well in terms of optimization i do have a component, which is causing quite some trouble, but that one i have planned to rewrite. Therefore i just want to make sure, that painting areas are properly. Following that point, i have computed all necessary areas and filled them into a list, which i pass as data has changed.
Its working properly as long as i only have one rectangle applied. As soon as i pass a second one, it seems its y - extension becomes ignored. For instance:
[x=0,y=0,width=322,height=20]
[x=0,y=620,width=322,height=20]
There everything between y=20 and y=620 also gets redrawn.
for (Rectangle rec : clippingAreas) {
painter.repaint(rec);
}
Ok i have tried paintImmediately within the EDT, which does work for now, but i wonder if this is a proper way to go:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (Rectangle rec : clippingAreas) {
painter.paintImmediately(rec);
}
}
});
Ok there we go:
package animationissue;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
public class AnimationIssue extends JFrame {
JPanel a = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("map has become repainted");
}
};
JPanel b = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("menu as well");
}
};
public AnimationIssue() {
this.setSize(500, 500);
this.setLayout(null);
a.setSize(400, 400);
b.setSize(400, 200);
this.getLayeredPane().add(a, JLayeredPane.DEFAULT_LAYER); // Map
this.getLayeredPane().add(b, JLayeredPane.PALETTE_LAYER); // Menu
a.setLocation(0, 0);
b.setLocation(0, 100);
a.setBackground(Color.red);
b.setBackground(Color.blue);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
// doin some computations for animation
// cast a repaint after having finished new
//animation information i.e. interpolation
while (true) {
try {
Thread.sleep(2000);
} catch (Exception e) {
}
// case 1 - just a repaint of the whole component - triggering map redraw results in menu redraw
// a.repaint();
// case 2 - repaint of specified rectangle
// Either passing one - the menu does not get repainted, or passing both - menu also gets repainted
//a.repaint(0, 0, 400, 100);
//a.repaint(0, 300, 400, 100);
// paintimmediately works for now
//a.paintImmediately(0, 0, 400, 100);
//a.paintImmediately(0, 300, 400, 100);
// Just repainting Menu does not trigger map to become repainted, except if its opague, but then it should become repainted
b.repaint();
}
}
});
t.start();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
AnimationIssue f = new AnimationIssue();
f.setVisible(true);
}
}
I was really looking forward to optimize the behavior, that no menu gets redrawn, if its not necessary. You have to imagine the menu of being a component holding several JLists with a lot of String drawing tasks, which really have a massive impact on cpu usage. I havent been wondering, since it gets redrawn approximately 25 times per sec.
I am just uncertain for the current solution, if using paintImmediately is proper. Apart from this - if you or somebody has an alternative - better - way to prevent useless redrawing (i really thought Glasspane or JLayeredPane or isOptimizedDrawing or isOpaque might help out), i am really thankful.
Best regards.
The following code just brings up a canvas in a window which just fills its content with red. However, when resizing the window it flickers a lot, because before each repaint the canvas appears to clear itself. After a bit of trivial searching it seemed to be because the update() method called g.clearRect(), but I've overriden that now and the flicker remains there, the canvas is still clearing itself before repaint.
I've played around with double buffering and that as far as I can tell that doesn't seem to fix things - I'm not sure how helpful it'd be anyway since the issue appears to be more with preventing the canvas from clearing before its repaint.
As an aside before everyone rushes in and suggests it, I have to use Canvas in this instance, not JPanel, because at a different point in time I'm using the same Canvas for native video playing with VLCJ.
public class MyCanvas extends Canvas {
#Override
public void update(Graphics g) {
paint(g);
}
#Override
public void paint(Graphics g) {
//By the time we get here, the canvas has been cleared to its background colour
g.setColor(Color.RED);
g.fillRect(0, 0, getWidth(), getHeight());
}
public static void main(String[] args) {
JFrame frame = new JFrame();
MyCanvas c = new MyCanvas();
frame.add(c);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Double buffering, as expected, turned out not to be the issue - either way the canvas was cleared before painting from code that seemed to be deep in the internals of the AWT library.
After tracing through the relevant internals, it seems that the "clear before paint" behaviour can be overridden by a property:
System.setProperty("sun.awt.noerasebackground", "true");
Adding the following stopped the background from being erased on the canvas before repainting, and therefore the associated flickering.
It should be noted this property is obviously Sun VM specific, so no idea if you get similar behaviour or not on another VM. It does however work perfectly for my use case.
I have the following code being called:
while(true){
view.onTick();
trySleep(55);
}
The onTick() method is described as such:
public void onTick() {
mainFrame.paintAll(mainFrame.getGraphics());
}
Here is where I set up my JFrame and JPanels etc (mainFrame is a JFrame):
private void runProgramSetup(){
JPanel canvas = new JPanel();
canvas.setLayout(new BoxLayout(canvas, BoxLayout.Y_AXIS));
mainFrame.getContentPane().add(canvas);
//create the main game panel
mapPanel = new MapPanel(model, this);
mapPanel.setPreferredSize(new Dimension(TOTAL_FRAME_WIDTH, MAP_PANEL_HEIGHT));
mapPanel.setBackground(Color.cyan);
//create the menu panel
menuPanel = new MenuPanel(model, this);
menuPanel.setLayout(new BoxLayout(menuPanel, 0));
menuPanel.setPreferredSize(new Dimension(TOTAL_FRAME_WIDTH, MENUS_PANEL_HEIGHT));
//add the panels to the window
canvas.add(mapPanel);
canvas.add(menuPanel);
//make both panels visible
mapPanel.setVisible(true);
menuPanel.setVisible(true);
}
Now here is my problem. Everything repaints when repaintAll() is called EXCEPT mapPanel's overridden paint(Graphics g) method:
#Override
public void paint(Graphics g) {
transformedImages.transformAndStoreImages(model);
paintGrid(g);
paintScenery(g);
paintElements(g);
paintDraggedElement(g);
paintUIOverlay(g);
}
It is only called once. That is it. However, every other component continues to repaint. It is only mapPanel that paints once. Here is what is even more strange. I am running on Ubuntu and get this problem. The rest of my team is running on Macs and they do not have this problem. The only way I have been able to solve this is to replace onTick() with two paint calls:
public void onTick() {
mainFrame.repaint();
mainFrame.paintAll(mainFrame.getGraphics());
}
This is all that has worked for me. I need both calls. Neither works alone. I don't like doing this though obviously because of inefficiency.. :/
Any ideas?
Thanks!
you should be overriding JPanel's
paintComponent(Graphics g)
not paint
The reason mainFrame.repaint() forces the map to refresh is because repaint() calls repaint(0, 0, 0, width, height), which marks the entire mainFrame's area to be marked as "dirty" for the RepaintManager. This is designed this way on purpose, because you usually do not want to repaint every pixel in the JFrame just to update a component. So, in onTick(), when mainFrame.paintAll() is being called, my guess is that the mapPanel's area has not been marked dirty, so the RepaintManager skips it, to save processing time. If you are very sure that you want to repaint the whole mapPanel every time onTick() is called, the simplest way would be to call mapPanel.repaint() inside of your onTick() method. This will mark the whole mapPanel as dirty so it will be redrawn asap. Also, if your menuPanel is just using regular swing JComponents, you don't need to manually cause them to repaint, they will be repainted when their values change, if you are using the API correctly.
I know this is a kind of old question, but I figured I'd answer in case anyone else runs into something similar.
I'm currently using an animation engine I designed that takes objects of type Drawable and adds them to a List. Drawable is an interface that has one method:
public void draw(Graphics2D g2d);
The extending animation manager iterates through this list and calls the draw() method on every object, passing the Graphics2D object obtained from the Swing component.
This method seemed to work well at first, but as I feared, it seems to be unable to handle multiple objects in the long run.
With merely two Drawables registered, both drawing images on screen, I'm seeing a bit of flashing after 30-60 seconds.
Is there a way to optimize this method? It currently calls upon the AWT thread (invokeLater) to handle all of the tasks. Concurrent drawing isn't really an option as this nearly always causes issues in Swing/AWT, in large part due to the fact that Graphics isn't synchronized.
If this just simply is a bad way of animating, what is a better method when you have multiple objects that all need things rendered with their own specific variables cough game cough?
Any help would be appreciated. Thanks!
EDIT:
I can't use repaint() beacuse my engine already calls the AWT thread to paint stuff. If I call invokeLater from the AWT thread, the image never gets painted for some reason.
I should also add that I'm using a system of ticks and fps. 60 ticks # 120 fps.
Each tick updates the game logic, while each frame render calls draw on the frame manager.
Is this a bad idea? Should I just use FPS and not ticks?
I think it would be more appropriate to override paintComponent(Graphics g) and regularly call the repaint method on the JPanel or whatever you're drawing on with a Timer. Your problems may be due to to you trying to draw and then Swing doing it's own draw.
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel() {
public void paintComponent(Graphics g) {
//draw here
}
};
panel.setPreferredSize(800, 600);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true)
new Timer(16, new ActionListener() {
public void actionPerformed(ActionEvent event) {
panel.repaint();
}
}).start();
}
}