Why is JFrame not adding a component properly - java

I'm having an error when trying to add a component to a JFrame.
This is the first class:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class FrameG extends JFrame
{
private static final String MOVE_UP = "move up";
public static int frameID = 1;
private static JFrame window = new JFrame();
private static openWin frame = new frame01();
public static void main(String[] args) {
window.setSize(1500,900);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
window.setResizable(true);
frame.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), MOVE_UP);
frame.getActionMap().put(MOVE_UP, new movement());
mainloop();
}
private static void mainloop()
{
window.removeAll();
switch(frameID)
{
case 1:
frame = new frame01();
frame.setLayout(new FlowLayout());
System.out.println(frame);
window.add(frame);
break;
default:
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(FrameG.class.getName()).log(Level.SEVERE, null, ex);
}
mainloop();
}
}
class movement extends AbstractAction
{
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("hi");
}
}
and the second class (it extends a class with an abstract paint method that extends JComponent):
import java.awt.Graphics;
import java.awt.*;
import javax.swing.JComponent;
import java.awt.geom.*;
public class frame01 extends openWin{
#Override
public void paint(Graphics g) {
Graphics2D pic = (Graphics2D) g;
pic.setBackground(Color.BLACK);
}
}
The error may be the invalid part, but i'm not sure what it is:
frameg.frame01[,0,0,0x0,invalid,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=0,maximumSize=,minimumSize=,preferredSize=]

You code shows a distinct lack of understanding into how Swing works
This...
private static void mainloop()
{
window.removeAll();
switch(frameID)
{
case 1:
frame = new frame01();
frame.setLayout(new FlowLayout());
System.out.println(frame);
window.add(frame);
break;
default:
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(FrameG.class.getName()).log(Level.SEVERE, null, ex);
}
mainloop();
}
Is a bad idea of two reasons, the first been that it will eventually generate a StackOverflowException, the second been that it violates the single thread rules of Swing.
Normally, a while-loop would generally be better, but as you're dealing with Swing based components you should consider using either a Swing Timer or SwingWorker, depending on what you are hoping to achieve.
Constantly adding and remove components also seems like a weird idea, but there's no context to the question to know what it is you're hoping to achieve. Having said that, as a general rule, if you want to switch between views a CardLayout is generally considered the preferred solution
This...
public class frame01 extends openWin{
#Override
public void paint(Graphics g) {
Graphics2D pic = (Graphics2D) g;
pic.setBackground(Color.BLACK);
}
}
is simply doing nothing, other then breaking the paint chain, which could cause no end of weird paint artefacts to be generated. All you code does if temporarily changes the background color of the Graphics context, but since you don't paint anything with it, it's effectively meaningless
As a general rule, it's recommended to override the paintComponent method of Swing components (and call super.paintComponent before you do any custom painting).
I would highly recommend that you take a look at:
Painting in AWT and Swing
Performing Custom Painting
Concurrency in Swing
How to use Swing Timers
Worker Threads and SwingWorker
How to Use CardLayout

Your code has lots of problems. The line private static openWin frame = new frame01(); seems fishy as I said in my comment. But its not the worst part:
THIS:
private static void mainloop()
{
window.removeAll();
switch(frameID)
{
case 1:
frame = new frame01();
frame.setLayout(new FlowLayout());
System.out.println(frame);
window.add(frame);
break;
default:
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(FrameG.class.getName()).log(Level.SEVERE, null, ex);
}
mainloop();
}
Is BAD! And it probably breaks since you are constantly creating a new frame01() and adding it to your window.
Also DO NOT override paint but paintComponent. Another thing I dont get is why override it in a separate class and not in openWin itself.
Another thing, DO NOT SLEEP THE MAIN THREAD, if you really must then run your own and put IT to sleep.
What is the purpose of this code? I'm trying to understand it so I can help you redesign it.
PS: not really an answer, just a bit longer than a comment.

Never use a Thread and it's start and stops as a timer. Use javax.swing.Timer instead. Also you've created a recursive function to implement an infinite loop which is very wired and prone to fail. Swing has it's own thread and when you mix it up with another threads unconsciously it transform your code to a monster we are all afraid of.

Related

Does "Thread.sleep()" do anything else than pause the program for a certain amount of time?

