JavaFX - Music On/Off Toggle Button (Not Working) - java

Below is the code sample, with other features left out. The code below encompasses the media player only. It's used on a menu screen for a project I'm working on. My issue is getting the musicButton (which is a toggle button- On/Off) to work properly. Using the following code, when I interact with the music toggle button, the playing music stops. When I click it again to resume playing, it does not resume. It stops once and stops altogether.
You can see I've tried simply using the boolean values of the toggle button in two if statements... If it's off and pressed, pause the music. If its on and pressed, resume the music. The problem is, as stated earlier, pausing the music works but it cannot be resumed. I've tried some combinations with loops, but nothing worked either.
I think if statements are too simple for this. I've scoured the JavaDocs and various online articles but I cannot find anything definitive. I've read a little about listeners, but they seem overly-complex for an on/off switch.
My question:
How do I get the musicButton to pause/play the music, whenver the user clicks it?
Any help is appreciated, thanks.
-Bagger
/* A simple game, the mechanics not yet implemented.
This is simply working on the title screen. */
import java.io.File;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.VBox;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;
public class MenuFX extends Application {
#Override
public void start (Stage primaryStage) {
// Make the window a set size...
primaryStage.setResizable(false);
// Create media player
// Rather than inputting the entire absolute URI, which would confine the program
// to the creator's device, we create a new file, grab the URI on whatever machine
// the program is running on and convert it to a string... portability.
Media menuMusic = new Media(new File("music/menu.mp3").toURI().toString());
MediaPlayer menuPlayer = new MediaPlayer(menuMusic);
// Want to see the absolute URI? Uncomment the next line
//System.out.println(new File("music/menu.mp3").toURI().toString());
// Adjust the cycles and volume then start playing menu music
// Lazy, but it will suffice
menuPlayer.setCycleCount(999999999);
menuPlayer.setVolume(0.1);
menuPlayer.setAutoPlay(true);
/*
Need assistance here
*/
// Create music toggle button
ToggleButton musicButton = new ToggleButton("Music On/Off");
if (musicButton.isSelected() == false) {
musicButton.setOnAction(e -> menuPlayer.pause());
}
if (musicButton.isSelected() == true) {
musicButton.setOnAction(e -> menuPlayer.play());
}
// Add all nodes to the vbox pane and center it all
// Must be in order from top to bottom
menuVBox.getChildren().add(musicButton);
menuVBox.setAlignment(Pos.CENTER);
// New scene, place pane in it
Scene scene = new Scene(menuVBox, 630, 730);
// Place scene in stage
primaryStage.setTitle("-tiles-");
primaryStage.setScene(scene);
primaryStage.show();
}
// Needed to run JavaFX w/o the use of the command line
public static void main(String[] args) {
launch(args);
}
}

I think you are over thinking it. You should have an EventListener on your ToggleButton to Pause and Play the music.
musicButton.setOnAction(event -> {
if (musicButton.isSelected()) {
menuPlayer.pause();
}else {
menuPlayer.play();
}
});
This should give you the desired effect.
The reason your code was not working is because the ToggleButton is not selected by default, so the only EventListener that gets associated with it is the menuPlayer.pause();. So when you click on it, it only ever pauses. I have moved your code into one EventListener, and used the approriate if-else.

Related

Javafx project sometimes freezes on alert

