Modifying interface with key interactions in javafx - javafx

I'm trying to code a 2048 game using JavaFX and I'm facing a problem.
#Override
public void start(Stage primaryStage){
primaryStage.setResizable(false);
Scene scene = new Scene(firstContent());
primaryStage.setScene(scene);
primaryStage.show();
scene.setOnKeyPressed(new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent e){
KeyCode key = e.getCode();
if((key.equals(KeyCode.UP))){
System.out.println("recieved UP");
Scene scene = new Scene(createContent());
primaryStage.setScene(scene);
primaryStage.show();
} else if(key.equals(KeyCode.DOWN)){
System.out.println("recieved DOWN");
}
}
});
}
So here I open my window initialised with firstContent (basically it creates an array of empty tiles, and fills two of them with 2 or 4 randomly), display it and start listening for key presses. The idea is to have a behavior for each arrow key (UP DOWN LEFT RIGHT) which will move the tiles accordingly. This is done by the following createContent() method :
public Parent createContent(){
String c = "";
List<Integer> known = new ArrayList<Integer>();
Pane root = new Pane();
root.setPrefSize(740, 700);
Random rand = new Random();
int pos1 = rand.nextInt(15);
if(tiles.get(pos1) != new Tile("")){
known.add(pos1);
pos1 = rand.nextInt(15);
if(known.contains(pos1)){
known.add(pos1);
pos1 = rand.nextInt(15);
}
}
for(int i = 0; i < NB_TILES; i++){
tiles.add(new Tile(c));
}
tiles.set(pos1, new Tile("2048"));
for(int i = 0; i < tiles.size(); i++){
// boring stuff to set the tile display to the right size
}
return root;
}
Now for the problem : when the application is running, if I press the down arrow, I do get on my terminal the "recieved DOWN" text as many times as I press the key as expected. But if I press the up arrow, the application will only recieve it once and the application seems to be frozen (meaning if I press down again, nothing happens).
As you could guess, I want to be able to call my method for each key press to be able to move my tiles around and utltimately combine them to get a playable version of 2048... Anyone know why my app gets frozen ?
If needed, I can provide other bits of code but I think I provided the essential. Just know that firstContent() works basically the same as createContent for now except it genereates two random numbers to get the first tiles of the game.
Thanks in advance for your help.

Related

How to disable the arrow button in JavaFX combo box

