Since I'm not a CS major, I'm having some difficulties translating my programming wishes into an actual program.
What it basically boils down to is the following: how can I alternate an image on a label, showing each image for an amount of tim specific for each image.
So: say I've images A and B; I'd like the user to see A for 1000ms and B for 200ms. This keeps on looping until a user presses a certain key.
Now, I'm able to load an image onto a panel, quite easily even, and I've managed to catch user input using KeyListener and stuff, which all works quite nicely and alot easier then I had expected. I also know how to use looping constructs like while, for and do..while, but this timer business is shady.
I see all kinds of stuff using threads and what not, I really don't need that. This is not about efficient programming or good code, it's simply about demonstrating something.
Any help would be greatly appreciated!
Use a SwingWorker<Void, Void>. The doInBackground method of the SwingWorker should look like this :
#Override
protected Void doInBackground() {
try {
while (true) {
displayImage(imageA);
Thread.sleep(1000L);
if (isCancelled()) {
return null;
}
displayImage(imageB);
Thread.sleep(200L);
if (isCancelled()) {
return null;
}
}
}
catch (InterruptedException e) {
// ignore
}
return null;
}
private void displayImage(final Icon image) {
SwingUtilituies.invokeLater(new Runnable() {
#Override
public void run() {
// display the image in the panel
}
});
}
The keylistener should simply cancel the SwingWorker.
Here's something that might be a good example:
http://www.java2s.com/Code/Java/Development-Class/UsejavautilTimertoscheduleatasktoexecuteonce5secondshavepassed.htm
I can try to explain the code if it appears confusing
There is nothing necessarily inefficient about using threads when threads are the right tool for the job.
In this case, it would not be unreasonable to create a new class that implements Runnable, which holds a reference to the label you wish to change the image on.
This means that the image could be changed without causing waits on the main application that would cause it to hang until it was done.
You would want to avoid 'Busy Loops' [basically, a while loop with no Thread.sleep() within it], and look to see if there is any needed thread exit criteria
Related
I am having troubles with programming a game called "Coup" and to explain what the problem is, i need to briefly explain what the game is about. Coup is a card game where players try to eliminate each other and be the last player left. To do this, players can choose a number of actions but they can also lie to other players. In this game, other players can call "bullshit" or block another players action. Because of this, i need to make Response windows which will let other players respond with a challenge, a block or do nothing. The result of the action is then dependent on the responses. For example, a player tries to get foreign aid. I can decide to block his action or to let him continue. There are consequences if i block but that isnt the problem here.
The real problem is this: I want to give all my players a chance to give a response (Block or do nothing) via a JFrame and I use a while loop to wait for all players responses. However, this while loop doesnt do anything except keep the method from progressing since the result depends on players reactions. With the while loop, the JFrame I constructed appears white and doesnt contain the two buttons i made. At this point i can even press the close button and it wont respond due to the while loop I made which is dependent on my responseless window. Is there anyway to fix this?
if (turnCount == players[currentPlayerNum].getPlayerNum()){
for(int i=0; i<players.length;i++){
//If it is not the players turn and they have one or two lives, make response window
if (players[i].getPlayerNum() != turnCount){
if (players[i].getLives() != 0){
//foreignAidBlock constructs the response window
players[i].foreignAidBlock(turnCount);
}
else{
//Not applicable since out of game/not in game
players[i].setResponse("NA");
}
}
else{
//Not applicable since out of game/not in game
players[i].setResponse("NA");
}
}
//Idea: PAUSE TILL RESPONSES COME IN
while(players[0].getResponse() != null || players[1].getResponse() != null || players[2].getResponse() != null || players[3].getResponse() != null)
{
System.out.println(players[0].getResponse());
System.out.println(players[1].getResponse());
System.out.println(players[2].getResponse());
System.out.println(players[3].getResponse());
}
The result i get from running this part of the code is:
"NA
null
NA
NA"
continuously. I use a null to represent a player that hasnt answered yet. At the end of the method, I reset all of my players abilities back to null.
All my response window will do is change the response of the player from null to "B" or "N". But without access to the buttons I cannot do this. Does anybody have any tips on how to use something to "pause" my thread besides using a while loop?
As Abdul Fatir mentioned, it is because the UI thread is being blocked.
I would however not reccomend directly messing with threads. There is a SwingWorker class exactly for this scenario and it is reccommended you use this.
For more info check this blog article - Swing Worker Example
You can also find some useful info in the following SO question - How do I use SwingWorker in Java
Here is quick use example from the official javadoc:
Say we want to find the "Meaning of Life" and display the result in a JLabel.
final JLabel label;
class MeaningOfLifeFinder extends SwingWorker<String, Object> {
#Override
public String doInBackground() {
return findTheMeaningOfLife();
}
#Override
protected void done() {
try {
label.setText(get());
} catch (Exception ignore) {
}
}
}
(new MeaningOfLifeFinder()).execute();
The while loop is blocking your UI Thread. Perform your while loop checks in a different Thread.
Thread t = new Thread(new Runnable(){...});
t.start();
This way your UI won't be blocked and controls will remain responsive.
I am learning java at the moment and trying to make different features and systems. Right now I am trying to make a really simple particle system, but I have problem.
public void eDelay()
{
try
{
Thread.sleep(17);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
public void engine()
{
int i = 0;
while (i < particles.size())
{
Particle a = (Particle) particles.get(i);
eDelay();
a.move();
i = i + 1;
}
}
public void actionPerformed(ActionEvent e)
{
engine();
repaint();
}
This is a part of my code where the particles suppose to move. It works fine and they do move, but I want them to move one after another one with a delay(17). However, they move all at once and not one after another.
(particles) is an arrayList with 50 elements in it, each element containst a position for a particle.
(a.move()) is just a movement method where particle moves up.
Don't worry for the rest of the code, the problem is in this part.
Graphics, positioning of the particles, movement of the particles, and other things work fine.
Going to make my comment an answer:
"Don't worry for the rest of the code, the problem is in this part. - Graphics, positioning of the particles, movement of the particles, and other things work fine."`
How can you say this when you know what the cause of your problem is? You could be creating a Swing application (which is part of the rest of the code) and calling Thread.sleep(...) on the event thread. So make no assumptions, tell us all of the story, show more pertinent code, and let's help you solve this.
Edit: I now see that this is in fact a Swing application and so that's likely your problem, although you don't show us where the Thread.sleep is called.Calling Thread.sleep(...) puts the thread that calls it asleep, and if this is the Swing event thread (or EDT), it will put your entire application to sleep -- not a good thing to do. So don't call Thread.sleep on the event thread and use a Swing Timer instead. The tutorials will show you how.
And of course again, don't make any assumptions.
You should call repaint in the engine() loop instead of actionPerformed().
Ok guys, this is actually a bit weird but I just rewrote the engine class again into a for loop and it works.
public void engine()
{
goingcrazy = goingcrazy + gameTimer.getDelay();
for(int i = 0; i < particles.size(); i++)
{
if (goingcrazy > 1000*i)
{
Particle a = (Particle) particles.get(i);
a.move();
}
}
}
I already posted a link for the full code so if you are interested you just need to change the old engine class with the one I just posted.
I have two basic questions.
I have a GUI project with Java Swing. When I put buttons on the frame and I double clicked them, I had the code of the actionPerformed, but it is blocked.
How can I put there a button and then use it on a actionListener?
My Project is about Server-client (multithread and sockets)
I call one method to reiceve one string that we can write on a JtextField and it stays on a while cicle with PrintWriter and a getOutputStream.
Something like:
do{
...
}while(thisstring!=null || thisstring!="exit")
So.. when I write something and press the button to send it, it stays on the cicle and the button blocks. How can I unblock the button to write something else?
Edit:
I understood the EDT problem, but I can't solve it.
I tried use the Timer but without success, something like that:
int delay = 1000; //milliseconds
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
//My action calling the Thread class with the while cicle that has the PrintWriter
}
};
new Timer(delay, listener).start();
How can I handle this to do the timer when I press the button?
How can I stay on the that cicle (read the comment line) to send the information by OutputStream every time that one user enter something on the text field?
I know that for example for a console application I use a BufferedReader and then I use the ReadLine() to wait for anything sent from the console, but with GUI interface it freezes all time..
There is a fundamental concept in Java GUI development surrounding which thread in which the developer implements user-interaction processing such as button clicks.
In short, you need to perform your processing outside of the thread that calls your action handling method. This single thread is known as the Event Dispatch Thread (EDT), and if you have logic that runs much more than a few milliseconds, it will prevent the UI from continuing to draw things like the button releasing, etc.
You'll want to move your long-running, socket code off the EDT. Doing so will allow the button to release and let the user interact with other controls (or even the same button).
To avoid duplicating other discussions on the topic, I direct you to this pretty good one. Additionally, this article gives a short overview of threading concepts in Swing.
Regards,
ScottH
According to your comment you have some naming issues there. You need a class that implements the ActionListener-interface like so: class YourListenerClass implements ActionListener, but you could also do that via an anonymous class like new ActionListener {
public void actionPerformed(ActionEvent e) {
//your code for your button here
}
});
when you set your ActionListener.
The crucial thing is that you need to name your method the correct way. It MUST be public void actionPerformed(ActionEvent e) and you definitely have to implement the ActionListener-interface.
The next thing is that you have to register your listener in your button like:
yourButton.addActionListener(new YourListenerClass);
or
insert an anonymous class like I showed to you before.
The 2nd thing sounds like an multithreading issue like I mentioned in my comment. I didnt follow scotth's link, but according to his description this might be a source you want to read to solve any further blocking issues.
EDIT:
Well, at first I didn't want to explain it, because it's quite a chunk of code, but as the problem persists I want to add something about SwingWorkers in my answer.
If you have long running code, it wont help to use a Timer as the code invoked by it will also be on the EDT as it's triggered by an event.
Instead of that you could use a SwingWorker to solve this. This needs some extra code, though.
Here's a simple approach you could follow:
public class WorkingHard{
SwingWorker<String, String> worker;
JButton yourButton = ...;
...
//do some cool stuff, as register those listeners!
...
public void actionPerformed(ActionEvent evt){
if(evt.getSource().equals(yourButton);
// Construct a new SwingWorker
worker = new SwingWorker<String, Void>(){
#Override
protected String doInBackground(){
//do your reading in this method, it will be executed in an own thread
String readText = "i will be read".
/*your reading algorithm, you could also call publish(...) to post your results,
e.g. likewise), then you also have to override process(...). this process will be
thread save, too*/
readText += ... ;
...
return readText;
}
#Override
protected void done(){
try {
//do sth. with your result, now thread safe.
someGuiElement.setText(get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
// Execute the SwingWorker; the GUI will not freeze
worker.execute();
}
}
If you want to know more about those workers... there several threads dealing about it, e.g. this one.
I'm trying to accomplish something very simple. First, load my layout (main.xml). Then wait 1 second, modify an image, wait 1 second and modify it to a third image. (My end goal is more complex, of course, but I can't even get this to work).
Basically, I get a black screen when the app loads. It stays that way until all the waiting is over, then it shows the final image. Here's my code:
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageCard[0] = (ImageView)findViewById(R.id.imageView11);
Thread.sleep(1000);
ImageCard[0].setImageDrawable(getResources().getDrawable(R.drawable.secondimage));
Thread.sleep(1000);
ImageCard[0].setImageDrawable(getResources().getDrawable(R.drawable.thirdimage));
ImageCard[0] is the image I'm trying to change. It loads in main.xml and shows fine if I remove the rest of the code.
If I change the sleep time to 5000, it then takes 10 seconds before it finally comes away from the black screen and shows the third image. This is how I know it's not just loading slow, but that it's actually waiting.
It's like it's not running sequentially. I'm no java expert, so I'm assuming I'm doing something dumb...
Thanks for any help!
I think you are blocking the UI Thead. Try Handler.postDelayed on a static Handler object.
Ok heres your problem, you can never do a sleep(...) when you are in the UIThread. The UIThread is never suppose to be locked up, it causes a lot of very bad things to happen in android. But there is a very easy way around it, just get off the UIThread and hop back on it when you need to. Heres what i would recommend:
public void onCreate(...)
{
super.onCreate(...);
myActivity.setContentView(R.layout.main);
new Thread(this).start();
}
public void run()
{
try
{
changeLayout(R.layout.main2);
Thread.sleep(5000);
changeLayout(R.layout.main3);
Thread.sleep(10000)
changeLayout(R.layout.main4);
}catch(Exception e){}
}
public void changeLayout(int id)
{
this.id = id;
myActivity.post(new Runnable()
{
public void run()
{
myActivity.setContentView(id);
}
});
}
private int id;
Of course with this example your class must implement Runnable to work. Only the UIThread can access the UI, no other thread can. Thats why you have to hop on and off the UIThread. Hope this worked!
Try adding ImageCard[0].invalidate() when you want it to draw.
I think Hovercraft Full of Eels is pointing you in the right direction. Essentially, you're not doing any multi-threading, you're telling the main thread to wait which means that it never completes the drawing. I'm not sure about Android, but Swing uses double-buffering by default (to avoid screen flashes), but that means that what is drawn, is actually drawn on to a buffer, not the window itself which is why you don't see anything. You could try disabling the double buffering (which Android is likely using) but that could cause other issues.
You might want to actually do multi-threading, or, I'm sure Android likely has a Timer component. If it does, I'd suggest you use it over Thread.sleep or actual multi-threading. Using a Timer you can have it fire an event after one second. That event will execute the other code.
do you have that code in constructor or in init() function? if yes, draw just the first picture and the Thread.sleep() function move after the place which the constructor or the init() function was called from.
then call repaint() function or something.
I am wanting my error messages to display in a JLabel temporarily, then set the text back to "". But instead it looks like it just skips over setting the message. I've stepped through the code and found it's setting the text, but it isn't displaying for some reason. I've even tried the repaint() method, but still nothing. Any help would be greatly appreciated.
Here's what I have:
public void displayError(String msg){
int ctr = 0;
while(ctr<2){
try {
lblError.setText(msg);
lblError.repaint();
Thread.sleep(500);
} catch (Exception e) {}
ctr++;
}
lblError.setText("");
}
I'm assuming that you're calling this method on the event dispatch thread. (If not, you should be, since almost all Swing calls should be made there.)
You need to allow the thread to retake control, which it's not able to do because of your Thread.sleep(). Look instead at invoking a Timer or SwingWorker background thread which, after two seconds, will reset the text.
http://download.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html