Invisible JFrame/JTable how much faster? - java

I have a swing app. with a jframe with lots of internal frames containing large JTable.
Those jtables get updated continuously so there is lots of repainting going on.
in some circumstances I can simply keep the JFrame invisible. (frame.setVisible(false))
I was wondering if anybody knows if I will gain something in terms of performance
(something considerable or not)
such as 50% gain or you would only get 2% gain...
and maybe some sort of explaination on what to expect.
thanks
p.s.
Another way to rephrase the question is:
Are swing components clever enough not to repaint/reflow theirselves if not visible ???

Take a look at the Swing Painting Guidelines which have some useful tips on painting efficiency. For example:
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.
Components which render complex output
should make smart use of the clip
rectangle to narrow the drawing
operations to those which intersect
with the clip area.
It's also quite easy to prove that non-visible components are not painted. The following code hides a panel and prints out if paint is called.
public static void main(String args[]) throws Exception {
JFrame f = new JFrame();
final JPanel p = new JPanel(){
public void paint(Graphics g){
super.paint(g);
System.out.println("IN PAINT");
g.fillRect(10, 10, 20, 20);
}
};
f.setLayout(new BorderLayout());
f.add(p, BorderLayout.CENTER);
JButton b = new JButton("OK");
b.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("PRESSED");
p.setVisible(!p.isVisible());
}
});
f.add(b, BorderLayout.SOUTH);
f.setSize(100,100);
f.setVisible(true);
}

I believe you should temporarily disconnect the TableModel from the JTable while you are not displaying the JTable.
The biggest problem you face is that the JTable reacts to model change events and continuously repaints itself.
It is also possible that the JTable is intelligent enough to not repaint itself while not visible (but has model changes) but I wouldn't put my money on this.

The best way to solve this question is to test both. If you profile both options and see no gain then you try a different route like sola suggested. Just make sure you are not optimizing the wrong part of your application.

I can't give you any numbers on how much performance gain you will see, this depends on too many factors and is best left up to your own profiling. However, if your component is not visible, then the paintComponent() (or repaint()) method won't be called either as far as I know.

If your JTable is in a JScrollPane it seems it will repaint at each line adition (and call to fireTableRowsInserted of your model). Thus because the scrollpane will update it scollbars and then its contained component (JTable). If your table is not in a scrollpane then it will redraw only the shown lines. Therefore if your lines are not shown you will not trigger a repaint... Stupide. I'm looking for a way to avoid this.

Related

animation pipeline using swing

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.

JPanel Obscures BufferedImage

