I have a simple function that generates JFrame containing an image:
//The window
JFrame frame = new JFrame();
//Topmost component of the window
Container main = frame.getContentPane();
//Turns out this is probably the simplest way to render image on screen
//with guaranteed 1:1 aspect ratio
JPanel panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
};
//Put the image drawer in the topmost window component
main.add(panel);
//Set window size to the image size plus some padding dimensions
frame.setSize(image.getWidth(null)+1, image.getHeight(null)+15);
frame.setVisible(true);
Result:
I think this happens because the window dimensions include the size of the top bar and the window borders.
I also tried to set the size for JPanel and call pack() on JFrame:
//Set the size to the panel and let JFrame size itself properly
panel.setSize(image.getWidth(null), image.getHeight(null));
frame.pack();
Result is even worse:
So how can I precisely specify the inner window dimensions in pixels?
Here's the full function code.
Specify the preferred size of the panel by overriding the getPreferredSize method of JPanel and calling pack() on the JFrame before making it visible
JPanel panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
#Override
public Dimension getPreferredSize(){
return new Dimension(image.getWidth(), image.getHeight());
}
};
See the answers in Use of overriding getPreferredSize() instead of using setPreferredSize() for fixed size Components on the use and effect of either this technique, or the setPreferredSize method
Related
Hello my JPanel wont repaint/resize in x, but just for y.
I can't figure out why.
This is my JPanel.
public class ImagePanel extends JPanel {
private BufferedImage image;
public ImagePanel(String path) {
try {
image = ImageIO.read(new File(path));
this.setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
} catch (IOException ex) {
// handle exception...
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(image, 0, 0,getWidth(), getHeight(), this);
}
}
This is how i add it to the JFrame:
ImagePanel westPanel = new ImagePanel("pic.PNG");
frame.add(westPanel, BorderLayout.WEST);
.
.
// adding a JTabel in BorderLayout.CENTER and some buttons and stuff in NORTH and SOUTH
.
.
frame.pack();
Is it because it has to be in CENTER of the frame borderlayout? Or can it be done in the WEST?
this.setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
Don't set the preferred size.
Instead override the getPreferredSize() method:
#Override
public Dimension getPreferredSize()
{
return new Dimension(image.getWidth(), image.getHeight());
}
Is it because it has to be in CENTER of the frame borderlayout? Or can it be done in the WEST?
If the image is added to the CENTER then the image will be the size of the space available to the frame, so both horizontal and vertical will be resized.
If the image is added to the WEST then the layout respects the width and only the height is resized to fill the height of the frame. So, yes, adding the image to the WEST is probably the cause of your problem.
Read the section from the Swing tutorial on Using Layout Manager for more information about how the constraints affect the layout for each layout manager.
If you need more help then post a proper SSCCE that demonstrates the problem.
In the code below, I create a JFrame, set the size to 800 by 800 pixels, set the layout to BorderLayout, then add a JPanel.
In the JPanel, I use paintComponent to draw a 20x20 pixel image in 7 locations.
At position 0-0, the icon appears in the upper right corner.
The icon is 20 pixels high, and the frame and panel are both 800 pixels high, so drawing the icon at x-780 should align the icon with the bottom of the window. But the icond doesn't even appear.
The remaining icons are drawn a x-770, x-760, x-758, and x-750. x-758 appears aligned with the bottom of the window. So I conclude that x[0] on the JPanel start at the JFrame's x[42]
I think I've set the BorderLayout correctly. I do call setSize() in the JPanel's constructor. I thought setting an explicit size might screw it up. But after commenting that line out, the program shows the same behavior.
Can you show me what I'm doing wrong?
Frametest.java
package frametest;
import java.awt.BorderLayout;
import javax.swing.JFrame;
public class FrameTest extends JFrame{
public static final int HEIGHT = 800;
public static final int WIDTH = 800;
public FrameTest(){
setLayout(new BorderLayout());
add(new Panel(WIDTH, HEIGHT), BorderLayout.CENTER);
pack();
setTitle("Frame Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(WIDTH, HEIGHT);
setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
new FrameTest();
}
}
Panel.java
package frametest;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class Panel extends JPanel{
private int height;
private int width;
Image icon1 = new ImageIcon(this.getClass().getResource("icon1.png")).getImage(); //Note that the png file is 20 x 20 pixels.
public Panel(int width, int hieght){
setBackground(Color.BLUE);
//setSize(width, hieght);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(icon1, 0, 0, this); //appears in the upper-right corner
g2d.drawImage(icon1, 20, 780, this); //does not appear
g2d.drawImage(icon1, 40, 770, this); //appears with bottom pixels cut off
g2d.drawImage(icon1, 70, 760, this); //appears with the bottom pixels cut off
g2d.drawImage(icon1, 100, 758, this); //appears aligned with bottom of the window
g2d.drawImage(icon1, 130, 750, this); //appears slightly above the bottom of the window
g2d.drawImage(icon1, 780, 20, this); //appears aligned with the right side of the screen.
}
}
You are setting the size of the JFrame. This includes the Frame Decoration, the Menu Bar (if you have one), and so on. If you want your JPanel to have the Dimensions 800 x 800, set the preferred Size of your JPanel and use JFrame.pack().
To do so, remove the following line from Frametest.java:
setSize(WIDTH, HEIGHT);
Then, in Panel.java, change this line from
//setSize(width, hieght);
to:
setPreferredSize(new Dimension(800, 800));
Instead of this, you can also overwrite getPreferredSize():
public Dimension getPreferredSize ()
{
return new Dimension(800, 800);
}
Use frame.pack() (as you are already using) instead of frame.setSize() that fits the components as per component's preferred size. Just remove setSize() calls.
Override getPreferredSize() to set the preferred size of the JPanel in case of custom painting.
sample code:
class Panel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
...
}
#Override
public Dimension getPreferredSize() {
return new Dimension(..., ...);
}
}
It sounds like the problem is because of the border around the JFrame. Things like the menu bar at the top will take away some space from the available space to display things in the JFrame.
Try adding getPreferredSize() to your JPanel, and calling pack() on your JFrame.
I have looked online, but I am still having trouble understanding how to add graphics to a JPanel
Here is the code for my panel class:
public class GamePanel extends JPanel {
public GamePanel(){
}
public void paintComponent(Graphics g) {
g.drawString("asd", 5, 5);
}
}
And my main method:
public static void main(String[] args) {
frame.setLayout(new FlowLayout());
frame.getContentPane().setBackground(Color.WHITE);
//i is an instance of GamePanel
frame.add(i);
frame.setPreferredSize(new Dimension(500, 500));
frame.pack();
frame.setVisible(true);
}
Text will only appear in a very tiny section of the screen (this applies to any graphics object I try to draw). What am I doing wrong?
FlowLayout respects preferred sizes of components. Therefore override getPreferredSize to give your JPanel a visible size rather than the default zero size Dimension that the panel currently has after JFrame#pack is called:
class GamePanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g); // added to paint child components
g.drawString("asd", 5, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}
Edit:
To eliminate gap between JPanel and its containing JFrame, set the vertical gap to 0:
frame.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
Two things jump out
Your Game panel has no preferred size, which, by default, makes 0x0. FlowLayout will use this information to make decisions about how best to layout your panel. Because the size is 0x0, the repaint manager will ignore it. Try overriding the getPreferredSize method and return a appropriate size or use a layout manager that does not use the preferred size, like BorderLayout
Your paintComponent method MUST call super.paintComponet
Try to draw a rectangle with different size, How to fit it in one frame proportionally(assume the frame is fixed)?
public class Draw extends JComponent {
public void paint(Graphics g) {
int width = 100;
int length = 100;
g.drawRect(10, 10, width, length);
}
}
public class DrawRect {
public static void main(String[] a) {
JFrame frame = new JFrame();
frame.setSize(400, 600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Container content = frame.getContentPane();
content.add(new Draw());
}
}
Custom painting is done by overriding the paintComponent(...) method, not the paint() method. This advice is made daily. Search the forum for more information and examples.
If you want to know the space available to the component then you can invoke the getWidth() and getHeight() method. Once you know these values you can determine how big you want to paint your rectangle.
Components should be added to the frame BEFORE the frame is made visible.
You don't need to use the getContentPane() method. Since JDK5 you can just add components directly to the frame and they will be added to the content pane for you.
I have a class Pad_Draw extending JComponent. Constructor is
public Pad_Draw()
{
this.setDoubleBuffered(true);
this.setLayout(null);
};
The paintComponent methpod is :
public void paintComponent( Graphics g )
{
graphics2D = (Graphics2D)g;
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2D.setPaint(Color.white);
graphics2D.fillRect(0, 0, getSize().width, getSize().height);
}
In a JFrame I am adding this by a ScrollPane:
JScrollPane Padscroller = new JScrollPane();
Padscroller.setWheelScrollingEnabled(true);
Padscroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
Padscroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
Padscroller.setPreferredSize(new Dimension(200, 100));
Padscroller.setMinimumSize(new Dimension(200, 100));
Padscroller.setMaximumSize(new Dimension(200, 100));
Padscroller.setViewportView(drawPad);
content.add(Padscroller, BorderLayout.CENTER);
But as soon as I add the content in the Frame and set the size of the frame. The drawing pad is taking whole size whatever it needs.
I want my specified size (200,100) to be maintained and I actually want something like Windows Paint application has. I should be able to increase the size by extending a corner. As soon as I extend corner the scrollbar gets activated. Can anyone give me any idea how to achieve it?
The default layout manager for JFrame, BorderLayout does not respect preferred sizes of its components.
You could a layout manager that does, such as BoxLayout, and override getPreferredSize in your Padscroller component:
JScrollPane padScroller = new JScrollPane() {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 100);
}
};
Regarding increasing the size, have a look at using a MouseAdapter and updating this property in the mouseDragged method.