You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX GridPane鼠标拖拽识别混合尺寸单元格高亮功能问题求解

问题根因

你的代码核心问题出在拖拽边界的计算逻辑:你仅用了拖拽终点所在节点的起始列、起始行作为边界值,没有考虑跨列/跨行节点覆盖的完整行列范围。比如你拖拽到跨2列的C节点右半区域时,C节点的起始列是0,实际覆盖到了列1,但你代码里取到的col1还是0,计算出来的选择矩形列范围是0~0,自然不会匹配到起始列为1的B节点。

修复方案

调整边界计算逻辑,记录起始节点和当前拖拽节点的完整行列覆盖范围,再计算全局选择矩形:

  1. 鼠标按下时,同时记录起始节点的最小/最大列、最小/最大行
  2. 拖拽时,计算当前节点的最小/最大列、最小/最大行
  3. 全局选择矩形的边界取上述所有值的最小、最大值
修改后的完整可运行代码

Java代码

import java.util.*;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.Node;
import javafx.stage.Stage;
import javafx.geometry.*;

public class AddTestSSCE extends Application
{
    GridPane theGrid;
    Node startNode = null;
    // 记录起始点覆盖的行列范围
    int colStartMin, colStartMax;
    int rowStartMin, rowStartMax;

    @Override
    public void start(Stage stage) {
        theGrid = new GridPane();
        ColumnConstraints col = new ColumnConstraints(300);
        theGrid.getColumnConstraints().addAll(col, col, col);
        RowConstraints row = new RowConstraints(200);
        theGrid.getRowConstraints().addAll(row, row, row);
        addGridPane();
        theGrid.getStyleClass().add("bg-grid");
        Scene scene = new Scene(theGrid, 1024, 768);
        // 若使用单独css文件可取消下方注释
        // scene.getStylesheets().add("addtestssce.css");
        stage.setScene(scene);
        stage.show();
    }

    public void addGridPane() {
        theGrid.setHgap(10);
        theGrid.setVgap(10);
        theGrid.setPadding(new Insets(0, 10, 0, 10));

        StackPane theSP = sPAdd(new Label("A"));
        theGrid.add(theSP, 0, 0, 1, 1);

        theSP = sPAdd(new Label("B"));
        theGrid.add(theSP, 1, 0, 2, 1);

        theSP = sPAdd(new Label("C"));
        theGrid.add(theSP, 0, 1, 2, 1);

        theSP = sPAdd(new Label("D"));
        theGrid.add(theSP, 2, 1, 1, 1);

        theGrid.addEventFilter(MouseEvent.MOUSE_PRESSED,
        new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent e) {
                startNode = (Node)e.getTarget();
                // 计算起始节点的完整行列范围
                colStartMin = GridPane.getColumnIndex(startNode).intValue();
                colStartMax = colStartMin + GridPane.getColumnSpan(startNode).intValue() -1;
                rowStartMin = GridPane.getRowIndex(startNode).intValue();
                rowStartMax = rowStartMin + GridPane.getRowSpan(startNode).intValue() -1;
                doHighlighting(colStartMin, colStartMax, rowStartMin, rowStartMax);
            }
        });

        theGrid.addEventFilter(MouseEvent.MOUSE_DRAGGED,
        new EventHandler<MouseEvent>() {
            Node lastNode = null;

            @Override
            public void handle(MouseEvent e) {
                double xLoc = e.getSceneX();
                double yLoc = e.getSceneY();
                Node moveTarget = getContainingNode((int)xLoc, (int)yLoc);
                if( moveTarget != null && lastNode != moveTarget )
                {
                    // 计算当前节点的完整行列范围
                    int colCurMin = GridPane.getColumnIndex(moveTarget).intValue();
                    int colCurMax = colCurMin + GridPane.getColumnSpan(moveTarget).intValue() -1;
                    int rowCurMin = GridPane.getRowIndex(moveTarget).intValue();
                    int rowCurMax = rowCurMin + GridPane.getRowSpan(moveTarget).intValue() -1;
                    // 计算全局选择范围
                    int c0 = Math.min(colStartMin, colCurMin);
                    int c1 = Math.max(colStartMax, colCurMax);
                    int r0 = Math.min(rowStartMin, rowCurMin);
                    int r1 = Math.max(rowStartMax, rowCurMax);
                    doHighlighting(c0, c1, r0, r1);
                    lastNode = moveTarget;
                }
            }
        });
    }

    void doHighlighting(int c0, int c1, int r0, int r1)
    {
        Rectangle2D rec1 = new Rectangle2D(c0, r0, c1-c0+1, r1-r0+1);
        List<Node> nodes = theGrid.getChildren();
        for( Node node : nodes )
        {
            StackPane sp = (StackPane)node;
            unhighlight(sp);
            if( occupiesCell(sp, rec1) )
            {
                highlight(sp);
            }
        }
    }

    boolean occupiesCell(Node node, Rectangle2D r1)
    {
        int col = GridPane.getColumnIndex(node).intValue();
        int row = GridPane.getRowIndex(node).intValue();
        int wid = GridPane.getColumnSpan(node).intValue();
        int hei = GridPane.getRowSpan(node).intValue();
        Rectangle2D r2 = new Rectangle2D( col, row, wid, hei);
        return r2.intersects(r1);
    }

    void unhighlight(Node node)
    {
        if( !(node instanceof StackPane) ) return;
        StackPane label = (StackPane)node;
        List<String> cList = label.getStyleClass();
        cList.remove("b2");
        cList.add("b1");
    }

    void highlight(Node node)
    {
        if( !(node instanceof StackPane) ) return;
        StackPane label = (StackPane)node;
        List<String> cList = label.getStyleClass();
        cList.remove("b1");
        cList.add("b2");
    }

    private Node getContainingNode(int xLoc, int yLoc)
    {
        for( Node node : theGrid.getChildren() )
        {
            Bounds boundsInScene = node.localToScene(node.getBoundsInLocal());
            if( boundsInScene.contains(xLoc, yLoc) )
            {
                return node;
            }
        }
        return null;
    }

    private StackPane sPAdd(Label label)
    {
        StackPane gPPane = new StackPane();
        gPPane.getChildren().add(label);
        gPPane.getStyleClass().addAll("b1", "grid-element");
        GridPane.setFillHeight(gPPane, true);
        GridPane.setFillWidth(gPPane, true);
        return gPPane;
    }

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

配套CSS代码

.bg-grid {
    -fx-background-color: slategrey;
}
.grid-element {
    -fx-border-width: 10;
    -fx-border-color: rgb(225, 128, 217);
    -fx-background-color: rgb(247, 146, 146);
    -fx-font: 36 arial;
}

.b1 {
    -fx-text-base-color: white;
    -fx-border-color: rgb(225, 128, 217);
}
.b2 {
    -fx-text-base-color: lightgray;
    -fx-border-color: rgb(233, 228, 86);
}

修改后拖拽到C节点右半区域时,会自动将A、B、C三个节点同时高亮,符合预期。


内容的提问来源于stack exchange,提问作者didntgoboom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 20:36:04