So, I have a JLayeredPane (technically a class subclassing JLayeredPane actually). On that is a JPanel. I want to add a BufferedImage to the Jpanel.
public class BigMap extends JLayeredPane implements MouseListener
JPanel mapPanel;
BufferedImage theMap;
public BigMap (BufferedImage m){
theMap = m;
mapPanel = new JPanel();
add(mapPanel, 0);
mapPanel.setBounds(0, 0, 640, 640);
//other unimportant stuff
}
#Overrride
public void paintComponent (Graphics g){
super.paintComponent(g);
Graphics2D gmap = (Graphics2D) mapPanel.getGraphics();
gmap.drawImage(theMap, null, 0, 0);
//some other stuff which is working just fine
}
The issue is that the BufferedImage isn't displaying. The JPanel is definately present as I can set its backgroundColour and see it if I wish. I realise that JLayeredPane doesn't have a layout manager and have had to set the bounds for the JPanel but that shouldn't be an issue for the JPanel itself, surely? And given that BufferedImage lacks methods to control its size directly I don't see how I'd overcome that if it were.
Any help appreciated.
The problem here is that you override the paintComponent() method of your layered pane, not the JPanel. The JPanel will paint itself later, as one of the children of your layered pane, and this will wipe out what you painted.
In general, a paintComponent() method should paint into the Graphics that was given to it, not into some other component's graphics.
Related
I wish to draw an HUD of sorts over a 3D OpenGL view, but it seems any drawing done in my panel will be overlooked, although it is done.
Here's some barebones code.
MyFrame;
public class MyFrame extends JFrame{
private static final long serialVersionUID = 1L;
public MyFrame(Labyrinth l){
super();
this.setTitle("My Frame");
this.setSize(512, 384);
this.setContentPane(new MyPanel());
//this.setVisible(true);//If needed here.
}
}
MyPanel;
public class MyPanel extends JPanel {
private static final long serialVersionUID = 1L;
public MyPanel(){
super();
this.setLayout(new BorderLayout());
MyCanvas mc=new MyCanvas(l);
mc.setFocusable(false);
this.add(this.mc, BorderLayout.CENTER);
//this.revalidate();//Doesn't seem needed in the instanciation.
}
public void paintComponent(Graphics g){
super.paintComponent(g);
this.mc.repaint();
g.setColor(new Color(128,128,128));
g.fillRect(0, 0, this.getWidth()/2,this.getHeight()/2);
//top-left quarter should be greyed away.
}
}
MyCanvas;
public class MyCanvas extends GLCanvas{
private static final long serialVersionUID = 1L;
public MyCanvas(){
super(new GLCapabilities(GLProfile.getDefault()));
}
}
The painting takes place, but isn't shown in the view. I've tried overriding repaint(), paint(Graphics), paintComponent(Graphics) and update(). I've been said that painting over "heavyweight" components was complicated, and that I should either paint directly in the component or use another type. I obviously need the GLCanvas to show a 3D render, and at the same time it does not seem to provide tools to draw an overlay. Someone told me to simply do my drawing in the JFrame's glassPane however that seems rather overkill, and I've been told never to play around the glassPane so I'm not planning on doing that.
I've seen many topics on the paintings call order but I cannot establish which would be correct while overriding such or such method, and I don't even know if or which method I should override. Is there an obvious way I'd have missed to have my simple JPanel paintings shown over its GLCanvas component?
First of all, I really wouldn't recommend getting a HUD through those means. As I can only imagine this hurting performance a lot. Granted I have never tried mixing Java, OpenGL and AWT's Graphics like that.
Now instead of using holding those classes together with duct tape, consider using JLayeredPane.
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.add(new MyCanvas());
layeredPane.add(new MyPanel());
frame.add(layeredPane);
Now the important part is that you must manually set the bounds of both components:
canvas.setBounds(x, y, width, height);
panel.setBounds(x, y, width, height);
If not you'll end up with the same problem as before:
The painting takes place, but isn't shown in the view
To demonstrate it working I created this small TestPanel class similar to your MyPanel.
public static class TestPanel extends JPanel {
private Color color;
public TestPanel(Color color) {
this.color = color;
}
#Override
public void paintComponent(Graphics g) {
g.setColor(color);
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}
}
Then creating two instances like this:
JPanel panel1 = new TestPanel(new Color(255, 0, 0));
panel1.setBounds(25, 25, 100, 100);
JPanel panel2 = new TestPanel(new Color(0, 0, 255));
panel2.setBounds(75, 75, 100, 100);
Then adding them to a JLayeredPane and adding that to a JFrame and we see this:
I found using the JFrame glass-panel to be a good solution, I use it to draw debugging text on top of 3D graphics, I haven't experienced any problems with it. Using the glass-panel method is more convenient than using a JLayeredPane because the resizing of the 3D panel will be handled for you. Note the 3D graphics must be drawn in a GLJPanel component or the layering won't work (as opposed to GLCanvas which is not a Swing component). The paintComponent(Graphics g) will be called at the same rate as the frame-rate of the GLJPanel. Note also, the glass-pane is hidden by default so setVisible(true) must be called on it.
import javax.swing.JFrame;
import com.jogamp.opengl.awt.GLJPanel;
// ...
public class ApplicationWindow extends JFrame {
public ApplicationWindow(String title) {
super(title);
GLCapabilities gl_profile = new GLCapabilities(GLProfile.getDefault());
GLJPanel gl_canvas = new GLJPanel(gl_profile);
// ... code here to draw the graphics (supply a GLEventListener to gl_canvas)
setContentPane(gl_canvas);
StatusTextOverlayPanel myGlassPane = new StatusTextOverlayPanel();
setGlassPane(myGlassPane);
myGlassPane.setVisible(true);
setVisible(true);
}
class StatusTextOverlayPanel extends JComponent {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14));
g2d.setPaint(Color.BLACK);
String statusText = String.format("C-elev: %.2f S-view %.2f D-view %.2f", 1459.0, 17.0, 2.574691);
g2d.drawString(statusText, 10, 20);
}
}
}
Here is an example of what it could look like (You'll need additional code to draw the axis and the square shown)
I am currently trying to make it possible for a JPanel to be zoomed in. My idea is pretty much as follows :
I have a JPanel (custom with overriden paintComponent etc.) that I place inside my JScrollPane.
What I do to zoom in is to scale up my JPanel using the following code (overriding the paint method)
#Override
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
if (m_hasBeenScaled)
{
m_transform.scale(m_zoomValue, m_zoomValue);
g2.setTransform(m_transform);
m_transform = new AffineTransform();
}
super.paint(g);
}
This works well, however my JScrollPane doesn't display scrollbars as I scale to bigger dimensions. How do I make the JScrollPane respond to this scale up of my JPanel ?
Here's the code I use to create both my JPanel and JScrollPan (Grid is my class extending JPanel):
m_gridPanel = new Grid();
m_gridContainer = new JScrollPane(m_gridPanel);
m_gridContainer.setPreferredSize(new Dimension(605, 605));
The size of the component will be affected by the zoom factor as well, to that end, setting the preferredSize to a "static" value makes no sense, instead, you should be overriding the getPreferredSize and adjusting the size returned by applying the zoom factor to it as well.
Zooming a component is much more complex than changing the transform, you should be translating the mouse events as well, see How to add MouseListener to item on Java Swing Canvas for example.
I have created a custom JPanel class called ImagePanel. I override the paintComponent method like this...
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0,0, null);
}
The purpose of the custom panel is to simply draw an image.
In my JFrame, I create a ScollPane that is added to the JFrame. When I created the ScrollPane though, I pass in the instance of my imagePanel, like this...
ip = new ImagePanel();
JScrollPane jsp = new JScrollPane(ip);
this.add(jsp);
Now all I want as an easy to use way of using the scroll bars to scroll over my image. Right now the image is very large and scrollbars do not appear. I use the policy to make them visible, but the handles to the scrollbars are not there.
Does anyone know an easy way to do this?
Try with JPanel#setPreferredSize() that will force the JScrollPane to show the scroll bar if needed.
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0,0, null);
// set the size of the panel based on image size
setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
}
EDIT
Setting setPreferredSize() inside overridden paintComponent() is not a good way.
You can do it in a simpler way using JLabel as suggested by #mKorbel. For more info have a look at the comments below.
BufferedImage image = ...
JLabel label = new JLabel(new ImageIcon(image)); // set the icon
JScrollPane jsp = new JScrollPane(label);
Screenshot:
I am new in Java and I am currently creating a game with graphics. I have this class that extends from JFrame. In this class, I have many JPanels that needs an image as background. As I know, to be able to paint images in the JPanel, I need to have a separate class that extends from JPanel and that class's paintComponent method will do the work. But I don't want to make separate classes for each JPanel, I have too many of them; and with the fact that I am only concerned with the background. How can I do this? is it with an anonymous inner class? How?
For better understanding I provided some code:
public GUI extends JFrame {
private JPanel x;
...
public GUI() {
x = new JPanel();
// put an image background to x
}
Why not make a single class that takes a Image??
public class ImagePane extends JPanel {
private Image image;
public ImagePane(Image image) {
this.image = image;
}
#Override
public Dimension getPreferredSize() {
return image == null ? new Dimension(0, 0) : new Dimension(image.getWidth(this), image.getHeight(this));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(image, 0, 0, this);
g2d.dispose();
}
}
You would even provide hints about where it should be painted.
This way, you could simply create an instance when ever you needed it
Updated
The other question is, why?
You could just use a JLabel which will paint the icon for you without any additional work...
See How to use labels for more details...
This is actually a bad idea, as JLabel does NOT use it's child components when calculating it's preferred size, it only uses the size of the image and the text properties when determining it's preferred size, this can result in the component been sized incorrectly
You don't have to make another class for it? You could just do:
x = new JPanel(){
public void paintComponent(Graphics g){
super.paintComponent(g);
//draw background image
}
};
You can do this in single line:
panelInstance.add(new JLabel(new ImageIcon(ImageIO.read(new File("Image URL")))));
I hope it will work for you.
How can I tell the paint method to draw background on JPanel only and not on the entire JFrame.
My JFrame size is bigger than the JPanel. When I try to paint a grid background for the JPanel, the grid seems to be painted all over the JFrame instead of just the JPanel.
Here parts of the code:
public class Drawing extends JFrame {
JPanel drawingPanel;
...........
public Drawing (){
drawingPanel = new JPanel();
drawingPanel.setPreferredSize(new Dimension(600,600));
}
public void paint(Graphics g)
{
super.paintComponents(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
paintBackground(g2); //call a METHOD to paint the for JPANEL
}
private void paintBackground(Graphics2D g2)
{
g2.setPaint(Color.GRAY);
for (int i = 0; i < drawingPanel.getSize().width; i += 300)
{
Shape line = new Line2D.Float(i, 0, i, drawingPanel.getSize().height);
g2.draw(line);
}
for (int i = 0; i < drawingPanel.getSize().height; i += 300)
{
Shape line = new Line2D.Float(0, i, drawingPanel.getSize().width, i);
g2.draw(line);
}
} //END private void paintBackground(Graphics2D g2)
}
If you want to do painting on the JPanel then override the JPanel, not the JFrame.
You should be overriding the paintComponent() method of JPanel. Read the section from the Swing tutorial on Custom Painting for a working example.
camickr is correct. So:
public class Drawing extends JFrame {
JPanel drawingPanel;
...........
public Drawing (){
drawingPanel = new MyPanel();
drawingPanel.setPreferredSize(new Dimension(600,600));
add(drawingPanel);
}
}
public class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
myBackgroundRoutine(g2);
}
}
You need to strictly separate your drawing from different components. Swing is already
managing subcomponents, so there is absolutely no need to implement drawings in your
Panel in the Frame (calling paintComponents() is a severe error).
And you should never override paint(), because only paintComponent()
is used in Swing. Don't mix both until you absolutely know what you are doing.
super.paintComponents(g);
I would suggest as your first point of investigation.
The code you posted is not complete, it's missing how the panel is added to the JFrame and which LayoutManager is being used.
The code seams to be correct. Are you sure the JPanel is not occupying the whole JFrame? Add a System.out.println(drawingPanel.getSize()) to check this.
If you are using the BorderLayout, the default for JFrame, and has just added the panel without any constraint, the panel will use the whole area. The PreferredSize is ignored.
Try this, just for testing:
public Drawing (){
drawingPanel = new JPanel();
drawingPanel.setPreferredSize(new Dimension(600,600)); // ignored
drawingPanel.setBounds(0, 0, 600, 600); // location and size
setLayout(null);
add(drawingPanel);
}
but IMO this is not the best or correct way to do it. I would prefer to override the paintComponent() method from the JPanel, as suggested by Thorsten and camickr.
But it will still use the whole area of the JFrame until other Component is added to the JFrame or the LayoutManager changed.
You should override the JPanel, not the JFrame to do painting. You can override the paintComponent() method of the JPanel