I'm trying to draw a normal blue rectangle on to a JFrame, when I press play no window appears at all (with no blue rectangle)
I know that there are tutorials online showing how to draw a rectangle to a JFrame, but I would like to know the problem with the following code and why it does not work.
public class Window extends JFrame {
public Window() {
initialize();
}
private void initialize() {
JFrame frame = new JFrame();
frame.setBounds(100, 100, 600, 600);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
frame.getContentPane().add(new Display());
}
public static void main(String[]args) {
Window window = new Window();
}
}
public class Display extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(0, 0, 100, 100);
}
public void reDraw() {
repaint();
}
}
Overall, I want to know the problems of the code above and how to fix it so that a blue rectangle is drawn on to a window when the program is played.
Thanks for reading : )
Your main issues are:
frame.getContentPane().setLayout(null); - This is generally a bad idea to start with. There are so many issues with this approach that it's difficult to list them all.
null layouts require you take full responsibility for the container's children's locations and sizes. They don't take into consideration different platform rendering systems and traits
You'll find it difficult to accurately size the parent window, as the available content size is the frame size MINIUS the frame borders, so while you're setting the frame to 600x600, the actual available content space is going to be smaller
A component's default size and position is 0x0 - so unless you're willing to take control of this (which is what layout managers do anyway), it will never be displayed (Swing's not dumb, but it is lazy ;))
Not calling setVisible on the JFrame
Not providing a sizing hint for the Display panel
Extending JFrame ... and not making any use of it. Extending JFrame is generally discouraged, but in your case, it's just adding unwanted noise
Work example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {//extends JFrame {
public Test() {
initialize();
}
private void initialize() {
JFrame frame = new JFrame();
//frame.setBounds(100, 100, 600, 600);
//frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//frame.getContentPane().setLayout(null);
frame.getContentPane().add(new Display());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
Test window = new Test();
}
public class Display extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(0, 0, 100, 100);
}
// Not sure what benefit this provides
//public void reDraw() {
// repaint();
//}
}
}
You might want to take a look at Laying Out Components Within a Container to get a better understanding of the layout managers and what they do and how they work (and why you should use them ;))
Related
Full image of my window
The grey border of JFrame visible
Problem:
I want my application to run on full screen (maximized) by default (but the maximum resolution varies by default from laptop to laptop). I am using a background image which scales according to size and width of user's computer on a JPanel.
But with the decorations and the resize features "ON" JPanel isn't completely filling the JFrame.
I wanted my application to:
Allow user to resize it as per use and the image to scale along with it
In the maximized view (by default: setExtendedState(JFrame.MAXIMIZED_BOTH);) the image covers the entire JFrame (Note: Happy with any solution that works on all devices with or without using the JFrame.)
My components if possible get resized too
I am using NetBeans, JFrame is in "absolute layout". I tried with JPanel both on absolute layout as well as BorderLayout (not working), tried pack() (also not working), jPanel1.setSize(WIDTH,HEIGHT) with the dimensions of the screen is also not working. Setting JPanels layout to NULL is also not resolving the issue :(
Sign_Up.java (JFrame)
public class Sign_Up extends javax.swing.JFrame {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
double width = screenSize.getWidth();
double height = screenSize.getHeight();
/**
* Creates new form Sign_Up
*/
public Sign_Up() {
initComponents();
Seticon();
btnSave.setEnabled(false);//save button
setExtendedState(JFrame.MAXIMIZED_BOTH);
//setSize(1920,1080);
setLocationRelativeTo(null);//makes aligned at center of screen
//jPanel1.setSize((int)width, (int)height);
//pack();
}
PanelScale.java
public class PanelScale extends JPanel {
Image iconbg;
public PanelScale() {
iconbg = new ImageIcon(getClass( ).getResource("/images/largesignup.png")).getImage( );
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gd = (Graphics2D)g.create();
gd.drawImage(iconbg, 0, 0, getWidth(), getHeight(), this);
gd.dispose();
}
}
Custom Creation Code in JPanel : new Panel.PanelScale();
The only thing that I found working was explicitly stretching the JPanel over the JFrame to some extra height (in NetBeans) but that resulted in my application window not at the center of the screen but shifted to the right.
Stretching the Jpanel Over JFrame to some more height
But when I try to do that using
setsize(new Dimension(width, height+40));
for the JPanel, it doesn't work.
Also I could have done this using JLabel but I want my image to cover the JFrame to full area while working in maximized or resized view on any device (larger or smaller Laptop like 1920x1080 resolution, 1280x720, etc.)
I would be grateful if any solution is provided, even some alternative way with or without JPanel.
Even if the application is able to work on Full Screen on any device with the image covering it full I will be satisfied, resizing feature can be sacrificed for the time being
Expected
BorderLayout (which is set by default for JFrame) will do exactly what you want automatically, you just then need to resize/position the background based on your needs.
null ("absolute") layouts really aren't a good idea.
Start by taking a look at How to Use BorderLayout. I would also recommend looking at Working with Images and the JavaDocs for Graphics2D which will provide you with the information you need on how to resize the image to your needs
Runnable example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new BackgroundPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class BackgroundPane extends JPanel {
private BufferedImage backgroundImage;
public BackgroundPane() throws IOException {
backgroundImage = ImageIO.read(getClass().getResource("/images/Mando01.jpeg"));
}
#Override
public Dimension getPreferredSize() {
if (backgroundImage == null) {
return new Dimension(400, 400);
}
return new Dimension(backgroundImage.getWidth(), backgroundImage.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (backgroundImage == null) {
return;
}
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
g2d.dispose();
}
}
}
Instead of using JPanel.setSize(), set the LayoutManager of the JFrame to null. Every time the window size is changed, resize the JPanel with the method JPanel.setBounds(0,0,windowWidth,windowHeight). You may have to call the repaint() method to redraw the components on the screen.
// Create a window with a size of 300x200
JFrame frame = new JFrame("Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setSize(300, 200);
frame.setLayout(null);
JPanel panel = new JPanel();
panel.setBounds(0, 0, 300, 200);
panel.setBackground(Color.BLACK);
frame.add(panel);
frame.setVisible(true);
// Resize the window to 600x400
frame.setSize(600, 400);
panel.setBounds(0, 0, 600, 400); // Update panel size
panel.repaint(); // Repaint the components
The result of the code is this:
If you remove the last two lines of the code, you'll notice that the size does not change.
I'm trying to draw a line in a JFrame, but line isn't drawn.
I tried to use the method setOpaque(true) for contentPane, lblNewLabel and l but nothing changed. I also tried call repaint(); outside this class but the situation is still the same. Here's the code:
public class DrawingClass extends JFrame
{
private JPanel contentPane;
public DrawingClass(int n, int s, int p) {
Line l= new Line();
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setBounds(700, 300, 480, 640);
contentPane = new JPanel();
contentPane.setOpaque(true);
setResizable(false);
setContentPane(contentPane);
contentPane.setLayout(null);
JLabel lblNewLabel = new JLabel("");
lblNewLabel.setIcon(new ImageIcon("image.png"));
lblNewLabel.setBounds(0, 0, 480, 640);
contentPane.add(lblNewLabel);
l.setBounds(0,0,480,640);
contentPane.add(l);
repaint();
}
class Line extends JPanel
{
public void paintComponent(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(10, 10, 15, 12);
}
}
}
I expect a little line on the top left of the JFrame, above the background wallpaper, but nothing happen. It shows only the wallpaper.
There are several errors in your code:
You're extending JFrame but you're not changing its behavior, so why are you doing that? JFrame is a rigid component, so it's never a good idea to extend from it, build your GUI based on JPanels instead. See: Extends JFrame vs. creating it inside the program
Don't explicitly set the size of the JFrame, call pack() on it and instead override getPreferredSize from the JPanel, see: Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
You don't need to call setOpaque(...) in this case.
Don't use a null-layout, it might lead to strange errors, because null Layout is Evil and frowned upon
We don't have access to your image so we cannot test the ImageIcon and it's also not related to your question. However you should load your images as resources
Don't explicitly set the bounds of each element, this is related to point (4) and you should use a Layout Manager or combinations of them to get your desired GUI.
Don't call repaint() that way, it has no effect, it is supposed to repaint your UI when there's a change in it. However there is no change at the start of your program.
You're breaking the paint-chain by not calling super.paintComponent(...) inside your paintComponent(...) method. Check the Tutorial on Custom Painting in Swing so that you learn how to do it properly
And be careful, as paintComponents(...) (With a trailing s) is different from paintComponent(...) (Look at your title)
So, after doing all of the above changes, we get to this simple program:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class DrawingClass {
private JPanel contentPane;
private JFrame frame;
public static void main(String args[]) {
SwingUtilities.invokeLater(() -> new DrawingClass().createAndShowGUI());
}
public void createAndShowGUI() {
frame = new JFrame(getClass().getSimpleName());
Line line = new Line();
frame.add(line);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
class Line extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(10, 10, 15, 12);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(480, 640);
}
}
}
Which produces the following output:
I want to put my background image at the very bottom in this frame, and the button on top. However the code I wrote below doesn't work. Can anyone see where the problems are?
Another thing is that even though I set the location for my button, it keep showing at the top center on the frame.
Please ignore the comment lines. (I was just guessing, and hoping them will work, but they don't apparently.)
public class Menu extends JFrame{
private JLayeredPane pane;
private JLayeredPane pane2;
public Menu(){
final JFrame f = new JFrame("Chinese Chess");
JButton play = new JButton("Play vs. AI");
f.setLayout(new FlowLayout());
f.setLocationRelativeTo(null);
f.setSize(800, 800);
f.setVisible(true);
f.setResizable(false);
//f.pack();
pane = new JLayeredPane();
pane2 = new JLayeredPane();
f.add(pane);
f.add(pane2);
//background image
JLabel background = new JLabel(new ImageIcon("res/img/background.png"));
background.setLocation(0, 0);
background.setSize(800, 800);
pane.add(background, JLayeredPane.FRAME_CONTENT_LAYER);
pane2.add(play, JLayeredPane.DEFAULT_LAYER);
//pane.moveToBack();
//button PlayAI
play.setLocation(500,500);
play.setPreferredSize(new Dimension(100,50));
//f.setLayout(new FlowLayout());
//frame menu
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//f.getContentPane().add(play);
play.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
new PlayAI();
}
});
}
public static void main(String[] args){
new Menu();
}
Problems/Solutions:
setLocation(...) and setBounds(...) types of calls are ignored by most layout managers. The only way to use them is to set the layout of the container to null via .setLayout(null);
But having said that, while null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
So in sum -- don't do this, don't use null layouts or setBounds, but rather nest JPanels, each using its own layout manager, and thereby create easy to maintain and decent GUI's.
If you want an image to be in the background, then draw it in a JPanel that you use as a container for your GUI components by drawing it in the JPanel's paintComponent(Graphics g) method as has been demonstrated in many many similar questions on this site -- I'll find you some of mine in a second.
If you add any JPanels on top of this image drawing JPanel, be sure that you can see through them by calling setOpaque(false) on these overlying JPanels. Otherwise you'll cover up the image.
Your code has two JFrames when only one is needed. Get rid of the one you don't use.
You call setVisible(true) too early on the JFrame, before components have been added to the GUI -- don't. Call it only after adding everything to the GUI so all display OK.
You're creating two JLayedPanes, and completely covering one by the other by adding them to the JFrame without understanding how the JFrame's BorderLayout handles added components.
I suggest that you not even use one JLayeredPane but instead draw in the JPanel as noted above, and use that as your container.
Your code looks to be opening a completely new GUI window when the play button is pressed, and if so, this can get annoying to the user fast. Consider swapping views instead with a CardLayout.
For example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
// extend JPanel so you can draw to its background
#SuppressWarnings("serial")
public class Menu2 extends JPanel {
private BufferedImage bgImage = null; // our background image
private JButton playButton = new JButton(new PlayVsAiAction("Play Vs. AI", KeyEvent.VK_P));
public Menu2(BufferedImage bgImage) {
this.bgImage = bgImage;
setLayout(new GridBagLayout()); // center our button
add(playButton);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bgImage != null) {
g.drawImage(bgImage, 0, 0, this);
}
}
// to size our GUI to match a constant or the image.
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
// if you want to size it based on the image
if (bgImage != null) {
int width = bgImage.getWidth();
int height = bgImage.getHeight();
return new Dimension(width, height);
} else {
return super.getPreferredSize();
}
// if you want to size the GUI with constants:
// return new Dimension(PREF_W, PREF_H);
}
private class PlayVsAiAction extends AbstractAction {
public PlayVsAiAction(String name, int mnemonic) {
super(name); // have our button display this name
putValue(MNEMONIC_KEY, mnemonic); // alt-key to press button
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO code to start program
}
}
private static void createAndShowGui() {
BufferedImage img = null;
String imagePath = "res/img/background.png";
try {
// TODO: fix this -- use class resources to get image, not File
img = ImageIO.read(new File(imagePath));
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
Menu2 mainPanel = new Menu2(img);
JFrame frame = new JFrame("Chinese Chess");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
Apart from the solution above... you should create and launch your swing application this way:
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
// Instantiate your JFrame and show it
}
package data;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main extends JFrame implements Runnable {
private JPanel contentPane;
private JPanel pp = new JPanel();
Thread page = new Thread();
public void run() {
while (!Thread.interrupted()) {
}
}
public Main() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 640 + 16, 480 + 39);
contentPane = new JPanel();
contentPane.setBounds(0, 0, 640, 480);
contentPane.setLayout(null);
setContentPane(contentPane);
pp.setBounds(0, 0, 640, 480);
pp.setBackground(Color.BLACK);
contentPane.add(pp);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Main frame = new Main();
frame.setVisible(true);
frame.setResizable(false);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
The above code works but setResizable causes an issue: http://i.stack.imgur.com/hQxPU.png
If I were to remove the setResizable then the grey at the bottom and right would be black like it's meant to. How can I disable resizing without causing this issue?
You're using an absolute layout (no LayoutManager set), and the black panel has fixed bounds. And that's exactly the reason the black panel won't fill its parent's bounds when the parent is resized.
Solution: use a LayoutManager which automatically recalculates the bounds of your content so it fills the available space.
// BorderLayout is your friend
contentPane.setLayout(new BorderLayout());
...
// delete this line, no need to set fixed bounds
// pp.setBounds(0, 0, 640, 480);
More on how to use layout managers in AWT/Swing:
The Java™ Tutorials - Using Layout Managers
Layout Managers have two purposes:
calculate the min/max/preferred size of a container
layout components by setting their bounds within the container.
If you want the black panel to have a size of 640x480, and the window to be non-resizable, you can set the preferred size and then pack the window, causing its size to become appropriate for the content's preferred dimensions:
pp.setPreferredSize(new Dimension(640, 480));
...
pack();
This is my code:
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test{
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame();
JPanel jp = new JPanel();
jp.setMinimumSize(new Dimension(200, 200));
jp.setPreferredSize(new Dimension(200, 200));
//frame.getContentPane().setMinimumSize(new Dimension(200, 200));
//frame.getContentPane().setPreferredSize(new Dimension(200, 200));
frame.getContentPane().add(jp);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}
}
It behaves exactly the way I want. User can resize the JFrame but it'll always be large enough to hold 200px x 200px JPanel. But when I remove:
JFrame.setDefaultLookAndFeelDecorated(true);
user can resize JFrame to any size.
Why does default look and feel decoration affect resizing? How can i make it work without setDefaultLookAndFeelDecorated(true)? I know I can set frames minimum size manually (JFrame#setMinimumSize(new Dimension dim)) and I will if there is no smarter solution.
I'm using jdk 1.7 on Windows 7.
This use to be a bug, the page speaks of work arounds like:
override setSize() and check if the new size is less than the desired
inside paint(Graphics g) of JFrame check height and width and "re-create" frame to new minimum size
however a person claims it to be fixed as the original example issued for the bug proposal omitted the call to setMinimumSize(...), this is needed because JFrame does not enforce minimum size
Here is the snippet which shows that persons' code:
import java.awt.Dimension;
import javax.swing.*;
public class JavaApplication118 {
private final Dimension m_dim = new Dimension(200, 150);
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new JavaApplication118().createAndShowUI();
}
});
}
private void createAndShowUI() {
// JFrame.setDefaultLookAndFeelDecorated(true);
final JFrame frame = new JFrame("Test") {
#Override
public Dimension getMinimumSize() {
return m_dim;
}
};
frame.setMinimumSize(m_dim);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents(frame);
// frame.setResizable(false);
frame.pack();
frame.setVisible(true);
}
private void initComponents(JFrame frame) {
JPanel jp = new JPanel();
jp.setMinimumSize(new Dimension(200, 200));
jp.setPreferredSize(new Dimension(400, 400));//for testing
frame.getContentPane().add(jp);
}
}
EDIT
The snippet overrode getMinimumSize() though this seems redundant after calling setMinimumSize(..), I kept it included as that's how I found the snippet (I did remove the overridden getPreferredSize() method of the JFrame which the snippet included).
Why does default look and feel decoration affect resizing?
Because then the window sizing is under the complete control of the LAF while dragging: The mouseHandler installed by (f.i.) MetalRootPaneUI doesn't resize the window below the min returned by the LayoutManager. Without setting the frame's minimumSize, you can still decrease its size programatically.
The only way (unfortunately) to enforce a window's minimum size always is to manually set it. Unfortunate, as that implies keeping track of dynamic changes of that minimum and update it as appropriate.
A snippet to play with (un/comment the defaultDecoration and frame min setting)
JFrame.setDefaultLookAndFeelDecorated(true);
JPanel panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
Dimension m = getMinimumSize();
// visualize the min
g.drawRect(0, 0, m.width - 1, m.height -1);
}
};
// BEWARE: for demonstration only, NEVER do in production code
panel.setMinimumSize(new Dimension(400, 400));
panel.setPreferredSize(panel.getMinimumSize());
final JXFrame frame = new JXFrame("", true);
Action action = new AbstractAction("resize") {
#Override
public void actionPerformed(ActionEvent e) {
frame.setSize(300, 300);
LOG.info("succeeded? " + frame.getSize());
}
};
panel.add(new JButton(action));
frame.add(panel);
frame.pack();
// set minimum is required to enforce the minimum
frame.setMinimumSize(frame.getMinimumSize());
Update
Looking at the Window source, turns out that you can have auto-magic dynamic respect of min size by overriding isMinimumSizeSet and returning true uncondinionally:
final JXFrame frame = new JXFrame("", true) {
#Override
public boolean isMinimumSizeSet() {
return true;
}
};
...
// no longer needed
// frame.setMinimumSize(frame.getMinimumSize());
not tested for side-effects, though