I have a project I am working on. I am trying to make a dictionary. For that, I have a .csv file with about 55000 words.I am using the trie data structure which has a startsWith() method which checks whether there is a word in the .csv file which matches the given prefix. I had managed to get it to work to find all words that match the given prefix and display them. Now, I have to develop this into a JavaFX app.
So, I thought of using a ComboBox which has its editable attribute set to true so that I could type into it and then the handler associated with the textProperty() of its editor would display all the words starting with given prefix in the listview of the combobox.
Now, the problem I have is that whenever I click the arrow button of the combobox the application stops responding (I think it's because the list view tries to resize itself to fit the items which are 55000).
So, what I want to know is how to disable the arrow button entirely. I have tried to set its background-color to transparent but even then it can still be clicked I want to make it so that it is disabled and transparent basically the combobox ends up looking like a text field.
If there are better, more efficient ways of implementing a dictionary I would appreciate it if you could guide me.
The ListView is a virtual control that only shows a certain number of cells at a time, it doesn't need to "resize itself to the number of items" in any way that would lock up your GUI.
Does this demo program do what you want?
public class Main extends Application {
public static void main(String[] args) throws IOException, URISyntaxException {
launch(args);
}
#Override
public void start(Stage stage) {
List<String> rawWords = Collections.emptyList();
try {
URI wordURI = new URI("https://www-cs-faculty.stanford.edu/~knuth/sgb-words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(wordURI.toURL().openStream(), StandardCharsets.UTF_8));
rawWords = reader.lines().collect(Collectors.toCollection(() -> new ArrayList<>(6000)));
} catch (IOException | URISyntaxException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
// make the list at least as big as in the question
while(rawWords.size() < 55000) {
ArrayList<String> nextWords = new ArrayList<>(rawWords.size() * 2);
nextWords.addAll(rawWords);
nextWords.addAll(rawWords);
rawWords = nextWords;
}
Collections.sort(rawWords);
ObservableList<String> wordList = FXCollections.observableArrayList(rawWords);
FilteredList<String> filteredList = new FilteredList<>(wordList);
ComboBox<String> combo = new ComboBox<>(filteredList);
combo.setEditable(true);
combo.getEditor().textProperty().addListener((obs, oldVal, newVal) -> {
filteredList.setPredicate(s -> newVal == null || newVal.isEmpty() || s.startsWith(newVal));
});
VBox vbox = new VBox(8,new Label("Dictionary ComboBox"),
combo,
new Label("\n\n\n\nThis space intentionally left blank.\n\n\n\n"));
vbox.setPadding(new Insets(8));
Scene scene = new Scene(vbox, 400, 300);
stage.setTitle("Demo - Filtered Combobox List");
stage.setScene(scene);
stage.show();
}
}

JavaFX Button EventHandler works one time(Card Shuffler)

I'm trying to make a program that will display a random set of 4 cards, then when I click the button again it will clear the old set and display a new random set.
Right now my program will display 4 random images of cards when I click the button; however, when I try to click it again nothing happens. I'm assuming it has something to do with the EventHandler no longer being registered to the button after I clear the root children. However, I don't know how to go about fixing this. Any help is greatly appreciated! I haven't been able to find an answer to this yet, and have only been learning JavaFX for about a week. Thank you.
The code I have so far:
public class CardShuffle extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = new StackPane();
Scanner input = new Scanner(System.in);
File cardsFolder = new File("C:\\Users\\timsp\\Pictures\\JPEG");
ArrayList<File> cardsFilePaths = new ArrayList<File> (Arrays.asList(cardsFolder.listFiles()));
Button deal = new Button("DEAL");
Pane hb = new HBox(10);
hb.setPadding(new Insets(5, 5, 5, 5));
root.getChildren().add(deal);
deal.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
root.getChildren().clear();
ArrayList<ImageView> cards = getRandomCards(cardsFilePaths);
for (int i = 0; i < 4; i++) {
cards.get(i).setFitWidth(150);
cards.get(i).setFitHeight(100);
hb.getChildren().add(cards.get(i));
}
root.getChildren().addAll(deal, hb);
}
});
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public ArrayList<ImageView> getRandomCards(ArrayList<File> cardsFilePaths) {
ArrayList<ImageView> cards = new ArrayList<ImageView>();
try {
for (int i = 0; i < 4; i++) {
Image card = new Image((new FileInputStream(cardsFilePaths.get((int) (Math.random() * 52)).getPath())));
ImageView temp = new ImageView();
temp.setImage(card);
cards.add(temp);
}
} catch (FileNotFoundException e) {
e.getMessage();
}
return cards;
}
}
Many problems here :
the first one, and the most important (because it hides your further error) is the root layout : you use a StackPane, the first thing you should do is to replace it by a VBox for example and rerun your program, it will be easier to see what really happens. (you will not have 4 cards, but 8, 12, 16 and so on).
the first one generates the second one. By doing this root.getChildren().addAll(deal, hb); you put the HBox layout above the button, and the click is first consumed by the HBox. Here is an example to see it more easily :
// Add the HBox as soon as the initialization
root.getChildren().add(deal);
hb.setOnMouseClicked(e -> System.out.println("HBox clicked"));
deal.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
root.getChildren().clear();
ArrayList<ImageView> cards = getRandomCards(cardsFilePaths);
for(int i = 0; i < 4; i++) {
cards.get(i).setFitWidth(150);
cards.get(i).setFitHeight(100);
hb.getChildren().add(cards.get(i));
}
hb.setStyle("-fx-background-color:CORNFLOWERBLUE;-fx-opacity:0.8;");
root.getChildren().addAll(deal, hb);
}
});
And the last one, you don't really want to remove all root's children, what you want is to replace your cards by another 4 ones. Thus it is not necessary to remove the button, only the HBox can be manipulated as shown by the following example :
// Add the HBox as soon as the initialization
root.getChildren().addAll(hb, deal);
deal.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// root.getChildren().clear();
// Replace the previous line by the following :
hb.getChildren().clear();
ArrayList<ImageView> cards = getRandomCards(cardsFilePaths);
for(int i = 0; i < 4; i++) {
cards.get(i).setFitWidth(150);
cards.get(i).setFitHeight(100);
hb.getChildren().add(cards.get(i));
}
// The following is useless now.
// root.getChildren().addAll(hb, deal);
}
});

how to make for loop for buttons in easy way?

