My code
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("Flappy bird");
frame.setSize(1200, 800);
FlappyBird game = new FlappyBird();
frame.getContentPane().setBackground(Color.YELLOW);
frame.add(game);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
while (true) {
game.moveBall();
game.gameOver();
game.moveRect();
game.repaint();
Thread.sleep(14);
}
}
Why isn't the frame.getContentPane().setBackground(Color.YELLOW); working?
I've tried to rearrange the order, like setting the color after making the frame visible.
It works alright, but you cannot see the background color because your FlappyBird instance is drawn on top of it. You can easily verify this by replacing your game class with an empty canvas like so:
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("Flappy bird");
frame.setSize(1200, 800);
//FlappyBird game = new FlappyBird();
Canvas game = new Canvas();
frame.getContentPane().setBackground(Color.YELLOW);
frame.add(game);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
// while (true) {
// game.moveBall();
// game.gameOver();
// game.moveRect();
// game.repaint();
// Thread.sleep(14);
// }
}
There are two things you can try:
Setting the background color not of the frame's content pane but of game:
//frame.getContentPane().setBackground(Color.YELLOW);
game.setBackground(Color.YELLOW);
Making sure that the frame's background color shows through the game instance by making the latter transparent:
game.setOpaque(false);
Removing the lines related to game, I was able to run this with the expected yellow result. The issue must be within the while loop
while (true) {
game.moveBall();
game.gameOver();
game.moveRect();
game.repaint();
Thread.sleep(14);
}
or
frame.add(game);
Without the FlappyBird class it's impossible to say exactly what is causing the issue, but based on the method names I'd look at repaint().
Related
For some reason, the JFrame is too small when run, and using pack(); doesn't seem to fix the problem. Any ideas?
public class Window {
public Window(String title, Game game) {
// Creates new JFrame
JFrame frame = new JFrame(title);
// Adds Game to window
frame.add(game);
frame.pack();
// Settings of Window
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
The cyan background is supposed to be a 64 x 64 border in the bottom and right sides. The boxes drawn are correct, which I checked by resizing the window. However the window is too small and does not fit the canvas.
Game Class:
public class Game extends Canvas implements Runnable {
private Handler handler;
public Game() {
// Create new window
Window window = new Window("Horses Aren't Real", this);
handler = new Handler();
this.addKeyListener(new KeyInput(handler));
handler.addObject(new Daanish(100, 100, ID.Player, handler));
}
public static void main (String args[]) {
// Creates new game
new Game();
}
public void draw() {
// Creates a new BufferStrategy
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
// This allows the game to preload 3 frames in order to prevent choppy framrate
this.createBufferStrategy(3);
return;
}
// Create Graphics
Graphics g = bs.getDrawGraphics();
g.setColor(Color.cyan);
g.fillRect(0, 0, 1024, 640);
g.setColor(Color.black);
g.fillRect(0, 0, 960, 576);
handler.draw(g);
// Remove frame from queue
g.dispose();
bs.show();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(1024, 640);
}
}
So you're running into a number of issues. The first been, the size of the window must also account for the frame decorations, this means that the available space for the content is the size of the window MINUS the size of the frame decorations.
Setting the size of the window directly is ill-advised.
pack asks the content of the frame for it's preferred size and uses that to wrap the window around it. So, instead of window size - decorations = content size, you have content size + decorations = window size
From the JavaDocs
Causes this Window to be sized to fit the preferred size and layouts of its subcomponents. The resulting width and height of the window are automatically enlarged if either of dimensions is less than the minimum size as specified by the previous call to the setMinimumSize method.
So, instead, override preferredSize of the Game class and return the amount of space you want, may be something like...
public class Game extends JPanel {
//...
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
//...
}
Your next problem is, Window extends from JFrame, but in the constructor, you're creating ANOTHER Frame ... so which one is actually doing what?
Remove the confusion. Avoid extending from top level containers like JFrame, you're not adding any new functionality to it
public class Window {
public Window(String title, Game game) {
// Creates new JFrame
JFrame frame = new JFrame(title);
// Adds Game to window
frame.add(game);
frame.pack();
// Settings of Window
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Updated...
So, I took your "updated" code, modified it so it would run, ran and didn't have any issues...
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Game game = new Game();
Window window = new Window("Help", game);
game.start();
}
});
}
public class Window {
public Window(String title, Game game) {
// Creates new JFrame
JFrame frame = new JFrame(title);
// Adds Game to window
frame.add(game);
frame.setResizable(false);
frame.pack();
// Settings of Window
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
public class Game extends Canvas { //implements Runnable {
// private Handler handler;
public Game() {
//
// handler = new Handler();
// this.addKeyListener(new KeyInput(handler));
//
// handler.addObject(new Daanish(100, 100, ID.Player, handler));
}
public void start() {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
draw();
try {
Thread.sleep(40);
} catch (InterruptedException ex) {
}
}
}
});
thread.start();
}
public void draw() {
// Creates a new BufferStrategy
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
// This allows the game to preload 3 frames in order to prevent choppy framrate
this.createBufferStrategy(3);
return;
}
// Create Graphics
Graphics g = bs.getDrawGraphics();
g.setColor(Color.cyan);
g.fillRect(0, 0, 1024, 640);
g.setColor(Color.black);
g.fillRect(0, 0, 960, 576);
// handler.draw(g);
// Remove frame from queue
g.dispose();
bs.show();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(1024, 640);
}
}
}
However, I'm running on MacOS. There is a little known issue with setResizable I've run across on Windows, where the window decorations change size when setResizable is called.
The "basic" solution is to call setResziable BEFORE calling pack, so that the frame decorations are modified before the the API makes decisions about how to update the size of the window.
I had thought this was fixed in later (8+) versions of Java, but since I don't run Windows, it's impossible for me to test
See My JFrame always becomes a few pixels too big. for some more details.
public class Window extends JFrame {
public Window(final int width, final int height, String title, Game game) {
super(title);
// Set dimensions
Dimension d = new Dimension(width, height);
game.setPreferredSize(d);
// Adds Game to window
add(game);
// Settings of Window
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
pack();
setVisible(true);
}
}
When you use pack, the frame generaly resize to content size
I have the following code
package trtyhard;
import javax.swing.JFrame;
public class TrtyHard {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("TryHard");
//frame.setSize(700, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setSize(700, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Sometimes JFrame appears with diffirent size. Sometimes it has 10 additional mm at the bottom, sometimes 5-7 additional mm at the right side.
How can i fix it?
In addition to making sure the GUI is created on the Event Dispatch Thread EDT) try restructuring the code as follow:
JFrame frame = new JFrame("TryHard");
//frame.setSize(700, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setSize(700, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
The point is to set the frame properties before you do a setSize() or pack() on the frame.
Read the section from the Swing tutorial Initial Thread for more information about the EDT.
The JFrame class has the setBounds(x, y, width, height) method. You can try it. https://docs.oracle.com/javase/7/docs/api/java/awt/Window.html#setBounds(int,%20int,%20int,%20int)
I have a simple Swing GUI. (and not only this, all swing GUI I have written). When run it, it doesn't show anything except blank screen, until I resize the main frame, so every components have painted again, and I can show them.
Here is my simple code :
public static void main(String[] args) {
JFrame frame = new JFrame("JScroll Pane Test");
frame.setVisible(true);
frame.setSize(new Dimension(800, 600));
JTextArea txtNotes = new JTextArea();
txtNotes.setText("Hello World");
JScrollPane scrollPane = new JScrollPane(txtNotes);
frame.add(scrollPane);
}
So, my question is : how can when I start this class, the frame will appear all components I have added, not until I resize frame.
Thanks :)
Do not add components to JFrame after the JFrame is visible (setVisible(true))
Not really good practice to call setSize() on frame rather call pack() (Causes JFrame to be sized to fit the preferred size and layouts of its subcomponents) and let LayoutManager handle the size.
Use EDT (Event-Dispatch-Thread)
call JFrame#setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) as said by #Gilbert Le Blanc (+1 to him) or else your EDT/Initial thread will remain active even after JFrame has been closed
Like so:
public static void main(String[] args) {
//Create GUI on EDT Thread
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("JScroll Pane Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTextArea txtNotes = new JTextArea();
txtNotes.setText("Hello World");
JScrollPane scrollPane = new JScrollPane(txtNotes);
frame.add(scrollPane);//add components
frame.pack();
frame.setVisible(true);//show (after adding components)
}
});
}
Your simple code is missing a few things.
You have to invoke SwingUtilities to put the Swing components on the event dispatch thread.
You should call the setDefaultCloseOperation on the JFrame.
You have to call the JFrame methods in the correct order. The setSize or pack method is called, then the setVisible method is called last.
public class SimpleFrame implements Runnable {
#Override
public void run() {
JFrame frame = new JFrame("JScroll Pane Test");
JTextArea txtNotes = new JTextArea();
txtNotes.setText("Hello World");
JScrollPane scrollPane = new JScrollPane(txtNotes);
frame.add(scrollPane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(800, 600));
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new SimpleFrame());
}
}
Basically I am making a snake game and I want to swap between the Game Menu and the actual Snake Game at will so I have a variable int visibleCanvas and the switch bellow. So basically I set as false the main menu visibility and put the game as visible and focusable.
However the screen goes black and it wont draw the actual game untill I press the "full screen button" (windows) which then shows the game as I wanted it to.
Both menu and SnakeC and classes that extend Canvas.
switch (visibleCanvas) {
case 0:
menu.setBackground(Color.black);
menu.setVisible(true);
menu.setFocusable(true);
menu.setPreferredSize(new Dimension(640, 480));
break;
case 1:
menu.setVisible(false);
snakeC.setBackground(Color.black);
snakeC.setPreferredSize(new Dimension(640, 480));
snakeC.setVisible(true);
snakeC.setFocusable(true);
snakeC.requestFocus();
break;
}
You're not revalidating and repainting the Component hierarchy. If you don't do that, then your application window won't be redrawn directly, but only when required (such as after a resize).
By the way, if you're making a game, you may want to look into double buffering using BufferStrategy, as it may help to prevent flicker during animations, and allow you to repaint the Canvas periodically (ensuring a constant frame rate and, as such, smooth animations).
Otherwise, this piece of code demonstrates a working example that switches Canvas instances when the first is clicked (you can replace the MouseListener with whatever trigger you want yourself):
public static void main(String[] args) {
final JFrame frame = new JFrame("Test");
frame.setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Canvas canvas1 = new Canvas();
final Canvas canvas2 = new Canvas();
canvas1.setBackground(Color.RED);
canvas2.setBackground(Color.BLUE);
canvas1.addMouseListener(new MouseAdapter() {
#Override public void mouseClicked(MouseEvent e) {
frame.remove(canvas1);
frame.add(canvas2, BorderLayout.CENTER);
System.out.println("Canvas switched to Canvas2");
frame.revalidate();
}
});
SwingUtilities.invokeLater(new Runnable() {
#Override public void run() {
frame.add(canvas1);
frame.setSize(640, 480);
frame.setVisible(true);
}
});
}
I am writing an a snake game, but there's a small problem. SOMETIMES there's a little gap between the panel and the frame. I really have no idea what could be the problem as it appears so irregularly.
SSCCE:
public class Game {
static JFrame frame = new JFrame("The Snake Game");
static Game game = new Game();
JPanel cards;
static JPanel card1 = new JPanel();
private Game() {
}
public void addComponentToPane(Container pane) {
// CARD 1
card1.setLayout(null);
card1.setPreferredSize(new Dimension(600, 625));
CardLayout cl = new CardLayout();
cards = new JPanel(cl);
cards.add(card1);
pane.add(cards, BorderLayout.CENTER);
}
private static void createAndShowGUI() {
game.addComponentToPane(frame.getContentPane());
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
} // end of main
}
Try calling setResizable AFTER you call pack
For some reason, doing it the other way seems to add about 10-20 pixels to the width and height.
If that doesn't work, call setResizable AFTER the frame has being made visible
I encounter the same problem today. After a little bit of Googling, I couldn't find any good answer. Kuba gave a good hint and the problem is finally resolved. I guess this issue is caused by the delay of the setResizable(false) function and hence it happens occasionally. My solution is adding a short hold after calling setResizable.
setResizable(false);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
Okay, I know this is a disgusting way to do it. But in the question JFrame isResizable(false) sizing issue is said: "You can reset it by call JFrame#pack after the calling JFrame#setResizable". So I thought, why not reset it twice?!
Code:
private static void createAndShowGUI() {
game.addComponentToPane(frame.getContentPane());
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
if(frame.getContentPane().getWidth() > 600){
frame.pack();
}
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
So calling the method pack() for the second time when the width is greater than my preferred size seems to resolve the problem.