I have created a program that draws a thick line.
import javax.swing.*;
import java.awt.*;
public class Movement {
int xGrid = 50;
public static void main(String[] args) {
Movement m = new Movement();
m.animate();
}
public void animate() {
JFrame frame = new JFrame("Movement");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ScreenDisplay display = new ScreenDisplay();
frame.getContentPane().add(display);
frame.setSize(400, 400);
frame.setVisible(true);
for (int aL = 0; aL < 200; aL++) {
xGrid++;
display.repaint();
try {
Thread.sleep(50);
} catch (Exception ex) { }
}
}
class ScreenDisplay extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.RED);
g.fillOval(xGrid, 175, 50, 50);
}
}
}
Because of the method "Thread.sleep(50)", the speed of the program slows down a little.
So I got a little curious and removed the "sleep()" method.
What I expected to output was the exact same output, just extremely fast.
However, it just prints out one circle in the frame.
I don't really know why it outputs just one circle, none of the researches I've done back up the answer.
Can anyone please explain why?
From Component.repaint documentation:
Repaints this component.
If this component is a lightweight component, this method
causes a call to this component's paint
method as soon as possible. Otherwise, this method causes
a call to this component's update method as soon
as possible.
By the looks of it, your for loop finishes so quickly that by the time the component calls the repaint method, it has already finished and therefore only paints the final circle stored in the buffer.

keyListener with Swing and game level system with Swing/keyListener and Thread