I'm using Eclipse to write javafx code for my university courses. When I run the code either in Eclipse, or after exporting it as a jar file, The program sometimes freezes.
For Example:
Normal box for data entry:
I have this window, where values for a rectangle can be entered.
If you press "submit" without adding both values, a NumberFormatException occurs, which I catch like so:
catch (NumberFormatException ex) {
System.err.println("NumberFormatException: " + ex.getMessage());
Alert alert = new Alert(AlertType.ERROR, "Bitte alle Felder ausfüllen", ButtonType.OK);
alert.showAndWait();
The alert window sometimes appears and works fine. Sometimes (rarely) it does 10 times in a row. Other times I get this:
Error State:
The program is now frozen and nothing is clickable. There is a thin, black line through one of the textfields. The program never recovers and has to be manually terminated through Eclipse or the task manager.
This sometimes happens on the first click on submit. Sometimes on the 5th, or later. But it always eventually happens. The program works fine in all other instances, so long as this alert is not triggered too often.
I have also had it happen in another project, where the trigger was a new window for data entry opening, much like the one I have here, but this time, only the alert freezes the program. The data entry window can be opened as many times as I like.
I'm using (course mandated) JavaFX11 and JavaSE-11 in Eclipse, on Manjaro.
Minimal reproducible example:
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.GridPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
GridPane root = new GridPane();
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
Button submit = new Button("submit");
Alert alert = new Alert(AlertType.ERROR, "Bitte alle Felder ausfüllen", ButtonType.OK);
submit.setOnAction(e -> alert.show());
root.add(submit,0,0);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Opens a simple GridPane with just the submit button. If I click it, it sometimes shows the alert, but it often freezes as described above. I can see no pattern in the freezes. It's not always the first time, or always the second. Sometimes it's immediate, sometimes it works 3 or 4 times, then freezes on the next submit.
There is no error message in the console.
Edit: Further experimentation has shown, that the window does not freeze. If you hit return, it hits the button in the alert, which closes it and lets me use the rest of the program.
So it seems the error is in the window not being displayed correctly and only showing up as a thin black line. Which is still a problem, because it also happens with windows where I can't just hit enter to close them, which leaves me stuck.
Turns out it was the issue described here: https://github.com/javafxports/openjdk-jfx/issues/222
Plasma-KDE has a bug that prevents these kinds of windows from opening. There's a workaround posted if it's a one time thing.
I fixed it by switching to xfce.

How to convert an image into a shape in Java?

I'm currently working on a stock market program that teaches students how to interact with the stock market. An issue that I'm working on right now revolves around the issue of efficiency and memory. I make all of my 2D icons (such as a settings icon, portfolio icons, etc) in adobe illustrator, I export those files as png files, and throw them into my program. I currently use JavaFX, and one of the features of JavaFX is what's called imageView, which is a method that handles the opening and viewing of images.
So let's say that the user would like to press on the Settings icon in the game, I would like to change the settings icon to a darker or lighter color when the user hovers over that icon in order to provide the user with a better user experience (UX) at the moment, I use two different images and remove one from the frame, and replace it with another, which is highly inefficient.
I know that JavaFX has a Shape class that inherits many methods such as Fill, or setFill, but these methods can only affect those of the Shape class.
My question is, "How can I convert an image that is imported into the project, into something that I can use methods such as setFill and Fill on"
If you're only aiming for basic changes such as darkening or lightning your icons, you can look at the Effects part of javaFx, you can read about it here, or else you can import your images as SVG as suggested in the comments
If you're planning to do it using Effects, you can achieve a darken-on-hover effect using the ColorAdjust Effect by setting the brightness value to something negative (the brightness in ColorAdjust ranges between -1 and +1 where 0 is the default) as in the following example
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage s) {
ImageView image = new ImageView(new Image("img.png"));
ColorAdjust darker = new ColorAdjust();
darker.setBrightness(-.3);
image.setOnMouseEntered(e-> {
image.setEffect(darker);
});
image.setOnMouseExited(e-> {
image.setEffect(null);
});
s.setScene(new Scene(new StackPane(image)));
s.show();
}
public static void main(String[] args) {
launch(args);
}
}
Changing the color of an image might involve adjusting the hue using ColorAdjust by setting the hue value
ColorAdjust hueShift = new ColorAdjust();
hueShift.setHue(-.3);
image.setOnMouseEntered(e-> {
image.setEffect(hueShift);
});
image.setOnMouseExited(e-> {
image.setEffect(null);
});
You can combine effects by setting effects as input to other effects, so for example, if you want to darken a Node and blur it at the same time you can set the blur effect as input to the darkening colorAdjust
GaussianBlur blur = new GaussianBlur();
blur.setRadius(10);
ColorAdjust darker = new ColorAdjust();
darker.setBrightness(-.3);
darker.setInput(blur);
image.setOnMouseEntered(e-> {
image.setEffect(darker);
});
image.setOnMouseExited(e-> {
image.setEffect(null);
});

