Joel Perez
Joel Perez

Reputation: 45

Events in Java (Listener)

It's not the first time I ask this, but now I have quite better knowledge of the problem. Ok, so what I want to do is a board game. It has a stack of tiles, and for each turn a player takes one tile from the stack and adds it to the board, then it's the turn of the following player and so on.

Tiles are added using the mouse and are shown in a gridpane, which is inside a scrollpane which is inside a borderpane (because we will have more stuff in the laterals and maybe in the top).

Let's check the code:

public final class GUI extends Application {

    private Game _game;
    private GridPane _visualBoard;
    private Stage _primaryStage;

    @Override
    public void start(final Stage primaryStage) {
    //stuff
    play();
    primaryStage.show();
}


public static void main(String[] args) {
      Application.launch(args);
}

public void addTile(myTile r) {
      double x, y;
      myVisualTile vT = new myVisualTile(r)
      _visualBoard.setOnMousePressed(new EventHandler<MouseEvent>() {
           @Override
           public void handle(MouseEvent e) {
               double mouseX = e.getSceneX();
               double mouseY = e.getSceneY();
              _visualBoard.add(vT, x, y);
           }
      });
}

public void play() {
    Player j;
    int i = 0;
    while (!_tileStack.empty()) {
        if (i == _players.size()) i = 0;
        j = _players.get(i);
        i++;
        turn(j);
    }
    System.out.println("Final");
}

private void turn(Player j) {
    myTile r=_tileStack.poll();
    addTile(r);
}

But this actually doesnt work as it should. Why? Because SOMEHOW I have to "listen" to the event addTile() to happen, so I've read documentation about this but I don't get a clearly answer.

By the way, let me point out that I know that tiles won't be added at the correct gridpane row, column as I don't know how to convert the pixels to a correct gridpane position, but that's not the main question.

Should I use an EventListener or an EventFilter? What's the difference between them? How to use them?

Upvotes: 0

Views: 201

Answers (1)

Slaw
Slaw

Reputation: 45736

First, I suggest reading this tutorial to learn more about event processing in JavaFX.


From this question and your other two, the conceptual problem you seem to be having is how to have "a loop without a loop".

The idea is, you'd have a class that represents the state of the game. The "state" includes things like whose turn it is, what tiles have been placed, what tiles are still available, what moves are valid, how many points does each person have, has the game been won, and so on.

public class Game {

    private final Deque<Tile> tileStack = ...;
    private final Tile[][] board = ...; // where "null" would be open (might want a better data structure)
    private final List<Player> players = ...;
    private final Map<Player, Integer> points = ...;
    private int currentPlayerIndex;

    private Tile currentTile;

    // getters/setters (if necessary)...
}

When using a design pattern such as MVVM, I believe the above would be the ViewModel. And as you're using JavaFX, you might want to expose those properties as actual JavaFX [ReadOnly]Property instances. That way you can use data binding between the View and the ViewModel. Things like Player and Tile would be Model objects.

Then you'd add methods to this class to manipulate the state of the game.

public void tryPlaceTile(int row, int column) {
    if (isValidMove(row, column) {
        addPlacedTile(row, column); // might update points
        if (tileStack.isEmpty()) {
            currentTile = null;
            endGame(); // stops the game, determines winner
        } else {
            currentTile = tileStack.pop();
            currentPlayerIndex = currentPlayerIndex == players.size() - 1 ? 0 : currentPlayerIndex + 1;
            updateView(); // likely not needed with data binding
        }
    } else {
        notifyUserOfInvalidMove();
    }

    // let method simply return, it will be called again when
    // the (next) player clicks in the appropriate place of
    // the view
}

Now, the above two code snippets are extremely crude (especially since I'm not very familiar with the game you're coding). Things like updateView() would not necessarily be needed if using data binding. The isValidMove(int,int) and notifyUserOfInvalidMove() wouldn't necessarily be needed either, not if the view (based on the state of the game) doesn't allow interactions with invalid locations. But the point is, when the user clicks on a location, you call this method. This method handles the player's move and updates the state of the game. The state change should affect the view so that the visuals accurately display the state1. Once the method returns, you are back to "waiting" for the next user interaction.

node.setOnMouseClicked(event -> {
    gameInstance.tryPlaceTile(/* needed information */);
    // anything else that needs doing
});

1. If you end up using background threads, remember to only ever update the view (directly or indirectly) on the JavaFX Application Thread.


I also recommend reading articles and tutorials about:

  • Model-View-Controller (MVC)
  • Model-View-Presenter (MVP)
  • Model-View-ViewModel (MVVM)

And variations thereof. These architectures often (not always) make it easier to implement software such as GUI applications. Determine which one best fits your needs (if any) and use it.

Upvotes: 2

Related Questions