I am a novice coder, have a tiny bit of experience with C++ about 10 years ago and now learning java (it's been about 4-5 months). I have a little collaborative project going, and I've got some things to figure out.
Here's the code:
import java.awt.event.*;
import java.awt.*;
import javax.swing.JFrame;
import java.util.Scanner;
import java.util.Random;
public class Game extends JFrame {
GamePanel panel;
int[][] grid;
int size;
//...and some other variables
public Game(String title) {
super(title);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new GamePanel(grid,size);
this.add(panel);
Button button = new Button("WHAT");
button.setBounds(-100, -100, 70, 70);
this.add(button);
button.addKeyListener(new KeyHandler());
}
class KeyHandler extends KeyAdapter {
public void keyPressed(KeyEvent e) {
int keycode = e.getKeyCode();
switch(keycode) {
//arrow keys input and stuff
}
if(checkForWin()) //checkForWin() returns a boolean value for win/loss
//won the game, thus end the thread here
if(checkForLoss()) // similar stuff as checkForLoss()
//lost the game, thus end the thread here
//...update the panel
panel.repaint();
}
}
public static void main(String [] args) {
Game me = new Game("GAME");
me.setVisible(true);
}
}
So that's pretty much how the whole game thing looks like.
And I have questions:
I am using a button and put it at a negative position to make it invisible and using it as a mean of KeyListener. Is there any other way to add a key listener? ex) to the panel or something?
I want to change it so that each "level" is a thread and make it like
public static void main(String [] args) {
int level = 1;
do {
GameThread gt = new GameThread("Game");
// run() will have the game constructor inside
gt.setLevel(level);
gt.start();
gt.join(); //wait until the game "level" is finished
if(won)
level++;
if(lost)
level = 1;
} while(!checkIfDonePlaying())
}
Somewhat like this. I'm having trouble making the thread continue to run until the game level is actually finished. How do I do that?
I want to add a JLabel to show the score on the frame. But when I do that, the score doesn't update when I repaint() it. How do I do that?
Thanks in advance!
A few things:
Yes, there is a way to add a KeyListener to the panel, and that's by using key bindings. For example:
javax.swing.InputMap im = panel.getInputMap(panel.WHEN_IN_FOCUSED_WINDOW);
javax.swing.ActionMap am = panel.getActionMap();
im.put(javax.swing.KeyStroke.getKeyStroke("pressed UP"), "up");
am.put("up", new javax.swing.AbstractAction() {
#Override
public void actionPerformed(ActionEvent ev) {
// handle up arrow key action here
}
});
Swing is an event-driven environment, so do-while loops should not be used. Instead, use a Swing timer that periodically checks if your level has been completed.
Since you're not doing any custom painting on your JLabel, you shouldn't be using repaint. Instead, use its setText method.
Here's a solution for your first issue:
panel.setFocusable(true);
panel.addKeyListener(this);
Then you should just be able to do the usual key listener methods. Be sure to implement KeyListener!
Solutions for your other issues on their way. (Actually take #TNT 's suggestion and use keybindings, I'm more comfortable with listeners but for games I usually use lwjgl or slick2d)
2.
I feel that running your programs levels may be a bit inefficient (that may just be me) I would suggest having one thread in a run method and have the following:
Public void reset(int level)
{
//reset all variables based on local variable level...
}
Call it like this:
Reset(++currentLevel)); //use ++ before currentLevel so it adds to it and then resets
And you could easily use switch cases to do some special stuff if you want
Switch(level){
case 1: //...
}
And so forth. (I'm typing on mobile sorry for weird caps)

Java AWT - repaint() method thread scheduling

What I am looking to do is have the user to be able to change perspectives from a KeyListener. If the user hits the specified key, than the perspective should change. Any ideas?
Even if I override the methods they still do not work. I have also tried KeyAdapter
package com.development.gameOne.environment.component;
import java.applet.Applet;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import com.development.gameOne.environment.applet.drawing.Perspective;
import com.development.gameOne.environment.applet.perspectives.p1.FirstPerspective;
import com.development.gameOne.environment.applet.perspectives.p2.SecondPerspective;
public class Component extends Applet implements KeyListener {
private static final long serialVersionUID = 1L;
private Dimension size = new Dimension(1280, 720);
private ArrayList<Perspective> perspectives = new ArrayList<Perspective>();
private boolean running = true;
private boolean switchPerspective = false;
public Component() {
setPreferredSize(size);
loadPerspectives();
addKeyListener(this);
setFocusable(true);
setVisible(true);
start();
}
private void loadPerspectives() {
perspectives.add(new FirstPerspective());
perspectives.add(new SecondPerspective());
}
public static void main(String[] args) {
new Component();
}
#Override
public void paint(Graphics g) {
while (running) {
for (Perspective p : perspectives) {
System.out.println(p.getPerspective());
while (!switchPerspective) {
System.out.println("Rendering");
p.start(g);
sleep(100);
}
switchPerspective = false;
}
sleep(10);
}
}
public static void sleep(int renderSpeed) {
try {
Thread.sleep(renderSpeed);
}
catch (Exception e) {}
}
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()){
case KeyEvent.VK_SHIFT:
System.out.println("KeyPressed");
switchPerspective = true;
break;
}
}
public void keyTyped(KeyEvent e) { }
public void keyReleased(KeyEvent e) {}
}
The program runs, but doesn't switch perspectives. I cannot seem to get the KeyListener
to work at all. I really have no idea what to do.
I don't think the issue is with your KeyListener, but is with your paint process
#Override
public void paint(Graphics g) {
while (running) {
for (Perspective p : perspectives) {
System.out.println(p.getPerspective());
while (!switchPerspective) {
System.out.println("Rendering");
p.start(g);
sleep(100);
}
switchPerspective = false;
}
sleep(10);
}
}
This will block the Event Dispatching Thread, preventing it from ever been able to process new events coming into the system
Take a look at Painting in AWT and Swing for details about how painting works in AWT.
The (simple) solution, in this case, would be to provide a other Thread which handles the timing between updates and simple call repaint when you want the UI updated.
A better solution would be take take advantage of the a BufferStrategy instead. It still require a Thread, but stops you from breaking the painting chain.
As a side note. AWT Applets are woefully out-of-date and were replaced by JApplet before 2000. Having said that, I would recommend against using applets at all, as they have enough problems which only increases the difficulty of starting development and focus on something like a JPanel added to an instance of a JFrame instead.
Take a look at Performing Custom Painting and Creating a GUI With JFC/Swing
I'd also drop the use of KeyListener as soon as you can in favour of Swing's Key bindings API. See How to Use Key Bindings for more details
I'd also avoid calling your applet Component, there already is a class called Component and this is just going to confuse matters...
And applets, definitely, should not have a main method. They are expected to be loaded by the browser directly and have a different, defined, life cycle.

Not getting the output as expected in Swing