As it is my first application in javafx and trying to make a simple calculator. I want to know how I can make this for loop in easy way :
for(int i=1; i<10; i++){
Button bt1 = new Button();
bt1.setText("1");
bt1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("1");
}
});
i 1;
}
And how to get this buttons in VBox.
I don't know why it takes a lot of time to do that
but as jewels said it should be very quick in doing that, i'm working with javafx since 9 months at work on a computer that is not very performant and, as i understand it, in the calculator you would have 10 buttons for numbers from 0 to 9, i'd code it like this:
VBox vboxForButtons = new VBox();
for(int i=0; i<10; i++){
Button btnNumber = new Button();
btnNumber.setText(String.valueOf(i));
btnNumber.setOnAction((ActionEvent)->{
System.out.println(btnNumber.getText());
});
vboxForButtons.getChildren().add(btnNumber);
}
In alternative to this, for small and slight applications i advice you to use the SceneBuilder tool linking directly the objects that you drag in the window to an FXML event in the code.
I hope this answer will help you.
You can achieve it using the following code.
VBox vbox = new VBox(); //create new VBox instance
vbox.setMinWidth(500); //set minimum width, dont think you'd need this.
for(int i=1; i<10; i++){
Button btn = new Button();
btn.setMinWidth(200);
btn.setMinHeight(50);
btn.setText("Button" + i);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println(btn.getText()); //Or "1" as in your code
}
});
vbox.getChildren().add(btn); //add button to your VBox
}
root.getChildren().add(vbox); //root of your pane
primaryStage.setScene(scene); //primaryStage is your stage, scene is the current scene
primaryStage.show();
Edit
Reading the comments in your question about performance issue, this will not fix any of it. I have basically written the same code that you've used above, and added VBox creation and adding Button to it, because from the question it sounded like you needed help with creating VBox and adding Buttons to it.

JavaFX8 presentation view (duplicate pane and content)

I'm trying to create a presentation view for my Drawing app, where only the drawing board (pane) is visible. So the user can show the presentation view on the projector while having the actual drawing pane and tools on the PC.
My current approach is to create a Snapshot from the pane in each frame and display it in an ImageView on the second stage.
public void startStream(){
new AnimationTimer() {
#Override
public void handle(long now) {
WritableImage image = drawingPane.snapshot(new SnapshotParameters(), null);
stream.setImage(image);
}
}.start();
final Screen screen = Screen.getPrimary();
final Stage stage = new Stage();
StackPane root = new StackPane();
root.getChildren().add(stream);
Scene scene = new Scene(root, drawingPane.getWidth(), drawingPane.getHeight());
stage.setScene(scene);
stage.setTitle("streaming stage");
Rectangle2D bounds = screen.getBounds();
System.out.println(bounds);
stage.setX(bounds.getMinX() + (bounds.getWidth() - 300) / 2);
stage.setY(bounds.getMinY() + (bounds.getHeight() - 200) / 2);
stage.show();
}
problem here is it's eating a lot of RAM, like 800MB and also 30% more CPU usage. I can understand, that creating an Image on every frame is not efficient, that's why I'd like to ask, if there is a way to get this working more efficiently.
Also if there is a better approach/solution to this feature, I'd be thankful for it.
To reduce the CPU usage, do not make a snapshot in an AnimationTimer—this really makes a snapshot at the frame rate (60 FPS) frequency, even if nothing has changed in the drawingPane. Instead, make the snapshot after drawingPane's layout. Adding a listener to drawingPane's needsLayoutProperty should do the trick:
drawingPane.needsLayoutProperty().addListener((obs, wasDirty, isDirty) -> {
if(!isDirty) {
WritableImage image = drawingPane.snapshot(new SnapshotParameters(), null);
stream.setImage(image);
}
});
This should lower the CPU usage when you are not editing the drawingPane.
It may also lower the memory usage, because garbage collector may kick in faster and collect the old snapshots. If not, consider re-using the WritableImage instance, if it already has the correct size:
WritableImage image = null;
drawingPane.needsLayoutProperty().addListener((obs, wasDirty, isDirty) -> {
if(!isDirty) {
if(this.image != null &&
(this.image.getWidth() != drawingPane.getWidth() ||
this.image.getHeight() != drawingPane.getHeight())) {
this.image = null;
}
this.image = drawingPane.snapshot(new SnapshotParameters(), this.image);
stream.setImage(this.image);
}
});
Note that this answer assumes that children of drawingPane are managed children (which is the default), and thus their layout being marked as dirty causes the drawingPane's layout being marked as dirty, so that you can actually observe any changes of the needsLayout property.

JavaFX 2.X - Animated background and animated controls

