Access cell of a column using a given index? - java

I have the index (as integer) of a cell in a TableColumn of a TableView. Using the index of the cell, how can I access the cell object at this index? I am missing something like
tableColumn.getCells()
which would then allow me to access the cell with the given index:
tableColumn.getCells().get(index)
Any hints on this? Thanks!
Update:
As per request, a use case for this:
Imagine a music playlist. For some reasons I am using a TableView for that and the TableView is as big as possible, meaning even if there are just 5 music tracks in the playlist and there is space for 10 rows, then 10 rows (5 filled, 5 empty) are shown.
I then implemented drag & drop for re-arranging items. Now imagine a user wants to place the second entry to the end of the playlist. He drags the second entry, but not directly over the last used table row, but on e.g. row 8. In that case I want to give a visual feedback that the music track is placed under the last used row (row 5, but the mouse is over row/cell 8!), so I need to modify row/cell 5, while the mouse is over row 8. Knowing which row is the last filled one is quite easy - it's tableview.getItems().size()-1, but getting a reference to row/cell 5 is a tricky thing / my problem here.

This API doesn't exist for a reason.
For better performance FX TableView manipulates cells by itself. Cells are created and destroyed depending on currently visible part of the TableView and other parameters. Even more: cell objects can be reused to represent data from other cells under some circumstances.
So return value of tableColumn.getCells().get(index) is not determinate, thus there is no API to receive it.
All cell related logic should be implemented in the CellFactory callback.
Can you describe what kind of logic you want to implement by accessing cells?

