I'm not 100% sure how to best describe this issue but I will try my best.
I am developing a music application and have different windows within one and have loaded panes into a little in app window shown below:
The bit that changes from a green button to the playlist screen is the window that has FXML files loaded into it.
I recently created a part of the application that plays songs from a button within this little window of the application but the media player is in the main application controller and needs to be here as it updates the values of the two sliders at the bottom.
I have seen something about referencing the exact instance of the controller I am using for the main application but I am unsure how to do this. I tried to create a static version of the mainAppController but this was a separate instance and therefore did not reference properly.
The only method I need to reference is the addPlayerListener one. I tried to make this static but because it dealt with the slider and so on from the FXML file that could not be made static, this did not work.
A simplified version of the controller and FXML file I need to reference from outside of the class looks like this:
public class MainAppController implements Initializable {
#FXML AnchorPane anchor;
#FXML AnchorPane window;
public static AnchorPane pane;
private boolean isPlay = false;
private boolean makeChanges = false;
public static MediaPlayer player;
PlayerUtils playerUtil = new PlayerUtils();
#FXML Slider volume, progressBar;
#FXML Label playTime, fullDuration;
String songFile;
Media songMedia;
Duration duration;
boolean updating = false;
//declaring the media player listener so that it can be removed and added
public InvalidationListener listener = new InvalidationListener()
{
public void invalidated(Observable ov) {
//if changes to the slider are being made then values will not be updated
//as it causes interference when trying to move the slider
if(makeChanges == false){
updateValues();
}
}
};
//this just plays or pauses depending
#FXML public void play_pause(){
play_pauseFunction();
}
//updates values of current time display (playTime) and updates the slider value
//called from the listener so constantly updating
protected void updateValues() {
//updates values of playTime and slider values
}
//THIS IS THE ONE I NEED TO ACCESS
//makes sure the player has no listeners before adding a new one
public void addPlayerListener(){
player.currentTimeProperty().removeListener(listener);
player.currentTimeProperty().addListener(listener);
}
#Override
public void initialize(URL url, ResourceBundle rb) {
}
}
Sorry if this question is poorly asked, but I couldn't think of another way of posing it.
Thank you for help in advance
Ammendment: I would actually like to access this method:
public void play_pauseFunction(){
if(isPlay == false){
//temp while playlists not working
if(player == null){
songFile = "E:\\Project\\Whupsh\\Songs\\Whenever, wherever - Shakira.mp3";
player = new MediaPlayer(PlayerUtils.getSongMedia(songFile));
progressBar.setDisable(false);
volume.setDisable(false);
duration = player.getMedia().getDuration();
}
player.play();
//adds listener to link with slider
addPlayerListener();
play_pause.getStyleClass().remove("play");
play_pause.getStyleClass().add("pause");
isPlay = true;
}
else{
player.pause();
play_pause.getStyleClass().remove("pause");
play_pause.getStyleClass().add("play");
isPlay = false;
}
}
This method is in the same class as all other methods
One way that worked was by having a static class holding all the controllers that would be needed to be accessed from another controller.
This is by not a particularly good way of doing things, and a better way of accessing controllers from outside another would be to pass it around as a parameter as kleopatra has said.
Passing Parameters JavaFX FXML
Related
I have a class (TaskInOurProgram) which contains ImageView in constructer.
Here is a little code snipped - declaration and then constructer:
private ImageView closeTask;
private ImageView pauseTask;
private final Image pauseImage = new Image("file:images/pause.png");
private final Image closeImage = new Image("file:images/close.png");
public TaskInOurProgram(String name, String configName, String extensionOfTheFile) {
this.nameOfTheFile = name;
this.configName = configName;
this.extensionOfTheFile = extensionOfTheFile;
this.closeTask = new ImageView();
closeTask.setImage(closeImage);
this.pauseTask = new ImageView();
pauseTask.setImage(pauseImage);
}
Then I have a observableArrayList which contains many of these TaskInOurProgram objects.
private ObservableList<TaskInOurProgram> tasksInTheTableView = FXCollections.observableArrayList();
I have a tableView which displays this list of objects. So then it looks like this.
Object are not there from the beginning, they need to be added by clicking at button ADD TASK and because it is observableList, it will show this "tasks" immediately after adding.
WHAT I NEED: What I need is to create method inside of the controller which will do something after click on "pause" ImageView. I really do not know how to do it, I was thinking about listeners somehow. And before that I need to put click event on imageView.
Can you guys, please, help me with this? Thanks for any idea.
Try using a Button with a Graphic instead of a plain ImageView. That way you can use the Button.setOnAction() method.
Where you are currently defining your ImageView, change it to the following:
this.pauseTask = new Button("", new ImageView(pauseImage));
this.pauseTask.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
pause();
}
});
You can define a method somewhere else in your program, something like this:
private void pause() {
// your code here
}
Use setOnMouseClicked() method for imageView to handle click events.
pauseTask.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event event)
{
pauseTask();
}
});
Then you can handle it in other method
void pauseTask()
{
}
I am having a problem with libGDX where when I resume the application after exiting with the back button, I get only a white screen.
The actual app runs, accepts touch input, and plays sounds, but the screen is just white.
I have read that keeping static references to Textures may cause this problem but I am not doing that.
Below is a simplified version of how my asset code works.
public class GdxGame extends Game {
private Assets assets;
#Override
public void onCreate() {
assets = new Asstes();
assets.startLoading();
/*
*Within SplashScreen, GdxGame.getAssets() is accessed, and
*manager.update() is called
*/
setScreen(new SplashScreen());
}
#Override
public void dispose() {
assets.dispose();
assets = null;
}
//Perhaps a problem??
public static Assets getAssets() {
return ((GdxGame) Gdx.app.getApplicationListener()).assets;
}
}
public class Assets implements Disposable{
private AssetManager manager;
public Assets() {
manager = new AssetManager();
}
public void startLoading() {
manager.load(....);
}
#Override
public void dispose() {
manager.dispose();
}
}
Upon returning to the application after the back button is pressed, the AssetManager is recreated, the SplashScreen is reopened (as white), and the AssetManager updates until all assets are reloaded (takes about 2 seconds).
So when the application is reopened, a new AssetManager is loading all of the necessary textures, but for some reason everything is still white.
Could it have something to do with how I access the AssetManager from my UI and Game classes?
//In GdxGame
public Assets getAssets() {
return ((GdxGame) Gdx.app.getApplicationListener()).assets;
}
That is the only place where I could see something going wrong, but even still, I don't understand what could be wrong with that.
Any help would be much appreciated.
Edit:
Here is the SplashScreen class. This makes the issue even more confusing. In the SplashScreen class I load, draw, and dispose a new Texture for the logo. Nothing to do with the AssetManager. Returning after the back button is pressed, this new Texture does not appear either.
public class SplashScreen extends Screen {
private Texture logo;
private Assets assets;
#Override
public void show() {
assets = GdxGame.getAssets();
logo = new Texture(Gdx.files.internal("data/logo.png"));
}
#Override
public void render(float delta) {
super.render(float delta);
if(assets.load()) {
//Switch screens
}
//getBatch() is the same form as getAssets() ((GdxGame) Gdx.app.getApplicationListener()).batch)
GdxGame.getBatch().draw(logo, 100, 100, 250, 250);
}
#Override
public void hide() {
super.hide();
logo.dispose();
}
}
It turns out the issue was to do with how I was managing screens. I had a convenience method in my GdxGame class that allowed me to switch screens based on a ScreenType enum.
Within the enum, I would create a new screen with reflection, given the screen's class in the enum constructor.
The problem is that I stored the screens in the enums, and only created them once. This means that I was keeping and using screens from the old context when I returned after pressing back.
To solve this, I simply had to change it so that a new screen is created every time the enum is accessed, instead of storing one at the start.
I want to have several JavaFX Buttons that update one Label in my Application with text. For testing purposes it's just Button Text.
What I did at first worked fine and looked like this:
String Text = "...";
public void kons() {
System.out.println("Works...");
System.out.println(Text);
Tekst.setText(Text);
Button G4 = new Button("Spadantes");
G4.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Text = G4.getText();
kons();
}
});
Then I decided to stylize my buttons with CSS and because I wanted to have several groups of buttons stylized in different way I subclassed JavaFX Button class in this way:
public class Buttons extends Button {
public Buttons(String text) {
super(text);
getStylesheets().clear();
getStylesheets().add("./Buttons.css");
Which still worked. But now I want my event handler to be moved to Button subclass (to avoid copy-pasting exactly same code into each and every button of mine). What I did looks like this:
public class Buttons extends Button {
public Buttons(String text) {
super(text);
getStylesheets().clear();
getStylesheets().add("./Buttons.css");
setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Main.Text = getText();
Main.kons();
}
});
}
}
Main is my extend Application class
Tekst is my label.
And sadly it throws me exception about calling non-stathic method and variable from static context. From what I understand instances are static and definitions are non-static. I tried to change everything "in the way" to static but it gives me red wall of errors after clicking button (nothing in compilation process). I also tried to call instance of my Application somehow but I have no idea how (from what I understand extend Application class intantiates itself on it's own while starting program so there's no "name" by which I can call it's Label.
What I'm looking for is "quick and dirty solution" to be able to use subclassed buttons (or other sliders, text-fields, etc.) that can call a method that updates something "on screen".
[EDIT] I'm using newest Java there is of course. In case it matters.
Instead of subclassing, why not just write a utility method that creates the buttons for you? I would also not recommend making the text variable an instance variable: just reference the Label directly.
public class SomeClass {
private Label tekst ;
// ...
private Button createButton(String buttonText) {
Button button = new Button(buttonText);
button.getStylesheets().add("Buttons.css") ;
button.setOnAction(e -> tekst.setText(buttonText));
return button ;
}
}
Then, from within the same class, when you need one of those buttons you just do
Button button = createButton("Text");
If you really want to subclass (which just seems unnecessary to me), you need to pass a reference to the label to the subclass:
public class LabelUpdatingButton extends Button {
public LabelUpdatingButton(String text, Label labelToUpdate) {
super(text);
getStylesheets().add("Buttons.css");
setOnAction(e -> labelToUpdate.setText(getText()) );
}
}
Then from your class that assembles the UI you can do
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Label tekst = new Label();
Button someButton = new LabelUpdatingButton("Button text", tekst);
// etc...
}
}
But again, creating a subclass that does nothing other than define a constructor that calls public API methods is redundant, imo.
Also, it's a bit unusual to create an entire stylesheet just for your buttons. Typically you would set a style class on the Button:
button.getStyleClass().add("my-button-class");
and then in the stylesheet you add to the Scene do
.my-button-class {
/* styles for this type of button */
}
I have the space bar set up to advance the "scene" of a game I am creating. The function is as follows (the Key Handler and everything are all properly implemented):
public void KeySPACE() {
beginGame(player, "Map0");
}
beginGame, for reference, looks like this:
public void beginGame (Player player, String map) {
player.createProfile();
this.map = mapFile.getMap(map);
this.map = findSpawnPoint();
this.arena = this.map.arena;
this.enemyPath = new EnemyPath(this.map);
this.scene = 1; //Game playing state
this.wave.waveNumber = 0;
}
My game starts at a menu screen, which you can advance to the game screen by pressing space (i.e. set scene = 1). However, I only want the space bar to do this once. The problem is, the user can inadvertently reset an active game by pressing the space bar.
Any ideas?
Can you not just add an if statement?
public void KeySPACE() {
if(this.scene == 0){
beginGame(player, "Map0");
}
}
And just make sure that before the game starts, you are on scene 0.
Alternatively you can create a boolean value to do the same, for example:
public void KeySPACE() {
if(!gameRunning) {
beginGame(player, "Map0");
}
}
You will need to define a boolean gameRunning which starts as false. You can set it to true in the beginGame method.
I wrote my program in two parts, I wrote the actual functionality first, and then the GUI to display it all.
I need to wait/pause for a user to click a "Done" button from another class(DisplayImages) before continuing executing code.
My DisplayImages class takes a list of MyImage. The images are then displayed in a jpanel and the user selects a couple of images
and then clicks a 'Done' button. How can I wait for a response or something like that?
public class One{
ArrayList<MyImage> images = new ArrayList<MyImage>();
public One(){
DisplayImages displayOne = new DisplayImages(images);
displayOne.run();
//I need to pause/wait here until the user has pressed the done button
//in the DisplayImages class
images.clear();
images = displayOne.getSelectedImages();
//do stuff here with images arrylist
}
}
DisplayImages class
public class DisplayImages extends JFrame{
private ArrayList<MyImage> images = new ArrayList<MyImage>();
private ArrayList<MyImage> selectedImages = new ArrayList<MyImage>();
public DisplayImages(ArrayList<MyImage> images){
this.images = images;
}
public void run(){
//code creates a jpanel and displays images along with a done button
//user presses done button
done.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
setVisible(false);
selectedImages = getSelectedImages();
//here I would send a signal to continue running in class One
}
});
}
private ArrayList<MyImage> getSelectedImages(){
//code returns list of all the images the user selected on JPanel
return results;
}
}
If for some reason you need to open and process the dialog in the same method then using the dialog approach suggested with JOptionPane seems fine. However this seems bad design (opening a frame and waiting for input in a constructor?). I would prefer an approach similar to the one below (please read my inline comments):
public class One {
ArrayList<MyImage> images = new ArrayList<MyImage>();
public One() {
// perform only initialization here
}
// call this method to create the dialog that allows the user to select images
public void showDialog() {
DisplayImages displayOne = new DisplayImages(images);
// pass a reference to this object so DisplayImages can call it back
displayOne.run(this);
}
// this will be called by the action listener of DisplayImages when Done is clicked
public void processSelectedImages(List<MyImage> selectedImages) {
images.clear();
images = selectedImages;
// do stuff here with images arrylist
}
}
public class DisplayImages {
...
public void run(final One callback){ // Note that now a reference to the caller is passed
// creates jpanel and displays images along with a done button
// user presses done button
done.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
setVisible(false);
selectedImages = getSelectedImages();
// here is how we can send a signal to notify the caller
callback.processSelectedImages(selectedImages);
}
});
}
...
}
As a side note please do not name your methods run() if you are not implementing the Runnable interface and/or using threads. This is very confusing
That is quite easy, some people here are thinking to complicated. You will need no multithreading, you just need a modal dialog. JOptionPane provides easy access to them.
I modified your code:
public class One{
ArrayList<MyImage> images = new ArrayList<MyImage>();
public One(){
DisplayImages displayOne = new DisplayImages(images);
int n = JOptionPane.showConfirmDialog(null, displayOne);
if (n == JOptionPane.OK_OPTION){
//I need to pause/wait here until the user has pressed the done button
//in the DisplayImages class
images = displayOne.getSelectedImages();
//do stuff here with images arrylist
}
}
}
MyImage class
public class DisplayImages extends JPanel{
private ArrayList<MyImage> images = new ArrayList<MyImage>();
public DisplayImages(ArrayList<MyImage> images){
this.images = images;
//code creates a jpanel and displays images along with a done button
}
public ArrayList<MyImage> getSelectedImages(){
//code returns list of all the images the user selected on JPanel
return results;
}
}
You will have to use threading.
http://docs.oracle.com/javase/tutorial/essential/concurrency/
Then you can set up each instance on a separate thread, and either use the built in Object.notify and Object.wait
Or have yourself a global flag variable, static, available to both classes, which you change to notify the other class that the done button was clicked.