Java JOptionPane Visual Artifact from Window Interaction

I am trying to create a simple menu for a small project using a JSpinner and JOptionPane. I created my desired output, but when I interact with the window or even hover over the buttons in the box, it creates visual artifacts over and over again (see images below):
JOptionPane before mouse hover
JOptionPane after mouse hover
I did some researching and thought it might be due to JOptionPane not being thread safe, but could not get anything to work.
Overall, my specific question is how do I prevent Java from repainting these visual artifacts over my JOptionPane window?
For reference, please see the method I am using to show this menu:
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SpinnerNumberModel;
import javax.swing.JSpinner;
public class Battleship
{
public static void main(String[] args)
{
SpinnerNumberModel battleshipRange = new SpinnerNumberModel(1, 1, 5, 1);
JSpinner rangeSpinner = new JSpinner(battleshipRange);
JOptionPane.showMessageDialog(null, rangeSpinner, "Battleship Number", JOptionPane.QUESTION_MESSAGE);
}
}
I am running this code on BlueJ and am using Windows 10 Pro.
Thank you in advance and apologies if this is a beginner question. I am still fairly new to programming.
EDIT: Updated code to give complete source of problem, but it disappeared. I will keep an eye on it to see where the source of the error first occurred.
After completing my project, I finally found why visual artifacts would pop up in all buttons/radio buttons/etc. in my game.
In the GridWorld source code, under the "GridPanel.java" script, the original programmers created a method called "setToolTipsEnabled(boolean flag)". Its function is to pop up a message box next to the cursor when it is hovering over the grid when the game stops running.
When extending GridWorld to create my project, the method reaches past the grid structure and attempts to create a tool tip underneath anything the cursor hovers over. Thus, creating visual artifacts on buttons/radio buttons/etc.
To fix this, I made sure this method was always set to false as I did not need tool tips for my game anyway. This method was implemented in the "GridPanel.java" and "GUIController.java" scripts (in the GridWorld code). I changed following methods to fix this problem:
GridPanel.java
/**
* Construct a new GridPanel object with no grid. The view will be
* empty.
*/
public GridPanel(DisplayMap map, ResourceBundle res)
{
displayMap = map;
resources = res;
// Phillip Sturtevant: Commented out to prevent visual artifacts
//setToolTipsEnabled(true);
}
GUIController.java
/**
* Stops any existing timer currently carrying out steps.
* Phillip Sturtevant Note: keep tool tips hidden to prevent visual artifacts
*/
public void stop()
{
display.setToolTipsEnabled(false); // hide tool tips while stopped
timer.stop();
running = false;
}
Alternatively, the method calls could be omitted entirely, but I commented them out in case I needed them in the future.
For reference, the method below sets the tool tips visibility in GridWorld (located in "GridPanel.java"):
/**
* Enables/disables showing of tooltip giving information about the
* occupant beneath the mouse.
* #param flag true/false to enable/disable tool tips
*/
public void setToolTipsEnabled(boolean flag)
{
if ("hide".equals(System.getProperty("gridworld.gui.tooltips")))
flag = false;
if (flag)
ToolTipManager.sharedInstance().registerComponent(this);
else
ToolTipManager.sharedInstance().unregisterComponent(this);
toolTipsEnabled = flag;
}

How to execute code during a drag and drop operation?