I would set a fixed cell size and just take a guess using the mouse coordinates. I just added it to an existing example and I'm sure it's not quite right but it shows the idea.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.converter.NumberStringConverter;
public class ChangePie extends Application {
#Override
public void start(Stage primaryStage) {
ObservableList<PieChart.Data> pieChartData
= FXCollections.observableArrayList(
new PieChart.Data("Grapefruit", 13),
new PieChart.Data("Oranges", 25),
new PieChart.Data("Plums", 10),
new PieChart.Data("Pears", 22),
new PieChart.Data("Apples", 30));
final PieChart chart = new PieChart(pieChartData);
chart.setTitle("Imported Fruits");
final TableView<PieChart.Data> tv = new TableView(pieChartData);
tv.setEditable(true);
tv.setFixedCellSize(40);
tv.addEventHandler(MouseEvent.ANY, (MouseEvent evt) -> {
double y = 0;
int idx1 = 0, idx2 = 0;
if (evt.getEventType() == MouseEvent.MOUSE_PRESSED) {
y = evt.getY();
idx1 = tv.getSelectionModel().getSelectedIndex();
} else if (evt.getEventType() == MouseEvent.MOUSE_RELEASED) {
idx2 = (int) Math.round((evt.getY() - y) / 40);
System.out.println("insert at " + idx2);
if (idx2 > 0) {
chart.setData(null);
PieChart.Data removed = pieChartData.remove(idx1);
pieChartData.add(Math.min(idx2, pieChartData.size() - 1), removed);
}
}
});
TableColumn tc1 = new TableColumn("Name");
tc1.setPrefWidth(100);
tc1.setCellValueFactory(new PropertyValueFactory("name"));
tc1.setCellFactory(TextFieldTableCell.forTableColumn());
TableColumn tc2 = new TableColumn("Data");
tc2.setPrefWidth(100);
tc2.setCellValueFactory(new PropertyValueFactory("pieValue"));
tc2.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
tv.getColumns().addAll(tc1, tc2);
Scene scene = new Scene(new VBox(chart, tv));
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Related

JavaFX adding nodes to anchorpane in for loop, but waiting a few seconds each iteration before adding the next node [duplicate]

This question already has answers here:
JavaFX periodic background task
(6 answers)
Closed 1 year ago.
I am making a game, what should happen is, inside a for loop while iterating over an arraylist. Inside each loop I want to add a node to my scene, then I want to wait some time and add another node to the scene. the time between each iteration is also defined in the item of the arraylist, and can be different each iteration.
What i've tried:
//Circle is my own class, other than these attributes along with getters it has nothing in it.
//The last number is the time to wait.
//Also for testing i used a static arraylist, normally the items come from a json file.
ArrayList<Circle> lvlNodes = new ArrayList<>();
lvlNodes.add(new Circle(50,50, Color.RED,250,100));
lvlNodes.add(new Circle(200,100, Color.BLUE,500,250));
lvlNodes.add(new Circle(900,500, Color.YELLOW,750,500));
lvlNodes.add(new Circle(400,50, Color.GREEN,1000,500));
//Iterating over the arraylist and adding the nodes.
for (int i=0; i<lvlNodes.size(); i++) {
Circle currentNode = lvlNodes.get(i);
//Add the node the the anchor pane.
view.addLvlNode(currentNode, diameter); //diameter is a final value, all nodes in the game are the same and thus only added once at the top of the json file
//Wait and move on to the next one.
try {
Thread.sleep(currentNode.getTimeToNextNode())
} catch (InterruptedException e) {
System.out.println(e);
}
}
//Adds the next node to the game.
public void addLvlNode(Circle circle, double diameter) {
//Create node.
javafx.scene.shape.Circle lvlNode = new javafx.scene.shape.Circle(circle.getPosX(),
circle.getPosY(), diameter/2);
//Anchor node the the anchorpane.
AnchorPane.setTopAnchor(lvlNode, (double)circle.getPosY());
AnchorPane.setLeftAnchor(lvlNode, (double)circle.getPosX());
anchrLevel.getChildren().add(lvlNode);
lvlNode.toBack();
}//addLvlNode.
The Thread.sleep() works, each iteration in the for loop is with the time in between. but the nodes don't get added until the for loop is done iterating.
Is there any way of adding the nodes inside the for loop with the time amount in between?
The problem that you're having is that all of your code is running on the FXAT, and while the FXAT is busy running that code, it can't do things like update the screen to actually add the circles to it. So when your code is finished, including all of the Thread.sleep() calls, it then does all of the screen painting - all at once.
Rather than build your custom Circle class, I've just built some arrays to hold the Circles and wait times. Then I put the addLvlNode & sleep into a background job. The background job uses Platform.runLater() to add the nodes onto the Pane on the FXAT:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import java.util.ArrayList;
public class CircleMaker extends Application {
private Pane mainPain;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
ArrayList<Circle> circles = new ArrayList<>();
ArrayList<Integer> waitTimes = new ArrayList<>();
circles.add(new Circle(50, 50, 125, Color.RED));
waitTimes.add(1000);
circles.add(new Circle(200, 200, 250, Color.BLUE));
waitTimes.add(2500);
circles.add(new Circle(900, 500, 375, Color.YELLOW));
waitTimes.add(5000);
circles.add(new Circle(400, 50, 500, Color.GREEN));
waitTimes.add(5000);
Thread waitingThread = new Thread(() -> {
for (int idx = 0; idx < 4; idx++) {
Circle thisCircle = circles.get(idx);
Platform.runLater(() -> addLvlNode(thisCircle));
try {
Thread.sleep(waitTimes.get(idx));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
mainPain = new Pane();
primaryStage.setScene(new Scene(mainPain, 1000, 800));
primaryStage.show();
waitingThread.start();
}
private void addLvlNode(Node circle) {
mainPain.getChildren().add(circle);
circle.toBack();
}
}
I also stretched out the wait times by a factor of 10 so that it was easier to see that the circles are each painted in turn with a pause between them.
You could probably use Timeline to do this, but since you said that the Circle data was eventually going to be JSON from an external source, that implies to me that you've probably got some kind of non-JavaFX mechanics happening so it's probably best to just use a background thread to do whatever you need. You can see from this example what the basic framework would be.

JavaFX Text - How to define the size in pixels, align it correctly and select a suitable font with the same letter width?

import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextBoundsType;
import javafx.stage.Stage;
public class TextSize extends javafx.application.Application {
#Override
public void start(Stage stage) {
var rectangle = new Rectangle(600, 100, Color.YELLOWGREEN);
var text = new Text(0, 50, "EXAMPLING");
text.setFont(Font.font("Monospaced", 100));
text.setBoundsType(TextBoundsType.VISUAL); // Removes text padding, but not the one on the left.
text.setTextAlignment(TextAlignment.CENTER); // Doesn't align text horizontally.
text.setTextOrigin(VPos.CENTER); // Aligns text vertically.
stage.setScene(new Scene(new Pane(rectangle, text)));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I need to edit the text.
I primarily want to set the correct Text size. Specifically, the distance between the Text and each edge of the Rectangle should be at least 5 pixels. I could achieve this by defining the size of the Text in pixels. In this case, the text height should be a maximum of 90px and the text length should be a maximum of 590px. However, the font size is not defined in pixels. Even if I enter it using fxml, it has no effect. How do I define it, please?
I want to place the text in the center of the Rectangle. Therefore, I need the position of the Text to define its center. var text = new Text(300, 50, "EXAMPLING"); But, as I said in the comments in the code, I couldn't align the text horizontally and remove the padding on the left (As can be seen from the picture). You will probably say that I should use StackPane. But, this is just an example, so I want to know if it can be achieved without it, please?
This Font has all the letters the same width, but I don't like it very much. Do you know of a better Font (Arial style), which also has the same width letters?
Please help.
Thank you
UPDATE:
I solved the size(1.) and font(3.) of the text using this code.
var rectangle = new Rectangle(600, 100, Color.YELLOWGREEN);
var text = new Text(0, rectangle.getHeight() * 0.5, "EXAMPLING");
text.setFont(Font.loadFont(getClass().getResourceAsStream("GT Pressura Mono Regular Regular.ttf"), rectangle.getHeight() * 1.3));
text.setBoundsType(TextBoundsType.VISUAL);
var max_width = rectangle.getWidth() - 7;
for (var i = text.getFont().getSize() - 0.5; text.getLayoutBounds().getWidth() >= max_width; i -= 0.5) {
text.setFont(new Font(text.getFont().getName(), i));
}
text.setTextAlignment(TextAlignment.CENTER);
text.setTextOrigin(VPos.CENTER);
stage.setScene(new Scene(new Pane(rectangle, text)));
stage.show();
However, I still don't know how to solve the positioning(2.).

Is there a way to have two different font sizes within the same text line in JavaFX?

I'm making a windows-run-mobile-concept stock market game using Java, and it's JavaFX libraries, and I'd like to have the form of currency on the lower right of the player's current balance, (in my case USD). The catch with this is, is that whenever the player gains a multiple of a thousand dollars in-game, the player's balance gets larger, which means that the string that holds the player's balance is also larger. This causes the fixed position of the string "USD" in the application to be overlapped by the player's current balance.
I've tried simply forcing the program to move the "USD" symbol whenever the number increases by a multiple of one thousand. This, however, seems highly inefficient and I'm willing to bet that there is a simpler way to do this.
double balance = 12313.00;
DecimalFormat decimalFormat = new DecimalFormat("#,###");
String numberAsString = decimalFormat.format(balance);
Text balanceText = new Text("$" + (numberAsString));
balanceText.setFont(Font.font("Modernist", 72));
balanceText.setFill(Color.web("77e6b3"));
balanceText.setLayoutX(25);
balanceText.setLayoutY(250);
Text currencyText = new Text("USD");
currencyText.setFont(Font.font("Modernist", 36));
currencyText.setFill(Color.web("77e6b3"));
currencyText.setLayoutX(275);
currencyText.setLayoutY(250);
Instead of trying to manually set the X/Y coordinates of your Text nodes, you should take advantage of the many built-in JavaFX Layout Panes.
You can easily built very complex UI layouts by mixing different layout panes in JavaFX. Each Pane can be styled and configured independently from each other, with different layout "rules" applied to each.
I recommend reading through the link above to get a better idea of what is available, but for your question, I've provided a complete example below.
Basically, we are using an HBox to hold both the player's balance and the currency designation in a horizontal orientation. Since we have a completely separate Label for our player's currency, when we increase the value or font size of that Label, the rest of the interface is not effected (meaning the currency type can remain the same size).
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class GrowingTextSample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// Here we'll create a simple HBox to hold our player balance and currency type. By using a HBox, we can add
// multiple Label (or Text) objects and style/size them separately
HBox hbPlayerBalance = new HBox(5);
hbPlayerBalance.setAlignment(Pos.CENTER);
// Let's create a Label to hold the player's current balance. For this example, I'm going to use a text Label
// to hold our Integer value. In a real application, you would want to use a special Binding to keep the TextProperty
// of the Label in sync with an Integer or Double value.
Label lblPlayerBalance = new Label("0");
// Here is our label for the currency type
Label lblCurrency = new Label("USD");
// Add our labels to the HBox
hbPlayerBalance.getChildren().addAll(lblPlayerBalance, lblCurrency);
// Create a Button that simulates an increase to the player's balance. Again, this is just a crude demonstration
// of how the layout panes (HBox, in this case) work to keep your layout clean and responsive.
Button btnIncreaseBalance = new Button("Get Rich");
// Add an action to our Button
btnIncreaseBalance.setOnAction(event -> {
// Change balance value
lblPlayerBalance.setText("12,322,242");
// Change balance font size. Notice changing the font size of the balance does not affect the currency Label
lblPlayerBalance.setStyle("-fx-font-size: 200%;");
});
// Add the HBox and Button to our root layout
root.getChildren().addAll(hbPlayerBalance, btnIncreaseBalance);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setHeight(200);
primaryStage.setWidth(300);
primaryStage.setTitle("GrowingTextSample Sample");
primaryStage.show();
}
}
Result:
Note: There are a lot of additional formatting and styling options available to make sure your layout is exactly how you'd expect. You'll need to read through the JavaDocs or find additional tutorials to learn more. This answer is only meant to demonstrate one of many possible solutions.
You can use a TextFlow to render rich text in multiple fonts, sizes, styles and colors:
Sample Code
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.*;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
import java.text.DecimalFormat;
public class MoMoney extends Application {
#Override
public void start(Stage stage) throws Exception {
double balance = 12313.00;
DecimalFormat decimalFormat = new DecimalFormat("#,###");
String numberAsString = decimalFormat.format(balance);
Text balanceText = new Text("$" + (numberAsString));
balanceText.setFont(Font.font("Modernist", 72));
balanceText.setFill(Color.web("77e6b3"));
Text currencyText = new Text("USD");
currencyText.setTextOrigin(VPos.TOP);
currencyText.setFont(Font.font("Modernist", 36));
currencyText.setFill(Color.web("77e6b3"));
TextFlow flow = new TextFlow(balanceText, currencyText);
flow.setMinSize(TextFlow.USE_PREF_SIZE, TextFlow.USE_PREF_SIZE);
VBox layout = new VBox(flow);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.getScene().setFill(Color.rgb(35, 39, 50));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
JavaDoc Description
Description of TextFlow from the JavaDoc linked above:
TextFlow is special layout designed to lay out rich text. It can be used to layout several Text nodes in a single text flow. The TextFlow uses the text and the font of each Text node inside of it plus it own width and text alignment to determine the location for each child.
Implementation Comments
By default, TextFlow will wrap text to new lines if there is not enough space to render all text, so I set the minimum size of the TextFlow to its preferred size to prevent that.
TextFlow can align the text with standard width based alignment settings such as left align, right align, justify etc. However, TextFlow doesn't have any way to vertically align text, for example to generate a superscript value. There are also other limitations such as making the text selectable for copy and paste or editing text. So look at it, and try it to see if it fits your purpose, if not then consider some of the other alternative mechanisms mentioned below.
Alternative Approaches
Other valid ways to do this are:
Using layout containers with constraints to control layout (as in Zephyr's answer and this related question: Javafx Text multi-word colorization).
Embedding a WebView to render CSS formatted html.
Using a third party lib, like RichTextFX.

JavaFX GUI doesn't run when I put a while loop into the code, but does when gotten rid of

so I'm currently making a simple turn based game in javaFX, but have ran into a problem. I'm trying to put in a while loop into the code (found in the second to last code block), but get nothing to run when I execute the program. I don't get any errors and no window pops up.
If I get rid of my while loop, then the GUI runs fine.
I've tried to move the while loop around, but this hasn't changed anything. I also tried going to a previous build to see if the while loop would work there, but the same issue happened to it too.
package application;
import javafx.scene.image.*;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
public class Game extends Application {
Button start, battle, attack, defend;
Scene intro, beginBattle, standby;
MediaPlayer mediaPlayer;
int monsterHealth = 100;
int playerHealth = 100;
public void start(Stage primaryStage) {
primaryStage.setTitle("Dungeon Crawler"); // Gives the title to the window.
primaryStage.setResizable(false); // Prevents the window from being resized.
Rectangle rectDungeon = new Rectangle(75, 15, 650, 125); // creates a Rectangle object that consists of the following parameters: X, Y , WIDTH, HEIGHT.
rectDungeon.setStroke(Color.WHITE); // Colors the borders of the rectangle white.
//
// Media musicFile = new Media("file:Night%20Cave.mp3");
// mediaPlayer = new MediaPlayer(musicFile);
// mediaPlayer.setAutoPlay(true); // Sets the music to start as soon as the program runs. NOTE: TRYING TO GET A MP3 FILE IN AND WHENEVER I USE THIS IT CAUSES THE PROGRAM TO NOT RUN.
// NEED TO FIGURE SOMETHING OUT.
// Sets up the button that will be used
// to start the game when it runs.
start = new Button();
start.setText("Start");
start.setOnAction(e -> primaryStage.setScene(beginBattle)); // Sets the button to activate an event handler for the next scene (beast battle starts).
start.setLayoutX(360); // Sets the x axis location of the button.
start.setLayoutY(720); // Sets the y axis location of the button.
Image dungeonImage = new Image("file:Dungeon.png"); // Sets the image object as an image of a door.
ImageView dungeonView = new ImageView(); // Sets up an object of the ImageView class.
dungeonView.setImage(dungeonImage); // sets the image of the door to be viewed in the GUI
// Dialogue initializing the player to what their task is.
Text dungeonText = new Text(" You traverse the sewers of your city. "
+ "\n You heard of a monster who lurked here and took a quest to destroy it. "
+ "\n Worrying for the safety of the citizens you now search for it."
+ "\n Suddenly, you hear a growl and see a mass bellowing towards you.");
dungeonText.setFont(Font.font("Sans serif", FontWeight.NORMAL, FontPosture.ITALIC, 20)); // Sets the text as Sans serif with no bolding, italics, and a size of 20
dungeonText.setStyle("-fx-fill: white;"); // Sets the text with the color white.
dungeonText.setLayoutX(90); // Sets the x axis location of the text.
dungeonText.setLayoutY(40); // Sets the y axis location of the text.
Pane root = new Pane(); // Sets an object to collect all items for the GUI for the scene in question.
root.getChildren().addAll(dungeonView, rectDungeon, dungeonText, start); // Collects the items needed in the scene.
intro = new Scene(root, 788, 788); // Sets up the GUI items, and the window size.
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Rectangle rectMonsterDungeon = new Rectangle(75, 15, 650, 65); // X, Y , WIDTH, HEIGHT ------------> CHANGE COMMENT
rectMonsterDungeon.setStroke(Color.WHITE);
// Sets up the button that will be used
// to move forward if in a one way hall.
battle = new Button();
battle.setText("Battle!");
battle.setOnAction(e -> primaryStage.setScene(standby)); // Sets the button to activate an event handler for the next scene (a hallway).
battle.setLayoutX(355); // Sets the x axis location of the button.
battle.setLayoutY(720); // Sets the y axis location of the button.
Image monsterDungeonImage = new Image("file:MonsterDungeon.png"); // Sets the image object as an image of a one way hallway.
ImageView monsterDungeonView = new ImageView(); // Sets up an object of the ImageView class.
monsterDungeonView.setImage(monsterDungeonImage); // sets the image of the one way hallway to be viewed in the GUI
// Dialogue warning the player of possible dangers
// ahead when they enter the first room of the crypt.
Text monsterDungeonText = new Text(" The beast has reared it's ugly head in front of you!"
+ "\n Prepare to battle or else perish!");
monsterDungeonText.setFont(Font.font("Sans serif", FontWeight.NORMAL, FontPosture.ITALIC, 20)); // Sets the text as Sans serif with no bolding, italics, and a size of 20
monsterDungeonText.setStyle("-fx-fill: white;"); // Sets the text with the color white.
monsterDungeonText.setLayoutX(90); // Sets the x axis location of the text.
monsterDungeonText.setLayoutY(40); // Sets the y axis location of the text.
Pane root2 = new Pane(); // Sets an object to collect all items for the GUI for the scene in question.
root2.getChildren().addAll(monsterDungeonView, rectMonsterDungeon, monsterDungeonText, battle); // Collects the items needed in the scene.
beginBattle = new Scene(root2, 800, 800); // Sets up the GUI items, and the window size.
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
while(playerHealth > 0 && monsterHealth > 0) {
Rectangle rectMonsterDungeonStandby = new Rectangle(75, 15, 650, 65); // X, Y , WIDTH, HEIGHT ------------> CHANGE COMMENT
rectMonsterDungeonStandby.setStroke(Color.WHITE);
Rectangle rectPlayerBoxSB = new Rectangle(140 , 650, 500, 125);
rectPlayerBoxSB.setStroke(Color.WHITE);
// Sets up the button that will be used
// to move forward if in a one way hall.
attack = new Button();
attack.setText("Attack!");
attack.setOnAction(e -> primaryStage.setScene(intro)); // Sets the button to activate an event handler for the next scene (a hallway).
attack.setLayoutX(300); // Sets the x axis location of the button.
attack.setLayoutY(720); // Sets the y axis location of the button.
// Sets up the button that will be used
// to defend against the monster if the player
// wished to block all or most damage from the
// monster.
defend = new Button();
defend.setText("Defend!");
defend.setOnAction(e -> primaryStage.setScene(standby));
defend.setLayoutX(410);
defend.setLayoutY(720);
Image monsterDungeonImageSB = new Image("file:MonsterDungeonSTANDBY.png"); // Sets the image object as an image of a one way hallway.
ImageView monsterDungeonViewSB = new ImageView(); // Sets up an object of the ImageView class.
monsterDungeonViewSB.setImage(monsterDungeonImageSB); // sets the image of the one way hallway to be viewed in the GUI
// Dialogue warning the player of possible dangers
// ahead when they enter the first room of the crypt.
Text monsterDungeonTextSB = new Text(" Choose from the options below."
+ "\n The Beast's Health: " + monsterHealth);
monsterDungeonTextSB.setFont(Font.font("Sans serif", FontWeight.NORMAL, FontPosture.ITALIC, 20)); // Sets the text as Sans serif with no bolding, italics, and a size of 20
monsterDungeonTextSB.setStyle("-fx-fill: white;"); // Sets the text with the color white.
monsterDungeonTextSB.setLayoutX(90); // Sets the x axis location of the text.
monsterDungeonTextSB.setLayoutY(40); // Sets the y axis location of the text.
Text playerTextSB = new Text("Which will you do?"
+ "\n Health: " + playerHealth);
playerTextSB.setFont(Font.font("Sans serif", FontWeight.NORMAL, FontPosture.ITALIC, 20));
playerTextSB.setStyle("-fx-fill: white");
playerTextSB.setLayoutX(310);
playerTextSB.setLayoutY(685);
Pane root3 = new Pane(); // Sets an object to collect all items for the GUI for the scene in question.
root3.getChildren().addAll(monsterDungeonViewSB, rectMonsterDungeonStandby, rectPlayerBoxSB, monsterDungeonTextSB, playerTextSB, attack, defend); // Collects the items needed in the scene.
standby = new Scene(root3, 800, 800); // Sets up the GUI items, and the window size.
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
primaryStage.setScene(intro); // Sets the first scene in the GUI as the DOOR.png with its accompanying text and button.
primaryStage.show(); // Shows the GUI to the user and provides the different scenes in the program
// depending on the button choices the user makes.
}
public static void main(String[] args) {
launch(args);
}
}
I'm expecting the GUI to run just as if the while was not there since both playerHealth and monsterHealth are initially set greater than 0.
Instead I get no GUI window to pop up and no errors come up.
Your expectation is simply wrong. You write:
I'm expecting the GUI to run just as if the while was not there since both
playerHealth and monsterHealth are initially set greater than 0.
But the while-loop condition states just the opposite:
while(playerHealth > 0 && monsterHealth > 0) {
For your initial conditions this loop will run for ever and thus block the JavaFX application thread for ever.

Scrollbar - the block increment seems variable not fixed

In the example below the block increment of the scrollbar is set to 100. However it appears that the actual value will be a value between 1 - 100 depending on where you click on the track bar.
When running the example try clicking roughly in the middle of the track several times to scroll down. When I do this I get output like:
ScrollBar Max: 400.0
LayoutY: -100.0
LayoutY: -200.0
LayoutY: -300.0
LayoutY: -375.0 // this varies depending on the click location
I am expecting the scroll units to change by 100 no matter where I click on the track. (ie, only a single label should be visible at a time).
Is this a common scrolling feature? If so, is there a way to turn it off?
Here is the code:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Orientation;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ScrollBarSSCCE extends Application
{
#Override
public void start(Stage stage)
{
int labelWidth = 300;
int labelHeight = 100;
String[] styles =
{
"-fx-background-color: #336699;",
"-fx-background-color: #996633;",
"-fx-background-color: #ff0000;",
"-fx-background-color: #00ff00;",
"-fx-background-color: #0000ff;"
};
VBox vb = new VBox();
for (int i = 0; i < styles.length; i++)
{
Label label = new Label();
label.setPrefWidth( labelWidth );
label.setPrefHeight( labelHeight );
label.setStyle( styles[i] );
vb.getChildren().add( label );
}
ScrollBar sc = new ScrollBar();
sc.setOrientation(Orientation.VERTICAL);
sc.setLayoutX(labelWidth);
sc.setMin(0);
sc.setMax( (styles.length - 1) * labelHeight);
System.out.println("ScrollBar Max: " + sc.getMax());
sc.setPrefHeight( labelHeight );
sc.setUnitIncrement( labelHeight / 2 );
sc.setBlockIncrement( labelHeight );
sc.valueProperty().addListener(
(ObservableValue<? extends Number> ov, Number old_val, Number new_val) ->
{
double y = -new_val.doubleValue();
System.out.println("LayoutY: " + y);
vb.setLayoutY( y );
});
Group root = new Group();
root.getChildren().addAll(vb, sc);
Scene scene = new Scene(root, labelWidth+20, labelHeight);
stage.setScene(scene);
stage.setTitle("ScrollBar SSCCE");
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
i cant give a good explanation
System.out.println("block increment: " +sc.getBlockIncrement()); is always the same, and when i tested it, it happens on both sides going down or going up, i think it has to do with the ScrollBar.adjustValue(position); so if i am to provide some sort of solution i will say adjust the Value when you most need it.
//in your value listener you can add these codes
if(new_val.doubleValue() > old_val.doubleValue()){ //going down
if(sc.getMax()-sc.getBlockIncrement() < Math.abs(y)){
sc.adjustValue(1);
}
}else{//going up
if(old_val.doubleValue() == sc.getBlockIncrement()){
sc.adjustValue(0);
}
}
i think with this you might want to set sc.setUnitIncrement(labelHeight/2); the same as sc.setBlockIncrement(labelHeight); as that code might ruing the flow of UnitIncrement
hope it hepls :)
The ScrollBar.adjustValue(position) method is responsible for this behaviour.
It basically has three steps:
determine a "position value" by using the relative location of the mouse pressed
determine the "new value" by using the block increment value (this is the value I want)
Determine whether to use the "position value" or the "new value".
Since I always want the "new value" I just removed step 3 from the adjustValue(...) method.
Here is the code for the custom ScrollBar:
ScrollBar sc = new ScrollBar()
{
#Override
public void adjustValue(double position)
{
// figure out the "value" associated with the specified position
double posValue = ((getMax() - getMin()) * Utils.clamp(0, position, 1)) + getMin();
double newValue;
if (Double.compare(posValue, getValue()) != 0)
{
if (posValue > getValue())
{
newValue = getValue() + getBlockIncrement();
}
else
{
newValue = getValue() - getBlockIncrement();
}
setValue( Utils.clamp(getMin(), newValue, getMax()) );
}
}
};

Categories