2

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);
  • If I understand correctly, you may want to look into the different JavaFX layout pane options (`VBox`, `HBox`, `Stackpane`, etc). If you keep the player's balance in a container separate from the "USD" string, you can force them to never overlap, regardless of how much the balance text grows. Without more details on your issue, though, it's not really possible to give an exact answer. – Zephyr Aug 27 '19 at 04:46
  • 1
    Unless you're working with graphics or animations, I think you should always avoid using `.setLayoutX()` and `.setlayoutY()` whenever possible. Let the JavaFX containers take care of the layouts for you. Setting absolute locations for regular UI controls will almost always lead to layout issues. – Zephyr Aug 27 '19 at 04:53
  • And you probably should not use Text but Label instead. – mipa Aug 27 '19 at 09:14

2 Answers2

6

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:

screencast


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.

Zephyr
  • 9,885
  • 4
  • 28
  • 63
3

You can use a TextFlow to render rich text in multiple fonts, sizes, styles and colors:

money

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:

  1. Using layout containers with constraints to control layout (as in Zephyr's answer and this related question: Javafx Text multi-word colorization).
  2. Embedding a WebView to render CSS formatted html.
  3. Using a third party lib, like RichTextFX.
jewelsea
  • 150,031
  • 14
  • 366
  • 406