I'm having trouble finding out why the following problem happens: In a program that uses "extends Frame" to create a window, I can use BufferedImage to draw to the graphics context of the Frame (not JFrame), and it looks just fine. However, the moment I declare a JPanel, all of the text drawn by BufferedImage becomes obscured (not completely, but semi-transparent and hard to read), even if I don't add the JPanel to the JFrame.
Here's a simplified version of the code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.Timer;
import java.awt.image.*;
import javax.swing.*;
public class MyProgram extends Frame {
static Frame f;
static Timer timer;
public static void main(String[] args) {
f = new Frame();
f.setSize(400, 200);
f.setResizable(false);
f.setVisible(true);
f.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
drawScreen();
}
});
drawScreen();
}
public static void drawScreen() {
BufferedImage off_i = new BufferedImage(f.getWidth(), f.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = off_i.createGraphics();
g.setColor(new Color(50, 50, 50));
g.drawString("Hit any key; why does this text change?", 15, f.getHeight() - 10);
Graphics off_g = f.getGraphics();
off_g.drawImage(off_i, 0, 0, null);
JPanel panel = new JPanel();
}
}
I could maybe understand seeing the problem arise if I had added the JPanel to the JFrame and didn't set any bounds to its visibility, but even creating the JPanel gives that issue, which seems weird to me.
Basically, what I'm trying to do here is take an existing program that I have that runs just fine without JPanel, and I want to add to it a JTextArea so that I can accept copy/paste text for modifying the displaying of the program.
My understanding of Java is kind of spotty, as I learned it mainly by hobby and not formally, but I'm always looking to learn more when I can. Thanks for the help!
Update: I discovered that this problem only happens when the draw function is called again, after the JPanel has been declared, though I still don't understand why it does that or how to get around it.
better would be put Image to the JLabel and how ot use Icon
please read Using Swing Components and how to LayoutManagers works
tons examples on java2s.com
Don't mix AWT components with Swing component. That is you should use a JFrame NOT a Frame.
Don't use getGraphics(). Custom Painting is done by overriding the paintComponent() method of a JPanel (or JComponent). You just use the Graphics object that is passed to the method. Then you add the panel to the frame.
As already mentioned using a JLabel is simpler because you don't have to do any custom painting. The tutorial also has a section on "How to Use Icons".
I tried to run your code. And although the effect that you are describing does not happen on my system I can recommend you something.
First try to create your panel before it is visualized. In this case java does not have to re-arrange the components that are already on screen.
Second, if you have to draw things on visible frame call validate() of the container when you are done. This makes java to re-arrange stuff.
Third, when you are using drawXXX() methods create your own class that extends Component, JComponent, Canvas and override its `paint(Graphics) method. In this case the system will call this method every time it needs (e.g. when window is resized) and your UI will be painted again.
BTW I have 2 questions:
why are you using drawText() instead
of Label or JLabel? Use them and
avoid such kind of problems.
Why do you extend your class from Frame and do not use this fact but create yet another instance of Frame?
As an answer to my original question:
It seems that initializing JPanel alongside awt draw() commands causes the text to be antialiased, which makes the text look harder to read, partially obscured, thinner, etc. Although I tried setRenderingHint() with VALUE_TEXT_ANTIALIAS_OFF, it did not solve the problem. But as other posters pointed out it's not best practice to mix the two components.
While this doesn't exactly solve my problem, it does answer the question of what is going on, that being text antialiasing as some result of JPanel (does that sound right?). Ideally I wouldn't want to rewrite all of the code just to add a single JTextArea into an already existing codebase. But perhaps it's good every now and then to revisit old code and revamp it where it may be faulty.
Thanks everyone for the comments, information, and resource links!

Swapping panels in a frame

So i have a frame and 2 panels, originally panel A is displayed in the frame. i have got it set up so when a button is clicked in the panel it makes a call back to the frame and runs this bit of code. ("splash" is the panel that is originally displayed and "game" is the panel i want to show after the button press)
public void startGame()
{
System.out.println("starting game");
remove(splash);
gameThread = new Thread(game, "game thread");
gameThread.start();
add(game);
/*
this.setSize(0,0);
this.setSize(450, 450);
*/
}
anyways this seems to remove the one panel fine but wont display the second panel until you resize the window. the commented section seems to solve this problem but i cant help think there is a better way to do this. i have surfed the API and haven't found anything helpful.
if anyone knows a better way, or has run into this problem. please share your knowledge, this one has me stumped.
You should use a CardLayout for this kind of thing instead of removing and adding panels.
The container holding the panels needs to be invalidated, validated and repainted (all on EDT). For example, if the frame is the holder then
frame.invalidate();
frame.validate();
frame.repaint();
This is of course after the target panel has been added.
As Michael pointed out CardLayout is a good way to go.
The specific functionality you are looking for is Container.validate(). It will "cause the container to lay out its subcomponents again. It should be invoked when this container's subcomponents are modified ... after the container has been displayed."
Right now, i really don't remember how since i haded the same problem you have, but, have you try to use:
this.setVisible(true);
That might work

The contents of my JComponent only refresh after a manual resize

I am trying to figure out why my JComponent refreshes when I manually drag my window, but it doesn't refresh when I call repaint or revalidate. The data is ready to be displayed, but it just won't show until I manually resize. Can anybody give some suggestions about what I can do or does this sound like it isn't a Swing problem since I tried repaint and revalidate?
One weird things I've noticed is that if I have this code:
sp.setSize(sp.getSize().width, sp.getSize().height+1);
sp.setSize(sp.getSize().width, sp.getSize().height-1);
If the first line is used, then the JComponent will refresh itself. If I use none or both of these lines it will not, which seems bizarre to me.
I am basically just putting a JPanel in a JInternalFrame in a JDesktopPane. There are two main functions for what I am trying to do. One adds the new JPanel and the other tries to refresh it so the new data will show:
public void addNewSP()
{
sp = new JInternalFrame("SP");
sp.setClosable(true);
sp.setLocation(700, 400); //this should be changed to something based on screen size
sp.setResizable(true);
sp.add(popUp);
this.parentContainer.add(sp, JLayeredPane.DRAG_LAYER);
sp.pack();
sp.show();
sp.setSize(500, 500);
sp.setPreferredSize(new Dimension(500, 500));
}
public void refreshSP()
{
sp.repaint();
sp.validate();
sp.repaint();
sp.validate();
parentContainer.validate();
parentContainer.repaint();
sp.setSize(sp.getSize().width, sp.getSize().height+1);
sp.setSize(sp.getSize().width, sp.getSize().height-1);
}
}
BTW parentContainer is the JDesktopPane
When changing the container's content, you have to call both:
revalidate() to make it recompute the layout for its content
repaint() to request a repaint for this container
but it just won't show until I manually resize.
We don't know the context of your question, which is why a SSCCE should always be posted as suggested earlier.
In general a JComponent, does not have a preferred size, so I'm guessing Swing doesn't think it needs to paint the component. When you resize the frame, chances are the component was added to the center of a BorderLayout so it automatically gets sized to fill the entire space of the frame.
The solution is to give your component a "preferred size" so that any layout manager can use this information to display the component properly.
if your are modifying container's subcomponents you should call jcomponent.validate();
I assume parentContainer is the JDesktopPane?
What kind of changes are you making to sp that are not showing up?
Changing the size of sp will cause Swing to repaint from scratch. That's why the setSize() is fixing the display.
Most likely, the changes you are making are either not happening on the EDT, or are not invalidating the right container. For example, if you change the visibility of a component in sp, you'll need to call sp.invalidate() to rerun the layout manager.
Have you checked that you're only changing components (or their models) on the EDT?
A quick test for that is to run with the Substance LAF as it will complain if you change things on another thread.

How would you make a frame visible if the mouse is inside it and invisible when it's not in Java?

So if I was writing pseudo code:
if(mouseInsideFrame==true)
frame.setVisible(true);
else
frame.setVisible(false);
What method would I use for the mouseInsideFrame in the if statement?
Thanks
I came across a post on java.net that covers visibility options, including this one using a private AWT API.
public class TransparentFrame {
private static final float OPAQUE = 1.0f;
private static final float TRANSLUCENT = 0.1f;
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 400);
frame.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
com.sun.awt.AWTUtilities.setWindowOpacity(frame, OPAQUE);
}
#Override
public void mouseExited(MouseEvent e) {
com.sun.awt.AWTUtilities.setWindowOpacity(frame, TRANSLUCENT);
}
});
frame.setVisible(true);
}
}
This is OK for toy code, but obviously I wouldn't use a private com.sun class for portable production code.
Update: Same method as before, but with more explicit step-by-step explanation.
Other respondents wonder what you want to achieve with this and question the design behind an app that unexpectedly jumps up at the user. I guess it's a matter of how dead set you are to implement this functionality exactly as you described. The technique itself may be useful for other purposes too, which is my main motivation for my new, improved revision of this answer.
As far as I know, a frame that's not visible can't capture mouse events. So it won't know if the mouse is inside it or not.
There's a loophole around this problem: If you make your frame visible but borderless and fully transparent, it will be visible in the technical sense but invisible to the user for practical purposes.
The borderless part is easy: setUndecorated(true).
It would be great if JFrame had a method like setOpaque() or setTranslucent() where you could make it fully transparent, but alas, it doesn't.
Another answer mentions a solution based on a Sun private class which does permit you to make the window transparent. This will work for current and probably near-future Sun JREs but is far from guaranteed to work with other and future JREs, so I can't recommend it. Sun expliticly advises against using their private classes this way.
There's an alternate, somewhat hacky alternative: The frame is left fully visible but it displays the image of a screenshot of the screen behind it. Because this means we're effectively looking through the frame, it's effectively invisible. This solution is described here: http://onjava.com/pub/a/onjava/excerpt/swinghks_hack41/index.html?CMP=OTC-FP2116136014 . The author and I both admit to this being a a bit clumsy; it also involves a lot more code than should be necessary. But it's based on standard Java coding and should be supported unchanged in many Java environments upward of about version 1.4 or so.
The tip describes how to create a Component that displays the screen background. That's fine for when you want the frame to be invisible, but what happens when you want it to be normally visible?
The thing to do is to give the JFrame's ContentPane a CardLayout and add both the TransparentBackground component and your intended main visible component (likely a JPanel) to it. With that set up, switching between "invisible" and visible involves simply:
setUndecorated(false) // (true)
cardLayout.last() // (first)
This switching, of course, will be controlled by a MouseListener you can add to the JFrame.
Out of the top of my head, there is a fairly easy way to get the position of the mouse on the screen (I think it has something to do with Toolkit). Now, if you can combine that with a way to find out your frame's position on the screen (if you don't already know), you have your solution.
I'm curious what you're trying to do though.
Given what you are trying to do, I would say you need two frames (or perhaps just JPanels and frame that does a lot of changing. One is to capture the mouse moving over it (make it transparent, undecorated or otherwise acceptably out of the way) and when the mouse moves over it, show the new frame (or panel) and then hide that when the mouse moves out of it.
The other answers here give you the basics on how to capture the mouse events and set the frame undecorated and transparent.
Normally you could use listeners. Specifically:
frame.addMouseListener(new MouseListener() {
public void mouseEntered(MouseEvent evt) {
frame.setVisible(true);
}
public void mouseExited(MouseEvent evt) {
frame.setVisible(false);
}
});
But the problem is that since your JFrame is not visible, there is no way to listen to mouse events!!!! At least, from what I know....

Categories

Resources