I am working on a small sound player which plays songs. I am using the TinySound library https://github.com/finnkuusisto/TinySound. And as I can see from the API, it does come with a method called .done() which tells me wheter a Music object is finished playing or not, but how can I test for it while playing?
I have currently created a JFrame with buttons and a Jlist which displays the songs, but I understand that if I try some sort of while loop to listen for wheter or not the song is finished I wont be able to use the other buttons such as stop, pause etc.
I was thinking somewhere along this line (pseudo code):
while(theSong.playing()){
if(theSong.done()){
playNext();
}
}
The problem is that when entering the while loop, I am not able to use any other functions in my program. If anyone wants to see some sample code, please let me know!
Sindre M
Running this while on the same thread as your GUI will make the interface stuck. You can achieve the desired functionality using SwingUtilities invokeAndWait method:
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
while(theSong.playing()) {
if(theSong.done()) {
playNext();
}
}
}
}
Related
I'm making a small game and I've already implemented a save function in which the game is saved (by writing information to a new XML file). The saving takes a couple of seconds and I want to do the following: while the program is saving the game, I want to change the look of the JPanel, and when it is done saving, I want to go back to another page(show another JPanel).
I have the following code:
confirm.addActionListener(new ActionListener () {
#Override
public void actionPerformed(ActionEvent e){
String fileNaam = saveGame.getText();
//This method changes the look of the panel
changePanel();
//This method saves the game
model.saveGame(fileNaam);
//This method takes the user back to a previous page
controller.viewTeamPage();
}
});
What happens is that the game is saved and the user is taken back to the teampage, but the panel is never changed. The changePanel() method does work, so that is not the problem but it seems like it is never executed. I was wondering if somebody knows how I can fix this.
EDIT:
private void changePanel () {
panel.removeAll();
panel.repaint();
panel.revalidate();
}
This is the method to change the look of the panel, for now I just remove everything on the panel to keep it simple.
Also, the saving is not done in a separate Thread, is that something I should look at?
EDIT 2: I fixed it by using a thread to save the game and return to the teampage after the saving is done. See the code below.
confirm.addActionListener(new ActionListener () {
#Override
public void actionPerformed(ActionEvent e){
final String fileNaam = saveGame.getText();
changePanel();
Thread t = new Thread (new Runnable () {
#Override
public void run() {
model.saveGame(fileNaam);
controller.viewTeamPage();
}
});
t.start();
}
});
If you are changing the same panel and not intializing a new panel then the problem i think is that you need to call the panel.revalidate or panel.repaint i think. I made a demo for a Procedural generation project and i had to do this to make my panel change.
Call your save game method from a new thread but don't "join" or "try" to wait for this thread to finish from inside the method actionPerformed();
Make the call to controller.viewTeamPage() after the save game thread is done saving the game. One simple way of doing that would be passing the "controller" object to the constructor of your custom thread so you can make that call after saving the game.
The step 1 is very important in this case because all the calls you are making in the method actionPerformed() are being made in the UI thread, preventing the entire UI from refreshing until the method returns. Even calling repaint() alone, in changePanel(), wont be enough because it just "schedules" a refresh on you panel that will only happen after actionPerformed() returns. If you put the most time consuming call in a separate thread however, the actionPerformed() returns quickly allowing the UI to be refreshed while the game saving thread is doing its job.
I'm creating a blackJack game in java using swing in netbeans. Cards are dealt out sequentially to a player's multiple hands and the dealer. JLabels with Card images are to jLayerdPanes when each card is dealt. I want to pause briefly after each card is dealt. Currently you just see them all being dealt at once. This is the code for the first four cards being dealt out.
if(hand1.handIsInPlay()==true){
dealCard(jLPaneHand1,hand1);
pause();
}
if(hand2.handIsInPlay()==true){
dealCard(jLPaneHand2,hand2);
pause();
}
if(hand3.handIsInPlay()==true){
dealCard(jLPaneHand3,hand3);
pause();
}
dealCard(jLPaneDealerHand,dealerHand);
And here is the pause method I tried:
public void pause(){
try {
Thread.sleep(1000);
}
catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
Here is a second pause method I tried:
public void pause(){
try {
TimeUnit.MILLISECONDS.sleep(1000);
}
catch (InterruptedException e) {
//Handle exception
}
}
Neither worked as intended. What looks to have happened was that if three hands were in play, then the program summed up 3x1000ms and waited for that length and then dealt all the cards at once.
How can I make the program pause after every card is dealt?
You need to remember that Thread.sleep is called in current thread. In Java with Swing there will be usually at least 2 threads - Main thread and Swing thread - when you trigger an action appropriate listener is called and it's method executed - as long as this method is still run it cannot move on to other tasks like. e.g. redrawing the cards.
So basically you need to move this pause action to some other thread that will order action of drawing a card with e.g. SwingUtilities.invokeLater, then wait for some time, then again order redrawing and so on.
Basically you need to make sure that GUI thread is not blocked and can work all the time, and orders comes from some other thread(s) - and they CAN sleep without bothering the user.
In your case all happens in 1 thread so when you call on sleep GUI thread sleeps and cannot perform any actions like drawing what you wanted it to draw - it patiently waits till you code in Swing thread stops its execution.
EDIT:
Quick example - knowing exact requirements and code would make it act better...
in listener call e.g.:
new Thread(new CardDealing()).start(); // delegates task to the new thread
while the task would look like:
public CardDealing extends Runnable {
// inner data, methods and so on
public void run() {
if(hand1.handIsInPlay()==true){
dealCard(jLPaneHand1,hand1);
pause();
}
if(hand2.handIsInPlay()==true){
dealCard(jLPaneHand2,hand2);
pause();
}
if(hand3.handIsInPlay()==true){
dealCard(jLPaneHand3,hand3);
pause();
}
dealCard(jLPaneDealerHand,dealerHand);
}
public void dealCards(...) {
// this part will be run in a non-gui thread - make sure that
// all calculations and changes in model are made here
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// this will be run in a swing thread
// - make sure that only code dealing directly
// with GUI is called here
guiDealCards(jPane,hand);
}
});
}
}
Of course depending on your requirements CardDealing class might take parameters, contains references to some data and so on - but that's a matter of overall design. Communiction between GUI and working thread should work more or less like this. You might also try invokeAndWait in this particular case - just make sure that pause() is NOT called inside GUI thread (custom Swing components, Runnables that you delegate, paint methods etc).
EDIT 2:
You might find this link helpful - it would help you understanding how Swing works, and why you should divide your logic into at least 2 parts: GUI and non-GUI. Then whatever
isn't related to GUI place directly in dealCards method, while all GUI related stuff in guiDealCards - I don't know what exactly you are doing there so I cannot you that for you.
I was trying to sharp my OOD thinking. Here is my question.
Suppose you want to design a music mp3 player. And I got a class with a collection of playlists.
class Player {
Map<String, List<Song>> playlists; // <Name, PlayList>
public void play (Song song) {
// decode song
// play song
}
public void play (String playlistName) {
// play a playlist
for (Song song : playlists.get(playlistName)) {
play (song);
}
}
public void stop () {
// stop playing
}
public void pause () {
// pause, resume playing the last song when hit play again
}
}
Let's assume "Song" already contains all the metadata of a song. All the functionalities of methods have been described. I got stucked when I was trying to realize the "pause" method. What would you do to realize this?
Thanks!
Look into state pattern. Which you will store state of the player. When you hit pause and play you will know the state of the player.
I would have a Song member variable that tracks the currently playing song. If there is nothing playing, the value would be set to null. When you pause a Song, you simply call the Pause method on the Song class if the currently playing Song is not null. The Song class in turn can track where in the song (in terms of minutes) it is and figure out how a resume would work.
It seems to me like th play method blocks until the song has finished. It is a completely single-threaded design. When you design your application like that, it can't react to user input while a song is playing (unless you place an input handler into the playing loop).
In a real-world application, the play() method would start playing the song in a separate thread (many audio APIs will do that for you, so that you don't have to mingle with multi-threading) and then return, so that the application can stay responsible while a song is playing.
That way the pause and resume methods would then interact with the thread which plays the song.
I have a Blackjack game that I've made in Java and I want to signal the start of the game by clicking a button. All my action listeners work just fine and all that, but the problem lies in that I can't figure out how to start the game without it running completely within the actionPerformed method. Obviously, a function continuously running within the actionPerformed method will effectively disable the rest of my GUI. Here's a code snippet....
go.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// START GAME SOMEHOW but must run outside of action listener
}
});
Obviously, a function continuously running within the actionPerformed method will effectively disable the rest of my GUI.
This is a valid observation and shows that you have understand the fundamental rule when working with Swing.
Your game is most likely event driven (correct me if I'm wrong) so the action performed by the button should just set the program in a new state, waiting for further events. This is nothing that should be time consuming, and is typically done directly by the EDT.
Of course, if you want to do a fancy start-new-game animation, that needs to be performed in a separate thread, in which case you simply start the animation thread (I would recommend using a SwingWorker though) from within the actionPerformed method, and then return.
In code, I imagine it would look something like this:
go.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Remove the menu components
someJPanel.removeAll();
// Show the game table
someJPanel.add(new GamePanel());
someJPanel.revalidate();
someJPanel.repaint();
// done. Wait for further user actions.
}
});
You game should probably start in its own thread and manage that itself (hard to say), but to get you going you could start your game in a new "external" thread, something like this in your actionPerformed:
public void actionPerformed(ActionEvent e) {
Thread thread = new Thread("Game thread") {
public void run() {
startGame(); //or however you start your game
}
};
thread.start();
}
I believe that you want to extend javax.swing.SwingWorker.
The non-ui start-up functionality would run in doInBackground and the done method would be called when it finishes to update the ui.
There's even an example in the javadoc Class Description to update a progressbar with the status of what's happening in start-up.
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