I will go straight to the point. I am building a server-client system that will basically be a cloud storage. Therefore, server will send client a list of files and folders at connection time and client will show them to the user.
What I am looking for is a JavaFx view that will show these contents in a desktop-like fashion: each of them with its own icon and a name under it.
I am confident there are better ways to do this than a complex ad-hoc GridView, but it seems I am unable to find them.
Hope someone will help me...
Thank you all in advance!
You should try to use flow pane of JavaFX, it will add children in flow. You can give icons to them on conditions like if you get the directory then give the folder icon else file icon like this.
Refer to this for the Flow Pane
and Layout building
Example :
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class DemoFile extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FlowPane flowPane = new FlowPane();
for (int i = 0; i < 20; i++) {
Button button = new Button("File Name or folder name");
button.setPrefSize(200, 200);
flowPane.getChildren().add(button);
}
Scene scene = new Scene(flowPane);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Try this example in which I have added 20 buttons in flow pane but you can change the component as you wish you can also set the padding of flow pane to give the spacing between the children's of flow pane
Related
I am in the process of teaching myself JavaFX. Coming from the Swing world there are a lot of similarities between the 2. Especially event processing. Part of my process is to try and mimic an existing application as closely as possible. One of the things I am doing is creating a dialog that will allow the user to select a font to use. There is a text field for them to type in the font name and a list where they can scroll and select one. When they start typing the list will automatically scroll to through the list to start matching what the user is typing. I am also trying to populate the text field with the currently matched font name and then highlight the portion that the user has not typed yet so they can continue to type until the correct match is found.
For example if the user types the letter 't' on Windows the first font found is Tahoma. So the text field will be set to Tahoma and the carat will be positioned right after the 'T' and the 'ahoma' will be highlighted. What happens instead is that the field is populated with Tahoma and the carat is positioned at the end and nothing is highlighted. So it is like it is ignoring the 2 lines of code for positioning and highlighting or the event processor is causing my calls to JavaFX libraries to be run out of order.
I think this may be a bug with JavaFX but it could also be my misunderstanding of the event system. Please let me know which one and why.
Here is a complete sample code showing the problem. Just start typing in the text field to try it out.
package test;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TestTyping extends Application {
ChangeListener<String> textChange;
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
TextField text = new TextField();
root.setTop(text);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
textChange = (observable, oldValue, newValue) -> {
text.textProperty().removeListener(textChange);
for (String family : Font.getFamilies()) {
if (family.equalsIgnoreCase(newValue) || family.toLowerCase().startsWith(newValue.toLowerCase())) {
text.setText(family);
text.positionCaret(newValue.length());
text.selectEnd();
break;
}
}
text.textProperty().addListener(textChange);
};
text.textProperty().addListener(textChange);
}
public static void main(String... args) {
launch(args);
}
}
Wrap caret position and select end into Platform.runLater. The problem is in events order. I don't know correct details about this issue so I will not provide you a detailed answer, only solution.
Platform.runLater(()-> {
text.positionCaret(newValue.length());
text.selectEnd();
});
Here's an alternative approach entirely, which uses a TextFormatter to modify changes to the text. The advantage here is that it doesn't rely on the "timing" of various property changes with respect to event handling, which is not documented and thus could possibly change in later JavaFX versions. It also avoids the slightly ugly "remove the listener and add it back" idiom.
import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TestTyping extends Application {
ChangeListener<String> textChange;
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
TextField text = new TextField();
root.setTop(text);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
UnaryOperator<Change> filter = c -> {
// for delete, move the caret, or change selection, don't modify anything...
if (c.getText().isEmpty()) {
return c ;
}
for (String family : Font.getFamilies()) {
if (family.toLowerCase().startsWith(c.getControlNewText().toLowerCase())) {
c.setText(family.substring(c.getRangeStart(), family.length()));
c.setAnchor(c.getControlNewText().length());
break ;
}
}
return c ;
};
text.setTextFormatter(new TextFormatter<String>(filter));
}
public static void main(String... args) {
launch(args);
}
}
This should be a relatively simple problem, but it is driving me insane. I am trying to create Mine Sweeper in JavaFX (mostly just for practice) but I can not get even a simple rectangle to display. I had the game running once before, but I am trying to make the game more abstract, and hence easier to code, but I am running into the issue of nothing being displayed.
I eliminated all extraneous code so it is as simple as possible. I am basically trying to create a Rectangle with a certain color and size called Box, add the box to the pane, and display the pane. In order to make Box a node that can be displayed on the pane, I made the Box class extend Rectangle, so that a Box would have the same properties as a Rectangle. But when I run the code, it gives just an empty pane with no box in it.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Minesweeper extends Application {
#Override
public void start(Stage stage) {
Pane pane = new Pane();
Box box = new Box();
pane.getChildren().addAll(box);
// Create the scene
Scene scene = new Scene(pane);
stage.setTitle("Minesweeper");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
public class Box extends Rectangle {
public Box() {
Rectangle box = new Rectangle(100, 100, 100, 100);
box.setFill(Color.BLUE);
}
}
I realized if I put the code from Box into the main Minesweeper class, it will display the box. But Box will have a ton of other properties and therefore needs to be a class on its own.
What am I doing wrong that does not allow the box to be displayed?
Thanks in advance for your help and consideration.
You create a new Rectangle in your Box class. This Rectangle is not added to any Parent container, so it's not visible.
Change your code to:
public Box() {
super(100, 100, 100, 100);
setFill(Color.BLUE);
}
I am well aware of the Java FX Node API which states:
A disabled Node does not receive mouse or key events.
So, I am trying to come up with a work around. In our old Swing application we used to allow users to double click on a disabled web/email field (Formatted TextField) to open a link to the page or their native mail client. I am hoping to simulate this behavior in FX. Instead of calling:
setDisable(true);
I am now calling:
setEditable(false);
The only remaining issue is that I would like to style the Node as if it were disabled, or at minimum disable text selection.
Is there a simple way to get the exact style of the Node when it is disabled, or will I need to create my own CSS class? (Unfortunately my CSS knowledge is relatively weak).
I can't see an "easy" way to do this with CSS, without replicating the default rules for a disabled text field (but maybe someone else has a trick for that).
Here's a completely different approach, though. When the text field is disabled, it doesn't receive mouse events, so any mouse events will just "drop through" to the node below it in Z-order. So if you wrap the text field in some pane, and register a mouse handler with the pane, that mouse handler will get invoked if the text field is disabled.
SSCCE:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class DIsabledTextFieldEventTest extends Application {
#Override
public void start(Stage primaryStage) {
TextField textField = new TextField();
StackPane textFieldHolder = new StackPane(textField);
CheckBox disableTextField = new CheckBox("Disable text field");
textField.disableProperty().bind(disableTextField.selectedProperty());
textFieldHolder.setOnMouseClicked(e -> {
if (e.getClickCount() == 2) {
System.out.println("Double click on disabled text field!");
}
});
VBox root = new VBox(10, disableTextField, textFieldHolder);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 350, 120);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Is there a simple way to get the exact style of the Node when it is disabled, or will I need to create my own CSS class?
If you do the below to your non-editable text field, it looks exactly like a disabled text field (tested on my MacBook).
textField.setStyle("-fx-opacity:0.5");
As the title says, I need to make a thin progress bar. I used this:
progressBar.setMaxHeight(0.1);
progressBar.setPrefHeight(0.1);
but that doesn't work. Does anyone have an idea?
You'll have to mess around with the styling to get it any smaller. I really recommend taking a look a the caspian.css that's included with Javafx - that's the default style sheet. It helps a lot when trying to override the look and feel of the default skins. Here's an example I put together that shows how it can be done:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ProgressBarTest extends Application {
public static void main(String[] args) { launch(args); }
#Override
public void start(final Stage stage) throws Exception {
//All the controls are added here
VBox box = new VBox();
box.getStylesheets().add("test.css");
ProgressBar pb = new ProgressBar(50);
box.getChildren().add(pb);
//Setting up your scene
Scene scene = new Scene(box);
stage.setScene(scene);
stage.show();
}
}
And here's the test.css I loaded up:
.progress-bar .bar {-fx-padding:1px; -fx-background-insets:0;}
And here is the output of the test app:
I'm currently learning to create Graphical Interfaces using JavaFX as i feel like it's
more powerful than Swing and easier to code by hand instead of having to resort to
GUIBuilder.
While this also includes Swing i have read quite a few tutorials so far and I always see
all the code being written in either the main() or start() methods.
For example this code example from java.about.com:
//Imports are listed in full to show what's being used
//could just import javafx.*
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class ApplicationWindow extends Application {
//JavaFX applicatoin still use the main method.
//It should only ever contain the call to the launch method
public static void main(String[] args) {
launch(args);
}
//starting point for the application
//this is where we put the code for the user interface
#Override
public void start(Stage primaryStage) {
//The primaryStage is the top-level container
primaryStage.setTitle("example Gui");
//The BorderPane has the same areas laid out as the
//BorderLayout layout manager
BorderPane componentLayout = new BorderPane();
componentLayout.setPadding(new Insets(20,0,20,20));
//The FlowPane is a conatiner that uses a flow layout
final FlowPane choicePane = new FlowPane();
choicePane.setHgap(100);
Label choiceLbl = new Label("Fruits");
//The choicebox is populated from an observableArrayList
ChoiceBox fruits = new ChoiceBox(FXCollections.observableArrayList("Asparagus", "Beans", "Broccoli", "Cabbage"
, "Carrot", "Celery", "Cucumber", "Leek", "Mushroom"
, "Pepper", "Radish", "Shallot", "Spinach", "Swede"
, "Turnip"));
//Add the label and choicebox to the flowpane
choicePane.getChildren().add(choiceLbl);
choicePane.getChildren().add(fruits);
//put the flowpane in the top area of the BorderPane
componentLayout.setTop(choicePane);
final FlowPane listPane = new FlowPane();
listPane.setHgap(100);
Label listLbl = new Label("Vegetables");
ListView vegetables = new ListView(FXCollections.observableArrayList("Apple", "Apricot", "Banana"
,"Cherry", "Date", "Kiwi", "Orange", "Pear", "Strawberry"));
listPane.getChildren().add(listLbl);
listPane.getChildren().add(vegetables);
listPane.setVisible(false);
componentLayout.setCenter(listPane);
//The button uses an inner class to handle the button click event
Button vegFruitBut = new Button("Fruit or Veg");
vegFruitBut.setOnAction(new EventHandler() {
#Override
public void handle(ActionEvent event) {
//switch the visibility for each FlowPane
choicePane.setVisible(!choicePane.isVisible());
listPane.setVisible(!listPane.isVisible());
}
});
componentLayout.setBottom(vegFruitBut);
//Add the BorderPane to the Scene
Scene appScene = new Scene(componentLayout,500,500);
//Add the Scene to the Stage
primaryStage.setScene(appScene);
primaryStage.show();
}
}
But coding everything in the main() or start() goes against principles i learnt at school
and also seems like a bad thing to do too me.
As i always used to code splitting everything up in different methods and than keeping a clean main() by only calling methods to do stuff, so it's easy to read up on the programs execution path. As i see all tutorials etc. going against this by putting al their GUI code in the main or start methods i started wondering if this is conventional to do regarding GUI code or should i still try to put everything under custom methods and keep a clean main()?
Also could anyone share neat tutorials regarding JavaFX? I can't seem to find much in this matter.
EDIT: I should really just ask for a good example on how a class hierarchy of an advanced application interface looks like to get some ideas.
Hope this was a clear question,
thanks in advance.
Jasper.
You could create a GUI class that holds all of your GUI code together, or simply create a GUI method that resides below main. Then, in main, you could call:
public static void main(String[] args)
{
launch(args);
}
public void start()
{
launchGUI(Stage primaryStage)
}
And then create your launchGUI method either in the same class, or create a new class specifically for the GUI and them use import to import it into the class containing main/start.