Issue
I've been working on a memory card game on javaFX where you click a card and it flips and you click another and it flips if they are the same it stays if the are different they close, I followed this tutorial but I want to to change it from letters to images, the problem is when ever I click a tile an image on a different tile is shown
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.*;
public class Main extends Application {
private int num_of_pairs = 8;
private int num_per_row = 4;
Tile selected = null;
private int clickCount=2;
@Override
public void start(Stage primaryStage) throws Exception {
// Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
Pane root = new Pane();
ArrayList<Tile> tiles = new ArrayList<>();
ImageView[] puzz=new ImageView[num_of_pairs];
for(int i=0;i< puzz.length;i++){
puzz[i]= new ImageView("image.jpeg");
puzz[i].setFitHeight(110);
puzz[i].setFitWidth(110);
}
for (int i = 0; i < num_of_pairs; i++) {
tiles.add(new Tile(puzz[i]));
tiles.add(new Tile(puzz[i]));
}
Collections.shuffle(tiles);
for (int i = 0; i < tiles.size(); i++) {
Tile tile = tiles.get(i);
tile.setTranslateX(120 * (i % num_per_row));
tile.setTranslateY(120 * (i / num_per_row));
System.out.println(120*(i%num_per_row));
System.out.println(120*(i/num_per_row));
root.getChildren().add(tile);
}
primaryStage.setScene(new Scene(root, 800, 550));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
class Tile extends StackPane {
ImageView image;
Tile(ImageView image) {
this.image=image;
Rectangle border = new Rectangle(120, 120);
border.setFill(null);
border.setStroke(Color.BLACK);
getChildren().addAll(this.image,border);
setOnMouseClicked(this::handleMouseClick);
close();
}
public void handleMouseClick(MouseEvent event){
if (isOpen() || clickCount==0)
return;
clickCount--;
if (selected == null) {
selected = this;
open(() ->{});
}else{
open(() -> {
if(!hasSameValue(selected)){
selected.close();
this.close();
}
selected=null;
clickCount=2;
});
}
}
public boolean isOpen() {
return image.getOpacity() == 1;
}
public void close() {
FadeTransition ft = new FadeTransition(Duration.seconds(0.5), image);
ft.setToValue(0);
ft.play();
}
public void open(Runnable action) {
FadeTransition ft = new FadeTransition(Duration.seconds(0.5), image);
ft.setToValue(1);
ft.setOnFinished(e -> action.run());
ft.play();
}
public boolean hasSameValue(Tile other) {
return image.getImage().equals(other.image.getImage());
}
}
}
Solution
Nodes (such as ImageView
s) can only appear once in any scene graph. You are attempting to use each ImageView
twice in your root pane; essentially what happens here is the second time you add the ImageView
to the root (via the Tile
), it's removed from the first tile and placed in the second. Then if you click on the Tile
corresponding to the first time the image was added, the image appears using the coordinates from the second time it was added.
Instead, you can create an array of Image
s, and use each Image
twice in two different ImageView
s. Something like this should work:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.*;
public class Main extends Application {
private int num_of_pairs = 8;
private int num_per_row = 4;
Tile selected = null;
private int clickCount=2;
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Hello World");
Pane root = new Pane();
ArrayList<Tile> tiles = new ArrayList<>();
// Note array changed to Image:
Image[] puzz=new Image[num_of_pairs];
for(int i=0;i< puzz.length;i++){
puzz[i]= new Image("image.jpeg");
}
for (int i = 0; i < num_of_pairs; i++) {
// Create two *different* ImageViews for each Image:
ImageView first = new ImageView(puzz[i]);
first.setFitHeight(110);
first.setFitWidth(110);
ImageView second = new ImageView(puzz[i]);
second.setFitHeight(110);
second.setFitWidth(110);
tiles.add(new Tile(first));
tiles.add(new Tile(second));
}
Collections.shuffle(tiles);
for (int i = 0; i < tiles.size(); i++) {
Tile tile = tiles.get(i);
tile.setTranslateX(120 * (i % num_per_row));
tile.setTranslateY(120 * (i / num_per_row));
System.out.println(120*(i%num_per_row));
System.out.println(120*(i/num_per_row));
root.getChildren().add(tile);
}
primaryStage.setScene(new Scene(root, 800, 550));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
class Tile extends StackPane {
ImageView image;
Tile(ImageView image) {
this.image=image;
Rectangle border = new Rectangle(120, 120);
border.setFill(null);
border.setStroke(Color.BLACK);
getChildren().addAll(this.image,border);
setOnMouseClicked(this::handleMouseClick);
close();
}
public void handleMouseClick(MouseEvent event){
if (isOpen() || clickCount==0)
return;
clickCount--;
if (selected == null) {
selected = this;
open(() ->{});
}else{
open(() -> {
if(!hasSameValue(selected)){
selected.close();
this.close();
}
selected=null;
clickCount=2;
});
}
}
public boolean isOpen() {
return image.getOpacity() == 1;
}
public void close() {
FadeTransition ft = new FadeTransition(Duration.seconds(0.5), image);
ft.setToValue(0);
ft.play();
}
public void open(Runnable action) {
FadeTransition ft = new FadeTransition(Duration.seconds(0.5), image);
ft.setToValue(1);
ft.setOnFinished(e -> action.run());
ft.play();
}
public boolean hasSameValue(Tile other) {
return image.getImage().equals(other.image.getImage());
}
}
}
Answered By - James_D
Answer Checked By - Senaida (JavaFixing Volunteer)