So I'm making this program with a GUI and I haven't worked with Swing/SWT too much but a little bit to know what's going on.
Anyway, I add an actionlistener for a button so it'll add an image to the contentPane when I click on the button but it doesn't work unless I have it as a JComponent (as seen below) and add my other things (button, JLabel, etc) to it afterwards...AND set this JComponent to the content view (which doesn't make sense).... I've also tried making it extend JPanel and just clearing out original contents and re-adding them to the new JPanel. The thing is, when I do this it recreates the text for my JLabel in a weird way, and I just know there's gotta be a simpler, more efficient, way.
class ShowImage extends JComponent{
public ShowImage(){
super();
monkey = Toolkit.getDefaultToolkit().getImage(("D:/monkey.png"));
}
public void paintComponent(Graphics g){
g.drawImage(monkey, 20, 100, null);
repaint();
}
}
Do not invoke repaint inside paintComponent
Invoke super.paintComponent and then draw the image
Also, depending on the layout manager, this component will have a preferred size of (0, 0), and therefore will not be visible.
For more information, see 2D Graphics.
Edit -
Note that dynamically adding a component will force you to revalidate the container and issue a repaint request so the layout manager will layout its components again and remove any visual artifacts. Also, for more information regarding images, see Working with Images.
Anyway, the simplest approach would probably be to set the image as the icon of a JLabel instance and add that to the container. There's really no need to reinvent the wheel here.
g.drawImage(monkey, 20, 100, this);
..would most likely have fixed the problem in the original code. It was a combined problem of:
Loading the image in an asynchronous way. (Toolkit.getImage() as opposed to ImageIO.read().)
Painting it to a 'blinkered' ImageObserver. The JComponent implements ImageObserver. As soon as the image is totally loaded (as well as a few points before that), the observer will be informed, triggering a repaint().
Related
I've tried to paint component to PDF. I've got itextpdf 4.2 and everything works perfectly.
But this works only if I make visible the frame that I've tried to render.
The similar question that I've found is How to paint an invisible JFrame elsewhere? that has the same issue, but the solution wasn't provided in answer.
A little of code.
I've created a JFrame and insert main view that I want to render.
JFrame jframe = new ShowingFrame();
jframe.setPreferredSize(new Dimension(PDFHelper.getOriginalWidth().intValue(), PDFHelper.getOriginalHeight().intValue()));
jframe.setMinimumSize(new Dimension(PDFHelper.getOriginalWidth().intValue(), PDFHelper.getOriginalHeight().intValue()));
jframe.add(view);
jframe.setUndecorated(true);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setState(Frame.ICONIFIED);
jframe.setState(Frame.NORMAL);
//jframe.setVisible(true);
If I uncomment jframe.setVisible(true) than everything works.
But user will see this frame that I want to avoid.
So the question is: How to paint hidden control?
Inside Swing Component.java class all paint methods first check if the component is visible:
public void paint(Graphics g) {
if (isShowing()) {
// rest of the code...
}
}
I've tried to create inherit class ShowingFrame extends JFrame, that override isShowing and always return true. But this not helps to.
Swing (and the Java Graphics API) is optimized to stop rendering as soon as possible.
So the solution is to create a BufferedImage, get the Graphics instance from it and then call component.paint(g); with it.
Now you have a tab component. Try to get the content of the tab instead of rendering the tab itself. If that doesn't work, you can try to clone the tree of children, create a new JPanel, attach the children and render the result. But cloning can become tedious if the models don't behave well.
See this question for some code: Swing: Obtain Image of JFrame
Why do you want to paint something that is not visible? Your computer does not want to waste CPU cycles rendering graphics that can't be seen. In fact, there is a lot of computations done to see what parts of each window are visible and only paint the visible parts (the so called clip window).
If you want to paint something so you can use it later or save it you can always create a BufferedImage of the size you want and paint to that.
If I uncomment jframe.setVisible(true) than everything works. But user will see this frame that I want to avoid.
You can set the frame location so that it is not visible on the screen. Maybe something like:
frame.pack();
Dimension d = frame.getSize();
frame.setLocation(-d.witdh, 0);
I have a swing application in which I display images in a JPanel. If the app is unable to produce the image I want to remove the previous one from the JPanel and replace it with a JTextField and message. I can add the text field , but it's drawn on top of the previous contents, which is itself a subclass of JPanel. Here's what I have:
private void displayMessage(String message) {
JTextField tf = new JTextField(message);
cdPanel.removeAll();
cdPanel.add(tf, BorderLayout.NORTH);//tried lots of variations, inc. no layout
cdPanel.validate();
}
How can I get cdPanel to completely redraw itself?
You can simply try calling :
cdPanel.revalidate();
cdPanel.repaint(); // This is required in some cases
instead of
cdPanel.validate();
As you are dealing with unpredictable latency, use a SwingWorker to do the loading in the background, as shown here. The example uses pack() to resize the label to that of the image, but you may want to use a fixed-size grid and scale the images, as shown here.
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.
Is it possible to draw on a JFrame without adding a JPanel to it?
i override paintComponents() but it didn't show anything.
#Override
public void paintComponents(Graphics g) {
super.paintComponents(g);
g.drawString("for test", 10, 10);
}
Just in case anybody still insists of painting on the top-level Window directly (which is not recommended), here's how (because the code snippet linked to in the other answer is simply wrong)
JFrame frame = new JFrame("funny ...") {
#Override
public void paint(Graphics g) {
super.paint(g);
g.drawString("for test", 150, 150);
}
};
frame.getRootPane().setOpaque(false);
((JComponent) frame.getContentPane()).setOpaque(false);
Obviously, to make it shine-through all the way up, everything above (in Z-order) has to be not-opaque.
Cheers
Jeanette
Yes, it is. You'll want to work with the one of the panes in the JFrame such as the content pane or the glass pane, which you can access via getContentPane, etc.
For example the content pane is a Container, with a variety of add methods. To that you can add any Component - doesn't have to be a JPanel specifically. More at Using Top Level Containers.
Usually, though, drawing is done via overriding paint (for AWT) or paintComponent (for Swing). This means you need some sort of Component or JComponent that you put in your frame. More at the 2D Graphics tutorial. Why do you not want to change that?
You can also override JFrame and its content pane and have a content pane with an override paintComponent method.
I question, however, the necessity and wisdom of directly drawing on a JFrame.
It seems to be possible. Check this previous SO post to see how it can be done.
JFrames have a GlassPane on top of them, which can be used for graphics. Here you have a simple example that shows how to use it.
In short, my need is to have a background Image in my java app, and upon some event, create some other graphics on top of that image.
I was thinking I would use a JPanel to draw the background image in, add it at to my JFrame the start of program, and then add other JPanels on top of that upon certain events. The problem is Swing gives the JPanels added first the highest Z index, so what should be my background is showing up on top of everything.
Is there any way to control the Z index/order of the JPanels, or am I going about this completely wrong?
You can use the setComponentZOrder() to handle Z-Order in your application.
Resources :
JavaDoc - Container.setComponentZOrder
oracle.com - Mixing heavy and light components
Sounds strange to add mutiple JPanels and use z-order. I would suggest you either simple add ONE JPanel with the paintComponent(Graphics g) method overwritten, where you draw the correct image according to your events.
Or use the CardLayout, and add JLabels (with different images), then when your event triggers, use
CardLayout cl = (CardLayout)getLayout();
cl.show(this, "card3");
to show the correct JLabel.
The JLayeredPane is designed for just this purpose. It allows you to control the Z-order of components.
I was thinking I would use a JPanel to
draw the background image in, add it
at to my JFrame the start of program,
Sounds reasonable.
and then add other JPanels on top of
that upon certain events. The problem
is Swing gives the JPanels added first
the highest Z index, so what should be
my background is showing up on top of
everything.
Adding a panel to a panel is just like adding another component to the panel. The child component will be painted on top of the parent panel. There is no need to play around with Z-Order.
The key is to set a layout manager on the panel with the image. Then each panel you add to the image panel must be made non-opaque so that the background image will be painted.
Maybe the Background Panel can help you out. It automatically makes any component added directly to the panel non-opaque.