I know that JPanel is, by default, automatically double-buffered. However, I have a particularly time-intensive painting operation in my panel, but the panel only needs to be repainted when the underlying data changes, which is rare. Therefore, I'd like to reuse the JPanel buffer instead of having it clear after every call to repaint().
I've manually implemented a "dirty" flag on my JPanel subclass, but I have no idea how to cancel a paint operation once it's been started. I can't avoid the call to repaint in the first place, since my panel is inside a JScrollPane, which is being repainted every time it's resized (which does happen frequently), which causes my custom panel to be repainted.
Is there any way to do this without manually buffering the panel? If not, what's the recommended method for implementing a manual buffer in conjunction with a JPanel?
I have a particularly time-intensive painting operation .. only needs to be repainted when the underlying data changes, which is rare..
Paint the data to a BufferedImage, display it in a JLabel. Call label.repaint() if it changes. E.G. as seen in this answer.
Bonus Showing an image in a label is a way to get a GUI with a preferred size, that does not need to extend anything. To get the perfect size for the frame or dialog that displays it, call pack().
Related
I am using JPanels to simulate a print preview and just printing the content panel, however I have ran into a problem whereby if I try to print multiple panels that are essentially the same document, only the the one currently being displayed on screen will print.
Is there a way I can force the JPanel to repaint even if it is not currently on screen?
I have tried:
Disabling double buffering via:
JComponent.setDoubleBuffered()
RepaintManager.setDoubleBufferingEnabled()
Painting Twice
Painting through paint()
Painting through repaint()
Painting through print()
I couldn't get it to paint off screen and I felt like I was wasting my time battling the Swing API so I just cheated and made the panels display on screen as they are being printed; now I have a new "feature" that shows you the page being printed.
I'll leave the question open incase someone knows how to do this as I would prefer not to shove all the pages in the user's face.
Update
Turns out the problem was with components that had extended java.awt.Container, the Swing components must override certain AWT methods that deal with this sort of rendering.
When is the size of the JComponent is calculated? After being shown in the screen or before that?
if I send .getSize() message before .setVisible(true), would it give me the right answer?
Thanks
I sometimes check the sizes of my components when debugging to find out why I can't see them for instance. In most cases, the sizes will be realized when the GUI has been rendered. This can occur when pack() or setVisible(true) has called on the top-level window. My usual sequence of method calls is to call pack() first as this tells the layout managers to lay out the components that they are responsible for, and sets the sizes of the components and the GUI, then call setLocationRelativeTo(null) to center my GUI, then call setVisible(true) to display it.
The layout manager is responsible for determining the size of a component, so you don't know its actual size until the component has been added to the frame and the frame has been pack()ed ore made visible.
If you use a layout manager that respects the preferred size of a component then you can use:
component.getPreferredSize();
Why do you think you need to know the size? Generally you don't worry about sizes and let the layout manager do its job.
In addition to the usual pack() > setVisible(true) > getPreferredSize() sequence, you can validate() the relevant Container to preview the geometry, as shown here.
If I understand properly, the reason why you want to know the size of a component is to reset the size of a JWindow once a user click on the "More options" button, isn't it?
I would suggest to do the following: when the user clicks on that button, update your UI adding the extra component, and the execute pack() on the JWindow. It should resize to the proper size.
I've implemented a custom JPanel, whose paint method I've extended to do a lot of manual rendering in full screen mode. Now I would like to integrate another JComponent to this (in my case a JPanel that contains a JScrollpane with a JTextPane as its viewport) that should appear on top of my first panel, but because my custom rendering pipeline is complex, adding the JComponent to my panel and having it painted the traditional way through the AWT system is not an option (I tried and it's quirky at best, not functional at worst), so my question is: is it possible to manually order the JComponent to be painted at one point in my program by calling its regular paint method without tying it to a JContainer and if yes, how do I do this?
Thanks in advance for your answers.
See the LabelRenderTest.java source on this thread. The label is eventually drawn to screen, but it is painted to BufferedImage before ever being displayed.
The important line of the source is..
textLabel.setSize(textLabel.getPreferredSize());
You can take a look at CellRendererPane and see how for example BasicTableUI paints component images with it.
Yes, just call the normal paint method on the object and pass the Graphics you want it to paint on. However, this is just going to paint it and it sounds like you want it to possibly scroll which means you will need to add it to your custom JPanel. In that case just add the panel and you a layout manager that will place the component where you need it.
You should set size for the component. Then to position it use your Graphics' translate(x,y) to position the component in desired Point.
if there is any container higher level in the hierarchy you can use
validate(); repaint();
pair to do that.
if not you can change it's size or bounds ( like +1 , -1 ) at the end to make it repaint itself.
How to use paint() such that when repaint() is called the previously drawn object should not get deleted when drawing the new object. That is the drawing area must get UPDATED only and not REDRAWN.
In my code when one of many button is clicked, some aplhabet get displayed. I want to have functionality such that when other buttons are clicked the previously drawn alhabets must be present.Take is as if a string of alphabets getting created as the buttons are clicked.
Im using Java Swing for coding.
Some piece of my code:
if(source == btnAlpha[i] )
bollyDraw.repaint(); //bollydraw is an object of a JPanel extended class having implemented the paintComponent(Graphics g) method
In the paint() method:
if (word[i] == key) {
g.drawChars(word, i, 1, x, y);
}
In a project I worked on I used a List to store the objects that were to be drawn as a member of a class. Then as the user interacted with my UI I added/removed items from this list. The JPanel that rendered the items painted the items that were in this list. It's helps separate the UI logic from the paint logic as you can determine what goes into the paint list when an event is fired rather than in the paint method ( which should be as clean as possible ). However this will force you to repaint everything on every paint call.
In conjunction with this Kim's RepaintManager is a great way to limit what gets repainted. It is region based so there is some complexity in determining what region of the screen has changed. If you have the time it is better to use something like this otherwise it could be difficult to add this functionality down the road.
Your app must be prepared to re-paint anything it has painted. From your description, I'm afraid that means you have to keep track of everything you've painted.
The Swing behavior is partially dictated by the underlying window manager and graphical system, which at any time may chose to paint over an area where your application is present. You can override update() to control repaints initiated by your own app, and might be able to improve performance by implementing your own RepaintManager.
This tutorial explains Swing painting in more detail: http://java.sun.com/products/jfc/tsc/articles/painting/
Custom Painting Approaches shows a couple of ways to do this depending on your exact requirement.
I'm using a JFrame in which the CENTER portion of the BorderLayout is occupied by a JScrollPane that wraps around a JPanel. What I'm finding is that when I initiate the action that actually causes the JPanel to be displayed, the display doesn't change. But when I resize the JFrame, the new JScrollPane has now magically appeared.
So what methods are called when you resize a JFrame? If I know, then I can call it in the code and avoid having to resize the frame just to see the results of the operation.
Its been a little bit since I've done swing, but from memory, calling validate() on the panel should do the trick. This will cause it and its children to have their layout calculated which is when the scrollbars decision is made. If that doesn't work, try calling validate on the frame's content pane. This is a little more costly, but may be needed if other components are being considered.