I have a ScrollPane control with left and right parts inside. Left part contains a GridPane control. I would like to make the GridPane across the entire width of the parent VBox control.
For this purpose, I used a columnConstraints. Now, the GridPane is full-width, but the scrolling is broken. How can I fix it?
Please try the following sample with and without using the columnConstraints and you will see what I mean.
sample.FXML:
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.text.Text?>
<ScrollPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml"
fitToWidth="true"
fitToHeight="true">
<HBox>
<AnchorPane fx:id="leftPane"
minWidth="500"
prefWidth="500"
maxWidth="500"
style="-fx-background-color: orange">
<VBox style="-fx-background-color: lime"
AnchorPane.topAnchor="0"
AnchorPane.leftAnchor="0"
AnchorPane.rightAnchor="0"
AnchorPane.bottomAnchor="10">
<GridPane fx:id="gridPane"
style="-fx-background-color: brown">
<columnConstraints>
<ColumnConstraints />
<!-- This causes the trouble! -->
<ColumnConstraints hgrow="ALWAYS" />
</columnConstraints>
</GridPane>
<Label>Left Content</Label>
</VBox>
</AnchorPane>
<AnchorPane fx:id="rightPane"
HBox.hgrow="ALWAYS"
style="-fx-background-color: purple">
<Text>Right Content</Text>
</AnchorPane>
</HBox>
</ScrollPane>
with the corresponding Controller.java:
public class Controller implements Initializable {
#FXML
private GridPane gridPane;
private String[] splittedText;
#Override
public void initialize(URL location, ResourceBundle resources) {
// From Wikipedia
final String text = "Dolphins are a widely distributed and diverse group of aquatic mammals. They are an informal grouping within the order Cetacea, excluding whales and porpoises, so to zoologists the grouping is paraphyletic. The dolphins comprise the extant families Delphinidae (the oceanic dolphins), Platanistidae (the Indian river dolphins), Iniidae (the new world river dolphins), and Pontoporiidae (the brackish dolphins). There are 40 extant species of dolphins. Dolphins, alongside other cetaceans, belong to the clade Cetartiodactyla with even-toed ungulates.";
splittedText = text.split(" ");
for (int i = 0; i < 20; ++i) {
gridPane.add(new Text(Integer.toString(i)), 0, i);
gridPane.add(createFlowPane(), 1, i);
}
}
private FlowPane createFlowPane() {
FlowPane flowPane = new FlowPane();
for (int i = 0; i < splittedText.length; ++i)
flowPane.getChildren().add(new Text(splittedText[i]));
return flowPane;
}
}
Related
I want my Chat Window to be reziable and scrollable, so far everything works except the ChatLog itself.
I need a scroll bar, that's why I put the VBox inside of a ScrollPane (which is child of an AnchorPane), but this way only the ScrollPane is responsive (thanks to Anchor Values). If I unwrap VBox I can set Anchor Values, then it works but I'm loosing my scroll bar.
How can I mantain the scroll bar for ChatLog AND make it responsive (attached on the right side)?
FXML:
<AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" style="-fx-background-color: #5b2529;" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.lmu.jungejunkervp.ClientWindowController">
<children>
<ScrollPane fx:id="scrollPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="265.0" prefWidth="592.0" AnchorPane.bottomAnchor="57.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="5.0">
<content>
<VBox fx:id="chatLog" prefHeight="265.0" prefWidth="575.0" />
</content></ScrollPane>
<TextArea fx:id="messageBox" layoutX="5.0" layoutY="349.0" onKeyPressed="#onEnterSend" prefHeight="47.0" prefWidth="536.0" promptText="enter message..." AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="61.0" />
<Button fx:id="sendButton" mnemonicParsing="false" onAction="#setSendButtonAction" onMouseClicked="#setSendButtonAction" prefHeight="47.0" prefWidth="52.0" text="Send" AnchorPane.bottomAnchor="5.0" AnchorPane.rightAnchor="5.0" />
</children>
</AnchorPane>
public void addLabel(String message, Pos position) {
HBox hBox = new HBox();
hBox.setAlignment(position);
hBox.setPadding(new Insets(5, 5, 5, 10));
Text text = new Text(message);
TextFlow textFlow = new TextFlow(text);
textFlow.setStyle("-fx-background-color: rgb(233,233,235);" +
"-fx-background-radius: 20px");
textFlow.setPadding(new Insets(5, 10, 5, 10));
hBox.getChildren().add(textFlow);
Platform.runLater(new Runnable() {
#Override
public void run() {
chatLog.getChildren().add(hBox);
}
});
}
public void setSendButtonAction() {
String message = messageBox.getText().replaceAll("[\n\r]", "");
try {
if (!message.isEmpty()) {
// show message on the sending client window
addLabel(message, Pos.CENTER_RIGHT);
}
}
To answer your actual question, below is what you need add to the ScrollPane (in fxml with your current layout).
fitToHeight="true" fitToWidth="true"
The above code will make your VBox responsive with the ScrollPane.
I also suggest to change your layout to get rid of AnchorPane (will all those hardcoded positions). You can use VBox/HBox in conjuction with vgrow/hgrow policies. The optimized fxml will be as below:
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<VBox style="-fx-background-color: #5b2529;" spacing="5" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.lmu.jungejunkervp.ClientWindowController">
<children>
<ScrollPane fx:id="scrollPane" prefHeight="265.0" prefWidth="592.0" fitToHeight="true" fitToWidth="true" VBox.vgrow="ALWAYS">
<content>
<VBox fx:id="chatLog"/>
</content>
</ScrollPane>
<HBox spacing="5">
<TextArea fx:id="messageBox" onKeyPressed="#onEnterSend" prefHeight="47.0" promptText="enter message..." HBox.hgrow="ALWAYS" />
<Button fx:id="sendButton" mnemonicParsing="false" onAction="#setSendButtonAction" prefHeight="47.0" minWidth="52.0" text="Send"/>
</HBox>
</children>
<padding>
<Insets topRightBottomLeft="5"/>
</padding>
</VBox>
Having said that, I think you need to consider changing your layout to what #jewelsea mentioned in the provided example (using ListView).
I am new to JavaFx:
In my FXML I defined a linechart and over a controller I want to change e.g. the title of the chart.
But somehow it is not working.
Here the FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.chart.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ui.views.view2.viewView2Controller">
<children>
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0">
<children>
<Label text="View 2 Area" />
<Button mnemonicParsing="false" onAction="#nextPane" text="view 1 page" />
<LineChart fx:id="testChart">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</LineChart>
</children>
</VBox>
</children>
</StackPane>
Here the controller Code:
package com.ui.views.view2;
import com.ui.libs.VistaNavigator;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.chart.*;
public class viewView2Controller {
#FXML
private LineChart<String,Number> testChart;
#FXML
private Axis<String> xAxis;
#FXML
private Axis<Number> yAxis;
public viewView2Controller()
{
xAxis = new CategoryAxis(); // we are gonna plot against time
yAxis = new NumberAxis();
xAxis.setLabel("Time/s");
xAxis.setAnimated(false); // axis animations are removed
yAxis.setLabel("Value");
yAxis.setAnimated(false); // axis animations are removed
//creating the line chart with two axis created above
testChart = new LineChart<>(xAxis,yAxis);
testChart.setTitle("my first JavaFX Chart");
testChart.setAnimated(false);
}
public void nextPane(ActionEvent actionEvent) {
VistaNavigator.loadVista(VistaNavigator.viewConfig);
}
}
Whats wrong in my code and what do I need to change that I am able to change the chart title and afterwards with the new recommendation also able to populate values into it. :-)
BR
Dieter
I have a SplitPane (verticle) at the Center of a BorderPane in my Gui. In my SplitPane I add an HBox as the top and a FlowPane as the bottom. Everytime I add a set of cards to a VBox and add the VBox to my FlowPane it adds the VBoxes one after the other instead of adding them vertically. Does anyone know why?
#FXML
private FlowPane setsArea;
//sets
public void addSet(CardSet set) {
VBox setView = new VBox(20);
for (Card card : set.getCards()) {
ImageView imageView = new ImageView(card.getImgUrl());
setView.getChildren().add(imageView);
}
setsArea.getChildren().add(setView);
setViews.put(set, setView);
}
My GameView.fxml where I am adding the SplitPane as the center of my BorderPane.
<center>
<SplitPane dividerPositions="0.5" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0" style="-fx-background-image: url('game.jpg');" BorderPane.alignment="CENTER">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<HBox fx:id="playAreaTop" />
</children></AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<FlowPane fx:id="setsArea" />
</children></AnchorPane>
</items>
</SplitPane>
</center>
How I am adding the cardSets:
Card card = new Basic(Suit.CLUBS, 4, 1);
Card card2 = new Basic(Suit.HEARTS, 5, 2);
Card card3 = new Joker(Suit.JOKER, 15, 3);
Card card4 = new Ace(Suit.HEARTS, 14, 4);
ArrayList<Card> cards = new ArrayList<>();
cards.add(card);
cards.add(card2);
cards.add(card4);
CardSet cardSet = new CardSet(cards);
gameView.addSet(cardSet);
gameView.addSet(cardSet);
gameView.addSet(cardSet);
gameView.addSet(cardSet);
gameView.addSet(cardSet);
gameView.addSet(cardSet);
Thanks.
Adding image into VBox and put them all in a FlowPane don't make them vertically stacked you should replace your FlowPane into a VBox and add the ImageViews to the VBox which stack it's item vertically like so:
<SplitPane dividerPositions="0.5" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0" style="-fx-background-image: url('game.jpg');" BorderPane.alignment="CENTER">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<HBox fx:id="playAreaTop" />
</children></AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<VBox fx:id="setsArea" />
</children></AnchorPane>
</items>
#FXML
private VBox setsArea;
public void addSet(CardSet set){
for (Card card : set.getCards()) {
ImageView imageView = new ImageView(card.getImgUrl());
setsArea.getChildren().add(imageView);
}
setViews.put(set, setView);
}
So here's the deal, i'm trying to code a GUI that shows live data on a linechart.
So far so good, i can get the linechart working but not into the GUI.
Using scenebuilder, i made a view with a linechart object, in order to link it to my generated chart. But for some reason this does not seem to work with this code in my mainApp.
public void showSes() {
try {
// Load the fxml file and set into the center of the main layout
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/Session.fxml"));
AnchorPane Session = (AnchorPane) loader.load();
rootLayout.setCenter(Session);
SessionController controller = loader.getController();
controller.setMainApp(this);
controller.initGraph();
} catch (IOException e) {
// Exception gets thrown if the fxml file could not be loaded
e.printStackTrace();
}
}
This simply shows the view with an empty linechart.
I know the chart should know however, because i can use it to create a scene, and show that into the GUI, but the view i made in scenebuilder also has some other fields i want to show...
Does anybody have an idea?
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.chart.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="900.0" prefWidth="1280.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gui.view.SessionController">
<children>
<Pane prefHeight="900.0" prefWidth="1280.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<LineChart fx:id="linechart" layoutX="29.0" layoutY="194.0" prefHeight="416.0" prefWidth="1222.0" title="Temperature of session">
<xAxis>
<CategoryAxis label="Time (s)" fx:id="xAxis" />
</xAxis>
<yAxis>
<NumberAxis fx:id="yAxis" label="Temperature (°C)" side="LEFT" upperBound="160.0" />
</yAxis>
</LineChart>
<GridPane layoutX="254.0" layoutY="87.0" prefHeight="150.0" prefWidth="771.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="274.0" minWidth="10.0" prefWidth="274.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="273.0" minWidth="10.0" prefWidth="273.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="273.0" minWidth="10.0" prefWidth="273.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label prefHeight="53.0" prefWidth="153.0" text="Temperature fluid:" GridPane.halignment="CENTER" GridPane.valignment="TOP">
<font>
<Font size="16.0" />
</font>
</Label>
<Label prefHeight="52.0" prefWidth="181.0" text="Temperature vapor:" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="TOP">
<font>
<Font size="16.0" />
</font>
</Label>
<TextField fx:id="fluidT" editable="false" />
<TextField fx:id="gasT" editable="false" GridPane.columnIndex="2" />
</children>
</GridPane>
<Label layoutX="474.0" layoutY="14.0" text="TempTracker">
<font>
<Font size="50.0" />
</font>
</Label>
<TextArea editable="false" layoutX="190.0" layoutY="638.0" prefHeight="160.0" prefWidth="900.0" promptText=" ------Warning log------- " wrapText="true" />
<Button layoutX="540.0" layoutY="808.0" mnemonicParsing="false" onAction="#handleStop" prefHeight="65.0" prefWidth="200.0" text="STOP">
<font>
<Font size="22.0" />
</font>
</Button>
</children></Pane>
</children>
</AnchorPane>
CONTROLLER
private static final int MAX_DATA_POINTS = 50;
private String xSeriesData = "";
private XYChart.Series series1;
private XYChart.Series series2;
private ExecutorService executor;
private BlockingQueue<Number> dataQ1 = new ArrayBlockingQueue<Number>(1024);
private BlockingQueue<Number> dataQ2 = new ArrayBlockingQueue<Number>(1024);
#FXML
private CategoryAxis xAxis = new CategoryAxis();
#FXML
final NumberAxis yAxis = new NumberAxis();
#FXML
final LineChart<String, Number> linechart = new LineChart<String, Number>(xAxis, yAxis);
public void initGraph(){
xAxis.setAutoRanging(false);
xAxis.setTickLabelsVisible(false);
xAxis.setTickMarkVisible(false);
NumberAxis yAxis = new NumberAxis();
yAxis.setAutoRanging(true);
//Graph
final LineChart<String, Number> lc = new LineChart<String, Number>(xAxis, yAxis){
#Override
protected void dataItemAdded(Series<String, Number> series, int itemIndex, Data<String, Number> item){}
};
lc.setAnimated(false);
lc.setId("liveLineChart");
lc.setTitle("Animated Line Chart");
//Graph Series
series1 = new XYChart.Series<Number, Number>();
series2 = new XYChart.Series<Number, Number>();
linechart.getData().addAll(series1, series2);
series1.setName("T1");
series2.setName("T2");
fluidT.setText("0000");
gasT.setText("0000");
prepareTimeline();
Runnable con = new Consumer(this);
Thread c = new Thread(con);
c.start();
}
Don't create new objects for #FXML injected members
Never use new in conjunction with #FXML, i.e., never write:
#FXML
private CategoryAxis xAxis = new CategoryAxis();
Instead, just write:
#FXML
private CategoryAxis xAxis;
The FXMLLoader will automatically generate, i.e., create, a new object for each element in the FXML file and inject a reference to that into your controller where you provide an #FXML annotation. So if you reset the #FXML member reference to a new object you create in the controller there will be no association between that object and objects created by the loader.
Additionally, don't create another new LineChart within your initGraph() function. You already have a LineChart created by the FXMLLoader, just reference that. Same for NumberAxis and the other elements you are using #FXML injection with.
If you use an #FXML annotation also use <fx:id>
You have:
#FXML
private CategoryAxis xAxis;
So in your fxml, you should define:
<xAxis fx:id="xAxis">
Otherwise, the FXMLLoader will not be able to inject a reference to the axis you defined in your FXML.
Aside
You may have other errors in your code (e.g., around concurrency and threading). So the above might not be all of your errors. In general, when creating an mcve, try to eliminate anything which is not relevant to the question at hand (e.g. the threading code and non-linechart portions of the FXML), but include everything that somebody could use to copy and paste your code to compile and run it to replicate your issue.
Note: The Ensemble sample application contains a sample program which updates a graph in real-time based upon audio spectrum input data.
I would like to have buttons with equal(maximum) width in a gridpane.
When I'm trying to set it directly in FXML - it works perfectly.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="TestController">
<children>
<GridPane AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0" AnchorPane.bottomAnchor="0"
AnchorPane.topAnchor="0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" percentWidth="50.0"/>
<ColumnConstraints hgrow="SOMETIMES" percentWidth="50.0"/>
</columnConstraints>
<children>
<Button maxWidth="Infinity" text="Button1" GridPane.columnIndex="0"/>
<Button maxWidth="Infinity" text="Button2" GridPane.columnIndex="1"/>
</children>
</GridPane>
</children>
</AnchorPane>
But when I wanted to separate the whole grid into a custom control, the buttons stopped to fill the available width.
Here's a FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import CustomGridPane?>
<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="TestController">
<children>
<CustomGridPane AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0" AnchorPane.bottomAnchor="0" AnchorPane.topAnchor="0">
</CustomGridPane>
</children>
</AnchorPane>
And an extension:
public class CustomGridPane extends GridPane {
private Button button1 = new Button("button1");
private Button button2 = new Button("button2");
public CustomGridPane() {
super();
button1.maxWidth(Double.MAX_VALUE);
button2.maxWidth(Double.MAX_VALUE);
getColumnConstraints().add(new ColumnConstraints());
getColumnConstraints().add(new ColumnConstraints());
getColumnConstraints().get(0).setPercentWidth(50);
getColumnConstraints().get(0).setHgrow(Priority.SOMETIMES);
getColumnConstraints().get(1).setPercentWidth(50);
getColumnConstraints().get(1).setHgrow(Priority.SOMETIMES);
add(button1, 0, 0);
add(button2, 1, 0);
}
}
Am I missing something?
You are using maxWidth property getter instead of the method setMaxWidth (setter).
Please see the documentation of Button here
public final double maxWidth(double height)
Called during layout to determine the maximum width for this node. Returns the value from computeMaxWidth(forHeight) unless the application overrode the maximum width by setting the maxWidth property.
Replace the two lines with maxWidth(...) by these ones:
button1.setMaxWidth(Double.MAX_VALUE);
button2.setMaxWidth(Double.MAX_VALUE);
This can be achieved inside scene builder also...
I required this, while creating a calculator, with the buttons inside gridpane.
Happy Coding (: