РЕДАКТИРОВАТЬ: Проблемы возникают из-за использования SplitPane. Я удалил это, но теперь у меня возникают проблемы с MouseTransparent, см. комментарии

Я столкнулся с проблемой в StackPane, где класс Controller должен управлять переводами узлов с помощью перетаскивания мышью:

По какой-то причине событие .setOnMouseEntered правильно срабатывает всякий раз, когда мышь входит в StackPane. Однако setOnMousePressed (при нажатии на узел в StackPane) вообще не срабатывает, за исключением метки в центре StackPane. Когда я удаляю метку, ничего не срабатывает. Сам StackPane состоит из SplitPane, состоящего из нескольких других узлов.

Некоторые из этих узлов (те, которые предполагается перетаскивать) также имеют некоторое управление событиями setOnMousePressed и т. д., которое срабатывает правильно!

Чтобы лучше понять контекст: я работаю над реализацией игры «Морской бой». В настоящее время я пытаюсь управлять размещением лодки с помощью класса контроллера (чтобы я мог убедиться, что лодка не выходит за пределы или на другую лодку). Класс контроллера (BoatMoveManager) при создании экземпляра создает все эти обработчики событий mouseEvent на самой высокой панели StackPane (GameBoard расширяет StackPane).

public class BoatMoveManager {
    GameBoard gB;

