JavaFX GridPane鼠标拖拽识别混合尺寸单元格高亮功能问题求解
问题根因
你的代码核心问题出在拖拽边界的计算逻辑:你仅用了拖拽终点所在节点的起始列、起始行作为边界值,没有考虑跨列/跨行节点覆盖的完整行列范围。比如你拖拽到跨2列的C节点右半区域时,C节点的起始列是0,实际覆盖到了列1,但你代码里取到的col1还是0,计算出来的选择矩形列范围是0~0,自然不会匹配到起始列为1的B节点。
修复方案
调整边界计算逻辑,记录起始节点和当前拖拽节点的完整行列覆盖范围,再计算全局选择矩形:
- 鼠标按下时,同时记录起始节点的最小/最大列、最小/最大行
- 拖拽时,计算当前节点的最小/最大列、最小/最大行
- 全局选择矩形的边界取上述所有值的最小、最大值
修改后的完整可运行代码
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
相关产品推荐
相关产品推荐