I have a node I want to implement drag and drop for (this object is the source not the target). I also want the object to move along with the mouse cursor. I managed to do both of these but not at the same time.
It appears that setOnDragDetected and setOnMouseDragged don't work well together. Consider a node with the following handlers:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Example extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Rectangle rect = new Rectangle(20, 20);
rect.setOnMousePressed(e -> System.out.println("Pressed"));
rect.setOnMouseDragged(e -> System.out.println("Dragged"));
rect.setOnDragDetected(e -> {
System.out.println("Detected");
ClipboardContent content = new ClipboardContent();
content.putString("something");
Dragboard db = rect.startDragAndDrop(TransferMode.ANY);
db.setContent(content);
});
Group subGroup = new Group(rect);
Scene scene = new Scene(subGroup, 100, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Example.launch(args);
}
}
Now press the mouse on the node and move the mouse. This is the output:
Pressed
Dragged
Dragged
Dragged
Dragged
Dragged
Dragged
Detected
Once the drag is detected the MouseDragged handler stops.
How do I achieve what I described? One thing i noticed was maybe that I can use a onDragOver for the parent but i want the behavior to be in the node because that's where it really should be.
You are mixing two things up here. In short, when you call the startDragAndDrop method the system switches to drag and drop mode and Java stops delivering MouseEvent to rect.
The MouseEvent documentation has a section "Dragging gestures" which explains the three types of dragging gestures. Here is just a short summary:
simple press-drag-release - when a drag is detected Java continues to deliver MouseEvents to the node where the drag was detected.
full press-drag-release - you can call startFullDrag inside the handler you set with setOnDragDetected. Then Java also starts to deliver MouseDragEvents to other nodes (potential gesture targets).
platform-supported drag-and-drop - if you call startDragAndDrop inside the OnDragDetected handler, Java will stop to deliver MouseEvents and start to deliver DragEvents instead. This is used for drag and drop interaction with other applications.
It is not clear to me what you want to achieve, but as long as you do not want to drag something outside of your application, try using startFullDrag instead.
Also, it might be helpful to have a further look at the DragEvent and MouseDragEvent documentation.

JavaFX - Drawing component when the screen is turned off

My application works on tablet (with Windows 7) which sometimes turns off its display in order to save battery. After touching the screen, it turns on again.
Application should remain operational even after turning off the screen, what is, in general, accomplished (i.e. sounds are played). The only problem is the fact that when screen is turned off and application tries to add new component to the scene (technically - add children to the displayed GridPane), the newly added component is not visible when I touch the screen (and it turns on).
It is just like there was a cache which stores and restores displayed content when screen is turning on/off. After turning screen on - when I click windows button (keyboard) or or change focus to another application - newly added component appears.
I have tried setting -Dprism.dirtyopts=false but it did not change anything.
My Java is 7u67.
Is it known bug? Is there any programming workaround? I have tought about using WinAPI to force redraw/refresh of the application window but it seems too big for this case.
I have just figured out "simple" workaround:
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.ptr.IntByReference;
/** WINAPI CONSTANTS **/
private static final int RDW_UPDATENOW = 0x0100;
/**
* Redraws all windows associated with currently running process.
*/
public static void redrawApplicationWindows() {
final int processId = Kernel32.INSTANCE.GetCurrentProcessId();
User32.INSTANCE.EnumWindows(
new WinUser.WNDENUMPROC() {
#Override
public boolean callback(WinDef.HWND hwnd, Pointer pointer) {
IntByReference someProcessId = new IntByReference();
User32.INSTANCE.GetWindowThreadProcessId(hwnd, someProcessId);
if (someProcessId.getValue() == processId) {
User32.INSTANCE.RedrawWindow(hwnd, null, null, new User32.DWORD(RDW_UPDATENOW));
}
return true;
}
}, Pointer.NULL);
}
It requires you to have jna and jna-platform in dependencies.
This method should be called after drawing when the screen is turned off (it is another problem how to detect it). As my application rarely draws automatically (without user input), I have placed its call after every drawing happening automatically.
It works in Win7.

Categories

Resources