    public BoatMoveManager(GameBoard g){
        gB = g;

        //the following mouseEventHandler fires correctly whenever the mouse enters the GameBoard (StackPane)
        gB.setOnMouseEntered(event -> {
            System.out.println("GameBoard OnMouseEntered");

        //the following mouseEventHandler doesn't fire, except on a label in the middle of the GameBoard
        gB.setOnMousePressed(event -> {
            System.out.println("GameBoard OnMousePressed");
        });

Я не понимаю, почему событие срабатывает с меткой, а не с остальными узлами в GameBoard... (Может быть дело в том, что метка находится непосредственно "внутри" GameBoard, а другие узлы находятся в верхняя часть горизонтального сплиттера?)

Например, вот конструктор для GameBoard:

public class GameBoard extends StackPane {
    private SplitPane splitter;
    private TilePane gameZone;
    private TabZone tabs;
    private Grid playerGrid, opponentGrid, playerHarbor, opponentHarbor;
    private FlowPane playerGrids, opponentGrids, playerZone, opponentZone;
    private Score playerScore, opponentScore;
    private Label popup;

    private static HBox playerHBox, opponentHBox;
    private static VBox playerVBox, opponentVBox;

    private static BoatMoveManager bMM;

    private List<Boat> playerBoatsList = new ArrayList<Boat>(), opponentBoatsList = new ArrayList<Boat>() ;

    public GameBoard(){
        super();

        playerGrid = new Grid('G','H');
        opponentGrid = new Grid('G','H');
        playerHarbor = new Grid('H','H');
        opponentHarbor = new Grid('H','H');

        bMM = new BoatMoveManager(this);

        for(int i = 1 ; i < 5 ; ++i){
            playerBoatsList.add(new Boat(i, playerGrid, playerHarbor));
            opponentBoatsList.add(new Boat(i, opponentHarbor, opponentGrid));
        }

        playerGrid.drawBoat(playerBoatsList.get(0), 3, 4);
        playerGrid.drawBoat(playerBoatsList.get(1), 3, 5);
        playerGrid.drawBoat(playerBoatsList.get(2), 3, 6);
        playerGrid.drawBoat(playerBoatsList.get(3), 3, 7);

        playerScore = new Score();
        opponentScore = new Score();

        gameZone = new TilePane(Orientation.HORIZONTAL);
        gameZone.setStyle("-fx-background-color: lightgray");

        playerHBox = new HBox();
        playerHBox.getChildren().addAll(playerHarbor, playerGrid);

        playerHBox.setSpacing(Grid.getUnit());

        playerVBox = new VBox();
        playerVBox.getChildren().addAll(playerScore,playerHBox);
        playerVBox.maxWidthProperty().bind(gameZone.widthProperty().divide(2));
        playerScore.setAlignment(Pos.CENTER);

        opponentHBox = new HBox();
        opponentHBox.getChildren().addAll(opponentGrid, opponentHarbor);
        opponentHBox.setSpacing(Grid.getUnit());

        opponentVBox = new VBox();
        opponentVBox.getChildren().addAll(opponentScore,opponentHBox);
        opponentScore.setAlignment(Pos.CENTER);

        gameZone.getChildren().addAll(playerVBox, opponentVBox);
        gameZone.setHgap(Grid.getUnit()*3);
        gameZone.setAlignment(Pos.CENTER);

        gameZone.setSnapToPixel(false);
        tabs = new TabZone();

        splitter = new SplitPane();
        splitter.setOrientation(Orientation.VERTICAL);
        splitter.setDividerPositions(0.75f,0.25f);
        splitter.getItems().addAll(gameZone,tabs);

        splitter.setOnDragEntered(event ->{
            BoatMoveManager.setTarget(splitter);
        });

        popup = new Label("uninitialized");

        this.getChildren().addAll(splitter,popup);

        this.setOnDragEntered(event -> {
            BoatMoveManager.setTarget(this);
        });
    }

Метка, о которой я говорю, — «всплывающее окно», которая добавляется в самый конец конструктора вместе с «разделителем» в

this.getChildren().addAll(splitter,popup);

Но примите во внимание тот факт, что даже когда я его не добавляю, такие события, как MousePressed, не срабатывают через GameBoard...

Вот макет GameBoard, чтобы прояснить ситуацию (расположение сеток и лодок говорит само за себя): Макет GameBoard (Откройте это изображение в новой вкладке, чтобы просмотреть его в полном размере) Я надеюсь, что это не слишком загромождено, чтобы понять, что я пытаюсь сделать.. И почему это не работает.

0
Skwiggs 4 Дек 2014 в 02:16
Хорошо, я провел некоторое исследование, и проблема, похоже, возникает из-за SplitPane. Когда я добавляю gameZone непосредственно в GameBoard StackPane, события запускаются правильно! странный
 – 
Skwiggs
4 Дек 2014 в 19:09
Хорошо, сейчас я нахожусь в той точке, когда могу заставить события MouseEvents срабатывать правильно. ОДНАКО, когда я запускаю жест нажатия-перетаскивания-освобождения, даже если я установил для исходного узла значение mouseTransparent(true), я не могу вообще запустить дальнейшие события dragEntered на других узлах... Я следовал официальному JavaFx -8 Документ, в котором сказано установить mouseTransparent(true) при нажатии мыши и установить mouseTransparent(false) при отпускании мыши. Событие DragDetected срабатывает правильно, но не более того. Я теряю здесь свое дерьмо :(
 – 
Skwiggs
8 Дек 2014 в 23:32

2 ответа

Я действительно не уверен на данный момент. Мне пришлось бы протестировать код на своем компьютере, чтобы знать наверняка, но попробуйте

this.setMouseTransparency(true);

Это приведет к тому, что на саму панель стека нельзя будет щелкнуть. Также для дальнейшего использования вы можете опустить «это» в начале ваших вызовов GameBoard. Совершенно законно вызывать getChildren().add(node), если ваш класс расширяет StackPane.

0
Philip Vaughn 4 Дек 2014 в 03:15
1
this.setMouseTransparent(true) также делает все узлы внутри GameBoard StackPane прозрачными для мыши...
 – 
Skwiggs
4 Дек 2014 в 04:17
Вы выложили весь код? Я имею в виду, могу ли я запустить это на своем компьютере?
 – 
Philip Vaughn
5 Дек 2014 в 04:40
Это не сработает, если вы попытаетесь, но я обнаружил, что события снова запускаются, когда я удаляю SplitPane из уравнения. странный
 – 
Skwiggs
5 Дек 2014 в 13:41

Столкнувшись с той же проблемой (снова)..

Я считаю, что StackPane - это ваша проблема. Они не любят проходить события явно.. В моем случае установка: stackPane.setPickOnBounds(false);

Было моим решением.

Я надеюсь, что это решит проблему для вас ... возможно, ошибка?

0
jdub1581 19 Дек 2014 в 22:44
Тогда мое следующее предложение - посмотреть порядок дочерних элементов, размер узла-контейнера может быть масштабирован в соответствии со сценой (например, в fxml), если не указано иное, что может блокировать события мыши, проходящие к узлу, как кнопка позади него. и, увидев комментарии выше, держу пари, что это корневой контейнер в SplitPane, вы пытались установить свой контейнер в качестве корня?
 – 
jdub1581
6 Дек 2014 в 18:24
Root.setPrefSize(*Pane.USE_COMPUTED_SIZE, *Pane.USE_COMPUTED_SIZE);
 – 
jdub1581
6 Дек 2014 в 18:31
Я не понимаю, что вы имеете в виду. Что вы определяете как «корень»? Разделенная панель состоит из двух элементов; gameZone (который содержит множество других подузлов) и вкладки (который является просто расширенным классом TabPane). Я попытался заменить SplitPane на VBox, после чего события MouseEvents сработали правильно. Но теперь я сижу с моим дисплеем, идеально разделенным на две половины экрана, вместо перетаскиваемого разделителя... :@
 – 
Skwiggs
8 Дек 2014 в 14:21
Я обычно использовал «root», имея в виду узлы-контейнеры. например, если вы установите Stackpane в качестве корневого узла сцены, если вы специально не установите его размер, он будет масштабироваться, чтобы соответствовать всей сцене.
 – 
jdub1581
9 Дек 2014 в 17:15
Дело в том, что наши учителя специально просили нас не использовать FXML... Но в любом случае, я действительно использую StackPane в качестве корня для сцены. В нем находится SplitPane, несколькими слоями ниже.
 – 
Skwiggs
9 Дек 2014 в 21:10