i know multithreading a bit but not in vast and i think the problem is of multithreading. I am calling a method to set label's text by invoking a new thread and leaving it blank after a specified time. I am getting the desired output every time but not only the place which i am going to show you by my piece of code. I am expecting that message should be set and disappeared after the specified time and the window should be minimized after that time. But what actually happening is when it is going to the other thread main thread execution starts and goes for sleep for 5 sec and the message is not appearing and after 5 sec window is getting minimized without showing the message which i am setting on the label.
(Main thread)
Validation.setMessageOnLabel("Username and password has been copied", jLabel15,1.5F);
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
Logger.getLogger(PasswordManager.class.getName()).log(Level.SEVERE, null, ex);
}
setState(ICONIFIED);
validation.java (setMessageOnLabel())
static public void setMessageOnLabel(final String msg, final JLabel label, final float time)
{
new Thread(new Runnable() {
#Override
public void run() {
label.setText(msg);
try {
Thread.sleep((long) (time*1000));
} catch (InterruptedException ex) {
Logger.getLogger(PasswordManager.class.getName()).log(Level.SEVERE, null, ex);
}
label.setText("");
}
}).start();
}
Since you're calling setState() directly, I assume the first code snippet is part of a JFrame. In that case you're most probably sending the event dispatch thread to sleep for 5 seconds and thus prevent screen updates during that time.
Put the sleep into another thread or use a swing worker instead and call setState() on the EDT in the worker's callback method, since setState() is not labelled as thread-safe and calling it on a thread other than the EDT might result in unexpected behavior.
From the linked tutorial:
Some Swing component methods are labelled "thread safe" in the API specification; these can be safely invoked from any thread. All other Swing component methods must be invoked from the event dispatch thread. Programs that ignore this rule may function correctly most of the time, but are subject to unpredictable errors that are difficult to reproduce.
Don't use Thread.sleep(5000);, that block EDT.
For that purposes you can use swing Timer, examine next example:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
public class TestFrame extends JFrame {
private JLabel lbl;
public TestFrame() {
init();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private void init() {
lbl = new JLabel(" ");
JButton setText = new JButton("setText");
setText.addActionListener(getActionListener());
add(lbl);
add(setText,BorderLayout.SOUTH);
}
private ActionListener getActionListener() {
return new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lbl.setText("wait...");
Timer t = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lbl.setText("");
setState(JFrame.ICONIFIED);
}
});
t.setRepeats(false);
t.start();
}
};
}
public static void main(String args[]) {
new TestFrame();
}
}
When dealing with Swing components you shuld not use threads like that. Launch your own SwingWorker instead.
public class MySwingWorker extends SwingWorker<Object, Object> {
#Override
public Object doInBackground() {
//your code here
//dont forget to repaint changed component or validate parent of it,
//if your text dont shows up.
return null;
}
}
you can also execute your own runnable via SwingUtilites
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//again your code here...
}
});

Applet works, but swing components won't appear!

So I am creating this applet which I want to have full on swing components in it. I have looked at all the docs, I have made the applet, and I can get something to show up in it if I override the update(Graphics g) method, but simply adding components to the contentPane doesn't seem to be doing it! What am I doing wrong?
import javax.swing.JApplet;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import org.steephill.kindlab.LabApp;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
public class ClientApplet extends JApplet {
ClientTreePanel treePanel;
public void destroy() {
// Put your code here
}
public String getAppletInfo() {
return "KindLab Client Applet";
}
public void init() {
try {
LabApp.initializeHibernate();
if (!LabApp.authenticate("user", "pass")) {
JOptionPane.showMessageDialog(this, "authentication failed");
} else {
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception e) {
System.err.println("createGUI didn't successfully complete");
}
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(this, "error intitializing applet\r\n" + ex.getMessage());
}
}
protected void createGUI() {
treePanel = new ClientTreePanel();
treePanel.setVisible(true);
getContentPane().add(new JLabel("TESTING!"));
getContentPane().add(treePanel);
System.out.println("THIS DOES RUN");
}
public void start() {
// Put your code here
}
public void stop() {
// Put your code here
}
/* if I uncomment this method, it WORKS and I get "Hello World!"
public void paint(Graphics g) {
super.paint(g);
g.drawString("Hello World!",25,25);
}
*/
}
Please, help! And thank you!
Joshua
I see several problems with your code here:
you do not call pack() at the end of your GUI setup
you add several components to the applet's content pane, but without any layout constraints. The default content pane usually is a BorderLayout, so adding two components without any constraints will probably only put the ClientTreePanel on top.
Since you do not call pack(), the layout will not be calculated, which for your case probably results in nothing being displayed (you did not provide the code for ClientTreePanel).
You shouldn't have to call pack() - the layout will be calculated when the component first becomes realized, which happens when you call pack - but also when the component first becomes visible.
The "adding components without constraints" is on the right track - you should change the code adding components to the content pane to :
getContentPane().add(new JLabel("TESTING!"), BorderLayout.NORTH);
getContentPane().add(treePanel, BorderLayout.CENTER);
The other problem is why your ClientTreePanel component isn't showing up - it could be sizing, or a layout problem, or other things - but without seeing that code, it would just be guesses.
Try to delete your paint method and you'll see it works. The problem might be the fact that since you've got a paint method all the changes are made through it. It's weird though because it does display a JButton.

Categories

Resources