I'm just working on a little project and started to get in JavaFX.
I have a problem and I don't know any solutions..
So I managed it to bring a Scrollbar(incl Listener) into the root.
But the listener doesn't work.
I want to add many comboboxes one below the other and when I reached the scenesize(LayoutX) it should be possible to scrolldown.
How can I solve my problem?
package application;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Pane;
import javafx.scene.text.*;
import javafx.stage.Stage;
import application.XMLActions;;
public class Main extends Application {
/**
* Globale Variablen
*/
int abstandszaehler = 0;
private Pane root = new Pane();
private ScrollBar sB = new ScrollBar();
private XMLActions xA = new XMLActions();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("XML-Zeilenersteller");
primaryStage.setResizable(false);
/**
* Elemente für die Root
*/
//Buttons
Button newXMLLine = new Button();
newXMLLine.setText("Einfügen neuer XML-Zeile");
newXMLLine.setLayoutX(735);
newXMLLine.setLayoutY(80);
newXMLLine.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
setComboBox();
}
});
Button newXMLDocument = new Button();
newXMLDocument.setText("Erstelle XML-Dokument");
newXMLDocument.setLayoutX(735);
newXMLDocument.setLayoutY(550);
newXMLDocument.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Test2");
}
});
//Überschrift
Text header = new Text(105, 50, "XML Lines Creator");
header.setFont(new Font(30)); header.setStyle("-fx-underline: true;");
//Scrollbar
sB.setLayoutX(715);
sB.setLayoutY(80);
sB.setMin(0);
sB.setOrientation(Orientation.VERTICAL);
sB.setPrefHeight(500);
sB.setMax(360);
sB.setUnitIncrement(30);
sB.setBlockIncrement(35);
sB.valueProperty().addListener((ObservableValue<? extends Number> ov,
Number old_val, Number new_val) -> {
System.out.println(-new_val.doubleValue());
});
/**
* Hauptseite als Root
* Rootbearbeitungen
*/
root.setStyle("-fx-background-color: lightsteelblue");
root.getChildren().addAll(sB,newXMLDocument,header,newXMLLine );
//Scene setzen
Scene mainScene = new Scene(root, 900, 600);
primaryStage.setScene(mainScene);
primaryStage.show();
}
public void setComboBox(){
ComboBox cB = new ComboBox(xA.getList());
root.getChildren().add(cB);
cB.setLayoutX(80);
cB.setLayoutY(80 + abstandszaehler);
abstandszaehler = abstandszaehler + 30;
}
}
EDIT 1:
I got a little progress with that code in the listener:
root.setLayoutY(-new_val.doubleValue());
Replace the Pane with a ScrollPane. On the ScrollPane you can define the policy for your scrollbar.
If you define it like this, it will behave like a Pane:
ScrollPane sp = new ScrollPane();
sp.setHbarPolicy(ScrollBarPolicy.NEVER);
sp.setVbarPolicy(ScrollBarPolicy.NEVER);
Take a look at this article by Oracle.
First, if you wish to layout controls vertically consider using VBox. This VBox should then be enclosed by a ScrollPane.
If you then set the VBox's prefHeight to Control.USE_COMPUTED_SIZE and maxHeight to Double.POSITIVE_INFINITY the VBox should resize to fit it's content without limit, and the enclosing ScrollPane should show and hide scrollbars as necessary.
Related
I have a Label and a ProgressBar in a GridPane.
I've registered an onMouseClicked-event handler on the GridPane.
If I click on the Label the handler gets triggered.
If I click on the ProgressBar the handler doesn't get triggered.
Why? How can I fix it?
package test;
import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.stage.Stage;
public class ProgressBarTestApplication extends Application {
#Override
public void start(Stage primaryStage) {
GridPane gridPane = new GridPane();
ColumnConstraints columnConstraints0 = new ColumnConstraints();
columnConstraints0.setHgrow(Priority.SOMETIMES);
ColumnConstraints columnConstraints1 = new ColumnConstraints();
columnConstraints1.setHgrow(Priority.NEVER);
gridPane.getColumnConstraints().addAll(columnConstraints0, columnConstraints1);
RowConstraints rowConstraints0 = new RowConstraints();
rowConstraints0.setVgrow(Priority.SOMETIMES);
gridPane.getRowConstraints().add(rowConstraints0);
Label someLabel = new Label("Some Label:");
ProgressBar progressBar = new ProgressBar();
progressBar.setPrefWidth(250.0d);
someLabel.setLabelFor(progressBar);
gridPane.add(someLabel, 0, 0);
gridPane.add(progressBar, 1, 0);
gridPane.setCursor(Cursor.HAND);
gridPane.setHgap(5.0d);
gridPane.setOnMouseClicked(event -> System.out.println("Clicked!"));
Scene scene = new Scene(gridPane, 350, 150);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
It would seem that the MouseEvent is being consumed. I had a quick look through the ProgressBar class and it would seem that the ProgressIndicatorSkin's BehaviourBase may be causing the event to be consumed.
A quick and dirty solution would be to set the mouse clicked EventHandler for the ProgressBar to re-fire the event to the GridPane:
progressBar.setOnMouseClicked(e -> gridPane.fireEvent(e));
or to work with any Parent node:
progressBar.setOnMouseClicked(e -> progressBar.getParent().fireEvent(e));
or subclass ProgressBar to automatically apply this behavior:
public class NoConsumeProgressBar extends ProgressBar {
public NoConsumeProgressBar() {
setOnMouseClicked(e -> {
Parent parent = getParent();
if (parent != null) {
parent.fireEvent(e);
}
});
}
}
I have a Pane Object where users can drag and drop various ImageViews. For this, I used
pane.getChildren(imageViewObject)
method
Now, after replacing Pane with ScrollPane, it does not have this method. So I don't know how to get arrount this issue.
Thank you in advance
you can specify only one node with ScrollPane. To create a scroll view with more than one component, use layout containers or the Group class.
Pane pane = ...;
ScrollPane sp = new ScrollPane();
sp.setContent(pane);
Example:
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
*
* #author kachna
*/
public class Test extends Application {
#Override
public void start(Stage stage) throws Exception {
VBox root = new VBox();
root.getChildren().addAll(new Button("button1"), new Button("button2"), new Button("button3"));
root.setSpacing(10);
root.setPadding(new Insets(10));
ScrollPane sp = new ScrollPane();
sp.setContent(root);
sp.setPannable(true); // it means that the user should be able to pan the viewport by using the mouse.
Scene scene = new Scene(sp, 100, 100);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
So I have this code here, and it is supposed to dynamically add textfields and buttons depending on how many times the user presses New Column however it is not adding anything.
newColumn.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent e){
String column = columns.getText();
columns.clear();
final HBox hbox1 = new HBox();
final TextField textField = new TextField();
textField.setText(column);
Button delete = new Button("X");
vbox.getChildren().add(hbox1);
delete.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent e){
vbox.getChildren().remove(hbox1);
}
});
}
});
This is the part of the code that is supposed to add the new buttons. Here's the rest of the code, that displays the window and everythig else:
package GUI;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Example extends Application{
/**
* #param args
*/
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new Group());
primaryStage.setTitle("Parameters");
primaryStage.setWidth(500);
primaryStage.setHeight(800);
showWindow(scene);
primaryStage.setScene(scene);
primaryStage.show();
}
public void showWindow(Scene scene){
final VBox vbox = new VBox();
final HBox hbox = new HBox();
final TextField columns = new TextField();
Button newColumn = new Button("New Column");
Button done = new Button("Done");
hbox.setSpacing(5);
hbox.getChildren().addAll(columns, newColumn);
vbox.setSpacing(5);
vbox.setPadding(new Insets(20, 0, 0, 20));
vbox.getChildren().addAll(hbox);
newColumn.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent e){
String column = columns.getText();
columns.clear();
final HBox hbox1 = new HBox();
final TextField textField = new TextField();
textField.setText(column);
Button delete = new Button("X");
vbox.getChildren().add(hbox1);
delete.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent e){
vbox.getChildren().remove(hbox1);
}
});
}
});
vbox.getChildren().addAll(done);
((Group) scene.getRoot()).getChildren().addAll(vbox);
}
}
I'm also using JavaFX, if that helps.
If you want to be dynamic in adding and removing JComponents , consider storing them in an ArrayList or Vector. In the start, there will be a default JTextField and then then user will add one after another.
In the actionPerformed() of the JButton, create a new JTextField of whatever size you want. Call invalidate() on the content pane of your JFrame and then just add whatever component you need.
Good to go !
SSCCE
package stack;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class DynamicButtons extends JFrame{
JButton addMore = new JButton("AddMore");
Container contentPane = null;
public DynamicButtons(){
contentPane = this.getContentPane();
contentPane.setLayout(new FlowLayout());
addMore.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
JButton newButton = new JButton("ABCD");
contentPane.invalidate();
contentPane.add(newButton);
pack();
}
});
contentPane.add(addMore);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
new DynamicButtons();
}
});
}
}
The OP needs JavaFX
1. Get your whatever Pane you are using.
2. Call the yourPane.getChildren()
3. In the listener of the button, just add the child to the list you obtained above
Using JavaFX 2, I have a basic example of a ScrollPane that contains an HBox of Labels. I want to be able to add a Label to the HBox, and simultaneously scroll to the right edge of the ScrollPane so that the newly added Label is visible. My current method uses the setHvalue() to set the scroll position and getHmax() to get the maximum scrolling distance allowed.
The problem is that when I set the scroll position using getHmax(), it is as if the just-added Label is not computed in the ScrollPanel's scroll width. Is there a way that I can update this inner width before trying setHvalue?
Please see this simple example code which exhibits the issue.
In particular, please notice the addChatItem(String item) method, which contains the logic for scrolling to the edge of the ScrollPane.
import java.util.Timer;
import java.util.TimerTask;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class ScrollPaneTest extends Application {
static int defaultFontSize = 30;
ScrollPaneTest scrollPaneTest = this;
ScrollPane chatBoxScrollPane = new ScrollPane();
HBox chatBox = new HBox();
Chatter chatter = new Chatter();
public static void main(String[] args) {
launch(args);//default
}
#Override
public void stop() throws Exception {
super.stop();
System.exit(0);
}
#Override
public void start(Stage primaryStage) {
BorderPane borderPane = new BorderPane();
StackPane chatBoxStackPane = new StackPane();
chatBoxScrollPane.setContent(chatBox);
//chatBoxScrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
chatBoxScrollPane.setMaxHeight(50);
chatBoxStackPane.getChildren().add(chatBoxScrollPane);
borderPane.setCenter(chatBoxStackPane);
Scene scene = new Scene(borderPane, 800, 600);
primaryStage.setScene(scene);
primaryStage.setTitle("Scroll Demo");
primaryStage.show();
new Thread("mainGameControlThread") {
public void run() {
chatter.chatLoop(scrollPaneTest);
}
}.start();
}
public void addChatItem(String chatString) {
Label title = new Label(chatString);
title.setFont(new Font("Verdana", defaultFontSize));
chatBox.getChildren().add(title);
chatBoxScrollPane.setHvalue(chatBoxScrollPane.getHmax());
}
class Chatter {
public void chatLoop(final ScrollPaneTest test) {
Timer closingCeremonyTimer = new Timer();
closingCeremonyTimer.schedule(new TimerTask() {
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
test.addChatItem("Hello World. ");
}
});
chatLoop(test);
}
}, (long) (0.5*1000));
}
}
}
Here is an image of issue, notice how the ScrollPane has not scrolled to the right edge.
Edit:
I've come up with a workaround but it is very far from ideal. My solution is to start a timer which will use setHvalue() after enough time has passed for the ScrollPane to discover the true width of its content. my addChatItem() method now looks like this:
public void addChatItem(String chatString) {
Label title = new Label(chatString);
title.setFont(new Font("Verdana", defaultFontSize));
chatBox.getChildren().add(title);
Timer closingCeremonyTimer = new Timer();
closingCeremonyTimer.schedule(new TimerTask() {
public void run() {
chatBoxScrollPane.setHvalue(chatBoxScrollPane.getHmax());
}
}, (long) 50);
}
Unfortunately, the number 50 in that method needs to be greater than the time that it takes the ScrollPane to update its inner content's width, and that seems to be far from guaranteed.
you can bind to Hbox widthproperty chnages .
Sample Code :
//in start method add this code
DoubleProperty wProperty = new SimpleDoubleProperty();
wProperty.bind(chatBox.widthProperty()); // bind to Hbox width chnages
wProperty.addListener(new ChangeListener() {
#Override
public void changed(ObservableValue ov, Object t, Object t1) {
//when ever Hbox width chnages set ScrollPane Hvalue
chatBoxScrollPane.setHvalue(chatBoxScrollPane.getHmax());
}
}) ;
and
// remove below line from your addChatItem() method
chatBoxScrollPane.setHvalue(chatBoxScrollPane.getHmax());
Result :yes added numbering for fun ;)
In my case I needed to enclose a HBox called labels inside a ScrollPane called msgs and here is the way I handled autoscroll.
NOTE: The changeListener added to labels HBox is implemented via Lambda syntax(available in JDK8)
labels.heightProperty().addListener((observable, oldVal, newVal) ->{
msgs.setVvalue(((Double) newVal).doubleValue());
});
I want to create a refresh button for my webView that will be on top of the view (even if it hides part of the view), when I place the button on the Grid Pane it pushes the webView down or to the side (depends where I place the button)
How can I place my "refresh" button on top of the webView and not move it aside?
import java.util.List;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class webviewbrowser extends Application {
#Override public void start(Stage primaryStage) throws Exception {
Pane root = new WebViewPane();
primaryStage.setScene(new Scene(root, 1024, 768));
primaryStage.setFullScreen(true);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
/**
* Create a resizable WebView pane
*/
public class WebViewPane extends Pane {
public WebViewPane() {
VBox.setVgrow(this, Priority.ALWAYS);
setMaxWidth(Double.MAX_VALUE);
setMaxHeight(Double.MAX_VALUE);
WebView view = new WebView();
view.setMinSize(500, 400);
view.setPrefSize(500, 400);
final WebEngine eng = view.getEngine();
eng.load("http://google.com");
//final TextField locationField = new TextField("http://www.google.com");
//locationField.setMaxHeight(Double.MAX_VALUE);
Button goButton = new Button("Refresh");
goButton.setDefaultButton(true);
EventHandler<ActionEvent> goAction = new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
eng.reload();
}
};
goButton.setOnAction(goAction);
GridPane grid = new GridPane();
grid.setVgap(0);
grid.setHgap(0);
GridPane.setConstraints(goButton,2,0,2,1, HPos.RIGHT, VPos.BOTTOM, Priority.ALWAYS, Priority.ALWAYS);
GridPane.setConstraints(view, 0, 0, 2, 1, HPos.CENTER, VPos.CENTER, Priority.SOMETIMES, Priority.SOMETIMES);
grid.getColumnConstraints().addAll(
new ColumnConstraints(100, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true),
new ColumnConstraints(40, 40, 40, Priority.NEVER, HPos.CENTER, true)
);
grid.getChildren().addAll(goButton, view);
getChildren().add(grid);
}
#Override protected void layoutChildren() {
List<Node> managed = getManagedChildren();
double width = getWidth();
double height = getHeight();
double top = getInsets().getTop();
double right = getInsets().getRight();
double left = getInsets().getLeft();
double bottom = getInsets().getBottom();
for (int i = 0; i < managed.size(); i++) {
Node child = managed.get(i);
layoutInArea(child, left, top,
width - left - right, height - top - bottom,
0, Insets.EMPTY, true, true, HPos.CENTER, VPos.CENTER);
}
}
}
}
If you want to stack one component on top of another, don't use a GridPane for layout, instead use a parent that allows layout components to be placed on top of one another. For example, a standard Pane, a StackPane, Group or Region. In these stacked style layouts, the components are rendered in order of the child component's position in the parent's child list.
In your sample code you are already extending Pane, so get rid of all of the grid code and just do:
getChildren().addAll(view, goButton);
instead of:
grid.getChildren().addAll(goButton, view);
Modify the layout properties of your goButton to position it within a parent which does not manage the layout position of it's children, e.g. you can call goButton.relocate(xPos, yPos).
You have some custom stuff in the layoutChildren method you override that may mess up the default Pane layout processing logic. Overriding layoutChildren is more of an advanced layout topic and I wouldn't advise it for beginners.
Here is an updated sample you could look at which uses some of the concepts mentioned in this answer.
import javafx.application.Application;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.web.*;
import javafx.stage.Stage;
public class WebViewBrowser extends Application {
#Override public void start(Stage stage) throws Exception {
stage.setScene(new Scene(new WebViewPane("http://google.com")));
stage.setFullScreen(true);
stage.show();
}
public static void main(String[] args) { launch(args); }
}
class WebViewPane extends Pane {
final WebView view = new WebView();
final Button goButton = createGoButton(view.getEngine());
public WebViewPane(String initURL) {
view.getEngine().load(initURL);
getChildren().addAll(
view,
goButton
);
initLayout();
}
private Button createGoButton(final WebEngine eng) {
Button go = new Button("Refresh");
go.setDefaultButton(true);
go.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
eng.reload();
}
});
return go;
}
private void initLayout() {
setMinSize(500, 400);
setPrefSize(1024, 768);
view.prefWidthProperty().bind(widthProperty());
view.prefHeightProperty().bind(heightProperty());
goButton.setLayoutX(10);
goButton.layoutYProperty().bind(
heightProperty().subtract(20).subtract(goButton.heightProperty())
);
}
}