#javafx #mouseevent #gridpane
Вопрос:
Примечание: 25 лет работы с Java, 2,5 часа (почти) работы с JavaFX.
Я хочу иметь возможность выделить все ячейки сетки, по которым перемещается мышь, т. Е. Все, что пересекается с прямоугольником, загнанным в угол точкой щелчка и текущей точкой перетаскивания. Я могу это сделать, если все дети 1х1, но со смешанными размерами у меня нет радости.
Например, если в верхней строке 1 ячейка 1 столбца (A) и 1 ячейка 2 столбца (B), а в нижней-1 ячейка 2 столбца (C) и 1 ячейка 1 столбца (D), если я нажму A и перетащу вниз в C, я смогу выделить оба. Однако я не могу понять, когда я перетаскиваю в правую половину C, чтобы B был выделен.
Доска для Образцов:
Приношу извинения за HSCE — это немного длинновато, но я чувствовал, что его удаление снизит читабельность.
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
{
ArrayList<StackPane> sPanes = new ArrayList<StackPane>(); // selected panes
GridPane theGrid;
Node startNode = null;
int col0, col1;
int row0, row1;
@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);
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, //Creating the mouse event handler
new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
System.out.println("We're Moving!!");
startNode = (Node)e.getTarget();
sPanes.add((StackPane)startNode);
col0 = GridPane.getColumnIndex(startNode).intValue();
row0 = GridPane.getRowIndex(startNode).intValue();
System.out.printf("Starting at %d %dn", col0, row0);
}
});
theGrid.addEventFilter(MouseEvent.MOUSE_DRAGGED, //Creating the mouse event handler
new EventHandler<MouseEvent>() {
Node lastNode = null;
@Override
public void handle(MouseEvent e) {
Node target = (Node)e.getTarget();
double xLoc = e.getX();
double yLoc = e.getY();
Bounds bs = target.localToScene(target.getBoundsInLocal());
Node moveTarget;
if( bs.contains(xLoc, yLoc) )
{
moveTarget = target;
}
else
{
moveTarget = getContainingNode((int)xLoc, (int)yLoc);
}
if( moveTarget != null amp;amp; lastNode != moveTarget )
{
col1 = GridPane.getColumnIndex(moveTarget).intValue();
row1 = GridPane.getRowIndex(moveTarget).intValue();
doHighlighting();
lastNode = moveTarget;
}
}
});
}
void doHighlighting()
{
int c0, c1, r0, r1;
c0 = col0 > col1 ? col1 : col0;
c1 = !(col0 > col1) ? col1 : col0;
r0 = row0 > row1 ? row1 : row0;
r1 = !(row0 > row1) ? row1 : row0;
Rectangle2D rec1 = new Rectangle2D(c0, r0, c1-c0 1, r1-r0 1);
System.out.printf("Box: %d %d %d %dn", c0, c1, r0, r1);
List<Node> nodes = theGrid.getChildren();
for( Node node : nodes )
{
StackPane sp = (StackPane)node;
unhighlight(sp);
int col = GridPane.getColumnIndex(sp).intValue();
int row = GridPane.getRowIndex(sp).intValue();
if( occupiesCell(sp, rec1) )
{
highlight(sp);
}
}
}
boolean occupiesCell(Node node, Rectangle2D r1)
{
boolean result = false;
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)
{
Node tgt = null;
for( Node node : theGrid.getChildren() )
{
Bounds boundsInScene = node.localToScene(node.getBoundsInLocal());
if( boundsInScene.contains(xLoc, yLoc) )
{
return node;
}
}
return t>
}
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();
}
}
.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);
}
Комментарии:
1. Не проще ли зарегистрировать обработчики мыши в каждой из ячеек
StackPane
вместо регистрации обработчика в сетке? И даже с вашей стратегией, действительно ли вам вообще нужно пачкать руки координатами? Разве цель, на которую вы получаете ссылку, не является просто узлом, который вы хотите выделить/осветить в любом случае?2. Я попробовал несколько обработчиков, но не регистрировался при перетаскивании из одной ячейки в другую. Однако проблема с определением местоположения столбца в ячейках с несколькими столбцами остается.
3. Для нескольких обработчиков, я думаю, вам придется позвонить
startFullDrag()
в какой-то момент. (В данный момент я не могу легко получить доступ к документам Java.) И теперь я понимаю, в чем проблема. Так что, возможно, это в основном правильный подход, но просто вычислите координаты прямоугольника, нарисованного мышью относительно сетки (как вы уже делаете), создайтеBoundingBox
представление этого прямоугольника, затем повторите все дочерние элементы сетки и проверьтеboundsInParent()
, пересекается ли их ограничивающая рамка.4. Вам нужно только местоположение пикселя для создания
BoundingBox
. У него естьboolean intersects(Bounds)
метод, который вы можете вызвать, передавая каждому дочернему узлуboundInParent()
5. @didntgoboom Да, вы можете ответить на свой собственный вопрос (и принять его как рабочий ответ через некоторое время).
Ответ №1:
После небольшого общения с @james_d и @slaw, наконец, пришло рабочее решение.
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.*;
import javafx.css.PseudoClass;
public class AddTestSSCE extends Application
{
private static final PseudoClass highlight = PseudoClass.getPseudoClass("highlight");
ArrayList<StackPane> sPanes = new ArrayList<StackPane>(); // selected panes
GridPane theGrid;
Node startNode = null;
int x0, y0, x1, y1;
@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);
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, //Creating the mouse event handler
new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
System.out.println("We're Moving!!");
startNode = (Node)e.getTarget();
sPanes.add((StackPane)startNode);
x0 = x1 = (int)e.getX();
y0 = y1 = (int)e.getY();
doHighlighting();
System.out.printf("Starting at %d %dn", x0, y0);
}
});
theGrid.addEventFilter(MouseEvent.MOUSE_DRAGGED, //Creating the mouse event handler
new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
Node target = (Node)e.getTarget();
x1 = (int)e.getX();
y1 = (int)e.getY();
Bounds bs = target.localToScene(target.getBoundsInLocal());
Node moveTarget;
if( bs.contains(x1, y1) )
{
moveTarget = target;
}
else
{
moveTarget = getContainingNode( x1, y1);
}
if( moveTarget != null )
{
doHighlighting();
}
}
});
}
void doHighlighting()
{
int c0, c1, r0, r1;
c0 = x0 > x1 ? x1 : x0;
c1 = !(x0 > x1) ? x1 : x0;
r0 = y0 > y1 ? y1 : y0;
r1 = !(y0 > y1) ? y1 : y0;
Bounds dragged = new BoundingBox(c0, r0, c1-c0 1, r1-r0 1);
for (Node child : theGrid.getChildren())
{
child.pseudoClassStateChanged(highlight, dragged.intersects(child.getBoundsInParent()));
}
}
private Node getContainingNode(int xLoc, int yLoc)
{
Node tgt = null;
for( Node node : theGrid.getChildren() )
{
Bounds boundsInScene = node.localToScene(node.getBoundsInLocal());
if( boundsInScene.contains(xLoc, yLoc) )
{
return node;
}
}
return t>
}
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;
}
.grid-element:highlight {
-fx-text-base-color: lightgray;
-fx-border-color: rgb(233, 228, 86);
}
Комментарии:
1. Вам все еще нужно
startNode
,target
,getContainingNode()
, и т. Д.? Сейчас это просто выглядит излишним.