A few days ago I started studying JavaFX, and came across the desire to perform 2 experiments. Firstly, I would like to know if it is possible to put an animated background behind an user interface. I've succeeded in creating an animated background, and now I'm having great difficulties to position some controls in the middle of my interface.
I'd like to introduce you 2 pictures of my program. The first demonstrates the undesirable result that I'm getting:
I believe this is my nodes tree:
This is the code of my application:
public class AnimatedBackground extends Application
{
// #########################################################################################################
// MAIN
// #########################################################################################################
public static void main(String[] args)
{
Application.launch(args);
}
// #########################################################################################################
// INSTÂNCIAS
// #########################################################################################################
private Group root;
private Group grp_hexagons;
private Rectangle rect_background;
private Scene cenario;
// UI
private VBox lay_box_controls;
private Label lab_test;
private TextArea texA_test;
private Button bot_test;
// #########################################################################################################
// INÍCIO FX
// #########################################################################################################
#Override public void start(Stage stage) throws Exception
{
this.confFX();
cenario = new Scene(this.root , 640 , 480);
this.rect_background.widthProperty().bind(this.cenario.widthProperty());
this.rect_background.heightProperty().bind(this.cenario.heightProperty());
stage.setScene(cenario);
stage.setTitle("Meu programa JavaFX - R.D.S.");
stage.show();
}
protected void confFX()
{
this.root = new Group();
this.grp_hexagons = new Group();
// Initiate the circles and all animation stuff.
for(int cont = 0 ; cont < 15 ; cont++)
{
Circle circle = new Circle();
circle.setFill(Color.WHITE);
circle.setEffect(new GaussianBlur(Math.random() * 8 + 2));
circle.setOpacity(Math.random());
circle.setRadius(20);
this.grp_hexagons.getChildren().add(circle);
double randScale = (Math.random() * 4) + 1;
KeyValue kValueX = new KeyValue(circle.scaleXProperty() , randScale);
KeyValue kValueY = new KeyValue(circle.scaleYProperty() , randScale);
KeyFrame kFrame = new KeyFrame(Duration.millis(5000 + (Math.random() * 5000)) , kValueX , kValueY);
Timeline linhaT = new Timeline();
linhaT.getKeyFrames().add(kFrame);
linhaT.setAutoReverse(true);
linhaT.setCycleCount(Animation.INDEFINITE);
linhaT.play();
}
this.rect_background = new Rectangle();
this.root.getChildren().add(this.rect_background);
this.root.getChildren().add(this.grp_hexagons);
// UI
this.lay_box_controls = new VBox();
this.lay_box_controls.setSpacing(20);
this.lay_box_controls.setAlignment(Pos.CENTER);
this.bot_test = new Button("CHANGE POSITIONS");
this.bot_test.setAlignment(Pos.CENTER);
this.bot_test.setOnAction(new EventHandler<ActionEvent>()
{
#Override public void handle(ActionEvent e)
{
for(Node hexagono : grp_hexagons.getChildren())
{
hexagono.setTranslateX(Math.random() * cenario.getWidth());
hexagono.setTranslateY(Math.random() * cenario.getHeight());
}
}
});
this.texA_test = new TextArea();
this.texA_test.setText("This is just a test.");
this.lab_test = new Label("This is just a label.");
this.lab_test.setTextFill(Color.WHITE);
this.lab_test.setFont(new Font(32));
this.lay_box_controls.getChildren().add(this.lab_test);
this.lay_box_controls.getChildren().add(this.texA_test);
this.lay_box_controls.getChildren().add(this.bot_test);
this.root.getChildren().add(this.lay_box_controls);
}
}
I've tried to make the use of a StackPane as the root of my scene graph, but also found an undesired result. Despite the controls have stayed in the center of the window, the circles begin to move in as they grow and shrink, making it appear that everything is weird.
The second thing I would like to know is if it is possible to customize the controls so they perform some animation when some event happens. Although we can change the appearance of controls using CSS, it's harder to create something complex. For example, when a control changes its appearance due to a change of state, the transition state change is not made in an animated way, but in an abrupt and static way. Is there a way to animate, for example, a button between its states? This would be done using the JavaFX API? Or would that be using CSS? Or would not be possible in any way?
Thank you for your attention.
after much struggle, I and some users of the Oracle community could resolve this issue. I see no need to repeat here all the resolution made ​​by us, so I'll post the link so you can access the solution of the problem. I hope this benefits us all. Thanks for your attention anyway.
https://community.oracle.com/thread/2620500

Resources