Im trying to add a Score and Elapsed Time label (scoreAndTimer) to my already working snake game code. The problem is when I use scoreAndTimer.setText(); it stacks with previous text.
I tried to setText(); then setText(String); to clear previous one but it doesnt work also.
private JLabel scoreAndTimer;
private int sec, min;
private Game game;
public Frame() {
JFrame frame = new JFrame();
game = new Game();
frame.add(game);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Snake");
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
scoreAndTimer = new JLabel();
scoreAndTimer.setVerticalAlignment(SwingConstants.TOP);
scoreAndTimer.setHorizontalAlignment(SwingConstants.CENTER);
frame.add(scoreAndTimer);
timer();
}
private void timer(){
while(game.isRunning()){
scoreAndTimer.setText("SCORE: "+(game.getSnakeSize()-3)+" Elapsed Time: "+timeFormatter());
try{
if(sec == 60){
sec = 0;
min++;
}
sec++;
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
if(!game.isRunning())
scoreAndTimer.setText("Game Over");
}
private String timeFormatter(){
if(sec < 10 && min < 10)
return "0"+min+":0"+sec;
else if(sec >= 10 && min < 10)
return "0"+min+":"+sec;
else if(sec < 10 && min >= 10)
return min+"0:"+sec;
else
return min+":"+sec;
}
public static void main(String[] args) {
new Frame();
}
}
Program is working well but could'nt prevent overlap. There is no error. Im using totally 3 Threads in my program, im not sure if threads are making a problem about this. Code is a bit long thats why i dont share the rest for now, if needed i can share other parts also but i dont think the problem occurs at other classes.
JFrame, or more precisely it contentpane uses BorderLayout by default.
When you add components to a JFrame:
frame.add(game);
You implicitly add it to the BorderLayout.CENTER position, which is the default position. So frame.add(game); is equivalent to frame.add(game, BorderLayout.CENTER);
The BorderLayout.CENTER position (as well as other BorderLayout positions) can hold one component.
The problem is that you add another component to the same BorderLayout.CENTER position by:
frame.add(scoreAndTimer);
The solution is to add scoreAndTimer to a different position:
frame.add(scoreAndTimer, BorderLayout.PAGE_END);
and have
frame.pack();
frame.setVisible(true);
at the end, after you have added all components.
Important side note:
timer() as written is not going to work. Think of Swing Application as an application that runs on a single thread. When this thread is
busy with running the long while loop (like the one you have in timer(), it does not update the gui. The gui becomes unresponsive(freezes).
Use Swing timer.
Related
I am trying to render a complicated set of objects, and instead of trying to render each object individually, I thought it would be faster to render them as a BufferedImage. The only way I could figure out how to do that was to turn the BufferedImage into an ImageIcon, and set that to a JLabel, and add the JLabel to the JFrame. To update the image, I remove the JLabel, set it with the new BufferedImage, and re-add it to the JFrame. This makes the screen flash rapidly as you can see an empty frame in between each rendering. If I don't remove the label, the program runs extremely slowly. How do I fix this? Should I even be using JLabels or is there a better way?
public static void createWindow() {
frame = new JFrame("PlanetSim");
frame.setPreferredSize(new Dimension(WINDOW_X, WINDOW_Y));
frame.setMaximumSize(new Dimension(WINDOW_X, WINDOW_Y));
frame.setMinimumSize(new Dimension(WINDOW_X, WINDOW_Y));
foregroundLabel = new JLabel(new ImageIcon(foreground));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(foregroundLabel);
frame.pack();
frame.setVisible(true);
}
public static void setForeground() {
frame.getContentPane().remove(foregroundLabel);
foregroundLabel = new JLabel(new ImageIcon(foreground));
frame.getContentPane().add(foregroundLabel);
frame.pack();
}
I doubt the problem has to do with the loop itself, but I'm including it anyway just in case.
public void run() {
long lastTime = System.nanoTime(), now; //keeps track of time on computer in nanoseconds
double amountOfTicks = 60.0; //number of ticks per rendering
double delta = 0; //time step
long timer = System.currentTimeMillis(); //timer to display FPS every 1000 ms
int frames = 0;
while (running) {
now = System.nanoTime();
delta += (now - lastTime) * amountOfTicks / 1000000000;
lastTime = now;
while(delta >= 1) {
tick();
delta--;
}
if (running)
render();
frames++;
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
System.out.println("FPS: " + frames);
frames = 0;
}
}
}
public void render() {
populateWindow();
Window.setForeground();
}
frame.getContentPane().remove(foregroundLabel);
foregroundLabel = new JLabel(new ImageIcon(foreground));
frame.getContentPane().add(foregroundLabel);
frame.pack();
I would suggest there is no need to remove/add the label or pack the frame since I would assume the Icon will be the same size every time.
All you need to do is replace the Icon of the label.
//frame.getContentPane().remove(foregroundLabel);
foregroundLabel.setIcon( new ImageIcon(foreground) );
//frame.getContentPane().add(foregroundLabel);
//frame.pack();
I am trying to render a complicated set of objects, and instead of trying to render each object individually, I thought it would be faster to render them as a BufferedImage
Otherwise just do the rendering in your paintComponent() method. Swing is double buffered by default so it essentially removes the need to create the BufferedImage.
See: get width and height of JPanel outside of the class for an example of custom painting. Just update the ball count from 5 to 100 to make it more complex.
I am writing a Blackjack program using JFrame and trying to keep it as simple as possible. My JButton, jbHit works with a single click, however it overwrites the playersHand and playerSide slot with every click. I would like it to work with multiple clicks (3 clicks - since that is the max number of cards you can get after the first two are dealt) options It should count them so to speak so that the array index can record the card image. Here is my ActionListener code that I have so far. I am afraid I am stuck. Should I use some sort of for loop with an int i++?
//Hit Button ActionListener
jbHit.addActionListener( new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if ( playerValue < 21 ) {
//Draw a card
Card c = deck.drawCard();
playersHand.add(c);
playerSide[2].setIcon( new ImageIcon( c.getFilename() ) );
}
//If playerValue > 21, bust
else if ( playerValue > 21 ) {
//Toggle Buttons
jbDeal.setEnabled(true);
jbHit.setEnabled(false);
jbStand.setEnabled(false);
jbDoubleDown.setEnabled(false);
message = "You bust.";
}
}
});
You could create an array of "action commands" and every time you click the button, the action command changes to the next. If you reach the end, set the index back to zero. Perhaps something like this:
public static void main(String[] args)
{
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JButton button = new JButton("Action");
String[] commands = {"command1", "command2", "command3"};
button.setActionCommand(commands[0]);
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
JButton btn = (JButton)e.getSource();
String cmd = btn.getActionCommand();
System.out.println("Command: " + cmd);
if(cmd.equals("command1"))
{
btn.setActionCommand(commands[1]);
System.out.println("Command 1 was pressed");
}
else if(cmd.equals("command2"))
{
btn.setActionCommand(commands[2]);
System.out.println("Command 2 was pressed");
}
else if(cmd.equals("command3"))
{
btn.setActionCommand(commands[0]);
System.out.println("Command 3 was pressed");
}
else
System.out.println("Something went wrong!");
}
});
panel.add(button);
frame.add(panel);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
If you are using Java 7 or later, you can replace the if/else with a Switch statement.
I believe you're looking for a logical flag based on an integer, for example:
if(cardsDealt < 3) {
// DoThings();
} else {
return;
}
Which would require you to do
cardsDealt++;
at the bottom of your button click handle.
If this is not what you're asking, please re-explain the question.
It sounds like you might be a little confused about how jbHit will be called. Realize that it will be called one complete time every time the mouse is clicked. It's not like it inherently knows that it is on the second or third click. Add a class member like int clickCount; that you increment at the appropriate point inside of jbHit. Then you can alter your method's response depending on the value of clickCount.
For this program, the JButton doesn't seem to show up unless you click the area where the JButton should be; the JFrame starts up blank. The moment you click the button, the respective code runs and the button finally shows up.
How do I get the buttons to show up upon starting the program?
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
/*
The Amazing BlackJack Advisory Tool by JoshK,HieuV, and AlvinC.
Prepare to be amazed :O
*/
public class BlckJackUI {
//main class, will contain the JFrame of the program, as well as all the buttons.
public static void main(String args[])
{
//Creating the JFrame
JFrame GUI = new JFrame("Blackjack Advisor");
GUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GUI.setSize(1300, 900);
GUI.setContentPane(new JLabel(new ImageIcon("C:\\Users\\Hieu Vo\\Desktop\\Green Background.png")));
GUI.setVisible(true);
// Because each button needs to run through the Math class.
final Math math = new Math();
// The Ace Button:
ImageIcon Ace = new ImageIcon("/Users/computerscience2/Downloads/Ace.jpg");
JButton ace = new JButton(Ace);
ace.setSize(300, 100);
ace.setLocation(100, 100);
ace.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//Automatically default the the Ace to 11, and if Bust, Ace becomes 1.
if (math.array.playerhandtotal <= 21)
{
math.cardvalue = math.cardvalue + 11;
}
else
{
math.cardvalue = math.cardvalue + 1;
}
math.array.clicktracker++;
math.calcResult();
JOptionPane.showMessageDialog(null,math.array.result);
}
});
GUI.add(ace);
ImageIcon Two = new ImageIcon("/Users/computerscience2/Downloads/2.jpg");
JButton two = new JButton(Two);
two.setSize(300, 100);
two.setLocation(100, 200);
two.addActionListener(new ActionListener ()
{
public void actionPerformed(ActionEvent e)
{
/*
This generally repeats throughout the whole class, and the only
thing different is the changing cardvalue. When a button is pressed,
respective cardvalues are added into the playerhand ArrayList, and
totaled up to form playerhandtotal, which is a major factor in
bringing up the advice.
*/
math.cardvalue = math.cardvalue + 2;
math.array.clicktracker++;
math.calcResult();
JOptionPane.showMessageDialog(null,math.array.result);
}
});
GUI.add(two);
Et cetera, Et cetera... Just a bunch of the same stuff, more buttons coded the same exact way as JButton two, but with different value associated to them.
JButton start = new JButton("Start/Reset");
start.setSize(300, 100);
start.setLocation(500,500);
start.addActionListener(new ActionListener ()
{
public void actionPerformed(ActionEvent e)
{
/*
The start button also acts like a reset button, and the concept is fairly
simple. If we reset all the important values to 0 or "null," then the
program acts as if it was just opened.
*/
Arrays array = new Arrays();
array.playerhand.clear();
array.dealer = 0;
math.array.starttracker++;
math.array.clicktracker = 0;
array.playerhandtotal = 0;
math.cardvalue = 0;
array.result = null;
JOptionPane.showMessageDialog(null,"Please select the card \nthat the dealer is showing :)");
}
});
GUI.add(start);
GUI.setLayout(null);
This is all in the same class, and I understand a layout would be nicer, but perhaps there's a way to fix this issue using what I have now?
The program starts blank because you are calling setVisible before you add components. Call setVisible after you add components (in the end of contructor) and it should work fine.
Also, avoid absolute positioning and call of set|Preferred|Minimum|MaximumSize methods for your components. Learn how to use layout managers.
Write GUI.validate(); after you add all the components to your frame. Any time you add something to a frame you have to validate it like this. Otherwise, you will get the behavior that you have described.
No. The problem is caused by the layout. Before adding anything to a JFrame or a JPanel which you want it to have an absolute position, you have to setLayout(null). Otherwise, you'll have unexpected behaviours all the time. I just figured it out by myself after three hours of experimenting; suddenly, I connected your post with other different subject, and it worked, but this isn't well explained on the Internet yet.
I have a class that creates a JFrame and needs to have one image displayed in the JFrame. Images must rotate every few seconds.
I've tried using repaint(), revalidate(), and validate() on both the JFrame and JPanel and none seem to work. Following is my current code to update the JFrame/JPanel. This code will wait 4 seconds and then display the fourth image, but I would like it to update every second with a new image.
public void startSlideshow(){
int i = 0;
Long oldTime = System.currentTimeMillis();
do {
Long currentTime = System.currentTimeMillis();
if( currentTime - oldTime >= 1000 ) {
img = new JLabel(new ImageIcon("Albums/" + album + "/" + imageNames.get(i)));
panel.removeAll();
panel.setBackground(Color.BLACK);
panel.add(img);
panel.revalidate();
panel.repaint();
i++;
oldTime = System.currentTimeMillis(); // reset reference time
}
} while(i<imageNames.size());
panel.removeAll();
panel.setBackground(Color.BLACK);
panel.add(img);
panel.getRootPane().revalidate();
panel.repaint();
}
Any help is greatly appreciated as I've been banging my head for a while now. Thanks.
You're blocking the Event Dispatching Thread, preventing it from process new repaint requests.
Instead of using a loop and Thread.sleep, try using a javax.swing.Timer
Take a look at How to Use Swing Timers and Concurrency in Swing
You may also find it simpler to change the JLabel's icon instead of creating a new JLabel on each time through...
Hi I am writing a simple program to display a frame.
However the frame turns out real small when I type setLayout(null);
But if i ignore this command, the button is always at the top center
Can some one point out my error?
import java.io.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
class theframe {
private static void create() {
JFrame frame = new JFrame("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Insets insets = frame.getInsets();
frame.setSize(500 + insets.left + insets.right, 350 + insets.top + insets.bottom);
add(frame.getContentPane()); //my function add
frame.pack();
frame.setVisible(true);
}
public static void add(Container pane) {
pane.setLayout(null);
Insets insets = pane.getInsets();
JPanel p1 = new JPanel();
p1.setPreferredSize(new Dimension(500, 350));
JButton b1 = new JButton("one");
Dimension size = b1.getPreferredSize();
b1.setBounds(25 + insets.left, 5 + insets.top, size.width, size.height);
pane.add(p1);
p1.add(b1);
}
public static void main(String Args[]) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
create();
}
});
}
}
If you see the pack() function it is not in JFrame.java but Window.java
public void pack() {
Container parent = this.parent;
if (parent != null && parent.getPeer() == null) {
parent.addNotify();
}
if (peer == null) {
addNotify();
}
Dimension newSize = getPreferredSize();
if (peer != null) {
setClientSize(newSize.width, newSize.height);
}
if(beforeFirstShow) {
isPacked = true;
}
validateUnconditionally();
}
now if you see getPreferredSize() function in Container.java
#Deprecated
public Dimension preferredSize() {
/* Avoid grabbing the lock if a reasonable cached size value
* is available.
*/
Dimension dim = prefSize;
if (dim == null || !(isPreferredSizeSet() || isValid())) {
synchronized (getTreeLock()) {
prefSize = (layoutMgr != null) ?
layoutMgr.preferredLayoutSize(this) :
super.preferredSize();
dim = prefSize;
}
}
if (dim != null){
return new Dimension(dim);
}
else{
return dim;
}
}
Everything boils down to layoutMgr.preferredLayoutSize(this);
Since you are not setting the layout(or null) you are getting that issue. Either remove that statement or use some layout.
You're calling pack() method with absolute positioning (null layout), you should use LayoutManager instead of using setting the layout to null.
Just remove this line:
pane.setLayout(null);
NOTE: take my advice and learn yourself LayoutManagers, because if you refuse to learn, you'll probably go to null layout, yes,it is easier but highly recommended to not use it.
Some basic things you must know:
The default layout of JFrame is BorderLayout, which there only five locations you can put your components in as shown in the link.
FlowLayout is the default layout manager for every JPanel. It simply lays out components in a single row, starting a new row if its container is not sufficiently wide.
After all, read more about LayoutManagers before starting using swing, belive me it makes your work very easier, just try it, it's amazing.
Java GUIs might have to work on a number of platforms, on different screen resolutions & using different PLAFs. As such they are not conducive to exact placement of components.
To organize the components for a robust GUI, instead use layout managers, or combinations of them, along with layout padding & borders for white space.
See this answer for tips on how to:
Combine layouts.
Provide white space using layout constructor arguments & borders.
I had the same issue luckly, I solved with one line code. May this will also help you try to follow these steps:
1- Goto in your file code
2- In start of code find this line:
initComponents();
3- Just add this new code after this line:
this.setMinimumSize(new Dimension(300, 300));
The keyword "this" here representing your current top-level component, so new dimensions as (300, 300) is applied here. You may change this 300 values according to your required frame / component size.