如何更改 GridPane 中节点的位置
How to change positions of node in a GridPane
我有一个由 Tiles 组成的 4x4 gridpane 对象,它扩展了 ImageView,我想创建一种方法来通过鼠标拖放来更改连接的 Tiles 的位置。我已经想出如何获取拖动开始的第一个元素,但我无法获得拖放中的 ImageView 的引用。
平铺Class
public class Tile extends ImageView {
protected int xCoordinate;
protected int yCoordinate;
protected boolean isMoveable;
protected ArrayList<Point2D> points = new ArrayList<>();
public Tile(int xCoordinate, int yCoordinate) {
this.xCoordinate = xCoordinate;
this.yCoordinate = yCoordinate;
super.setFitHeight(125);
super.setFitWidth(125);
}}
GridPane 代码
GridPane gridPane = new GridPane();
for (Tile tile : tiles){
gridPane.add(tile,tile.getXCoordinate(),tile.getYCoordinate());
}
StackPane centerPane = new StackPane();
centerPane.setStyle("-fx-background-color: white;");
centerPane.getChildren().add(gridPane);
centerPane.setPadding(new Insets(0,50,0,50));
我已经试过了,但我不知道如何获取已连接 Tile 的参考
gridPane.setOnMouseDragged(e->{
System.out.println(e.getTarget());
gridPane.setOnMouseReleased(e1->{
System.out.println(e1.getTarget());
});
});
我已经创建了更改位置的代码,但我应该在松开鼠标时获取连接的 Tile 的参考。
Oracle 提供了关于使用 drag and drop in JavaFX 的优秀教程。
您可能想使用 Dragboard, which is a special kind of Clipboard。
注意事项
您实际上可能不需要移动您创建的图块。它们是 ImageView。
您可以将与源关联的图像放在拖板中,并在拖放时更改目标视图中的图像。
可以在拖板上设置dragView,用于拖拽操作的视觉反馈
您可以为拖板使用自定义内容类型而不是图像,链接的 Oracle 教程中对此进行了解释。
private static final DataFormat customFormat =
new DataFormat("helloworld.custom");
When putting a custom data onto a dragboard, specify the data type. Note that the data must be serializable.
When reading the data from the dragboard, a proper casting is needed.
潜在方法
您可以通过两种方式处理磁贴显示。
- 平铺视图和模型。
创建单独的磁贴视图和磁贴模型界面。
当图块改变时,不改变视图,只改变支持视图的模型实例。视图观察其模型的变化并自动更新自身。不创建新节点并且不移动现有节点。
这就是下面示例中的方法。视图是一个ImageView,模型是一个Image。
- 将视图和模型封装在一起。
在这种情况下,您将有关模型的信息放在视图中。
当一个节点被放置在网格中时,您记录它的网格位置,例如通过节点中的成员值或通过在节点上设置用户数据。
当一个节点被拖到一个新的位置时,您查询源节点的位置,然后使用以下方法交换网格中的源节点和目标节点:
GridPane.setConstraints(node, columnIndex, rowIndex)
这基本上就是您在问题中提出的方法。
我不提供第二种可能方法的实现。
例子
这些图像是手动拖动图块重新排序前后的网格图像。
该示例不会完全符合您的要求,它纯粹是作为示例提供的,如果您希望使用其中的某些概念,则需要对其进行调整。
ImageViewFactory只是用来创建测试图片,你可以忽略这部分。
拖动特定内容在 DragUtil class。
代码使用 Java 18 和更新的 Java 语言功能,因此要编译它,您需要启用适当的语言级别。
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Label;
import javafx.scene.effect.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class TileDragApp extends Application {
private static final String TEXT = "abcdefghijklmnop";
private static final int GRID_SIZE = 4;
private static final int TILE_SIZE = 60;
@Override
public void start(Stage stage) {
ImageViewFactory imageViewFactory = new ImageViewFactory();
ImageView[] imageViews = imageViewFactory.makeImageViews(
TEXT,
TILE_SIZE
);
DragUtil dragUtil = new DragUtil();
GridPane grid = new GridPane();
for (int i = 0; i < TEXT.length(); i++) {
dragUtil.makeDraggable(imageViews[i]);
grid.add(imageViews[i], i % GRID_SIZE, i / GRID_SIZE);
}
grid.setGridLinesVisible(true);
grid.setPadding(new Insets(20));
stage.setScene(new Scene(grid));
stage.setResizable(false);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
class ImageViewFactory {
private static final String CSS =
"""
data:text/css,
""" +
// language=CSS
"""
.root {
-fx-background-color: azure;
}
.label {
-fx-font-size: 40px;
-fx-text-fill: navy;
}
""";
public ImageView[] makeImageViews(String text, int tileSize) {
List<Character> chars =
text.chars()
.mapToObj(
c -> (char) c
).collect(
Collectors.toList()
);
Collections.shuffle(chars);
return chars.stream()
.map(
c -> makeImageView(c, tileSize)
).toArray(
ImageView[]::new
);
}
private ImageView makeImageView(char c, int tileSize) {
Label label = new Label(Character.toString(c));
StackPane layout = new StackPane(label);
layout.setPrefSize(tileSize, tileSize);
Scene scene = new Scene(layout);
scene.getStylesheets().add(CSS);
SnapshotParameters snapshotParameters = new SnapshotParameters();
snapshotParameters.setFill(Color.AZURE);
Image image = layout.snapshot(snapshotParameters,null);
return new ImageView(image);
}
}
class DragUtil {
public void makeDraggable(ImageView imageView) {
Effect highlight = createHighlightEffect(imageView);
imageView.setOnDragDetected(e -> {
Dragboard db = imageView.startDragAndDrop(TransferMode.MOVE);
ClipboardContent content = new ClipboardContent();
content.putImage(imageView.getImage());
db.setContent(content);
db.setDragView(makeSmaller(imageView.getImage()));
e.consume();
});
imageView.setOnDragOver(e -> {
if (e.getGestureSource() != imageView
&& e.getDragboard().hasImage()
) {
e.acceptTransferModes(TransferMode.MOVE);
}
imageView.setEffect(highlight);
e.consume();
});
imageView.setOnDragExited(e -> {
imageView.setEffect(null);
e.consume();
});
imageView.setOnDragDropped(e -> {
Dragboard db = e.getDragboard();
boolean success = false;
if (db.hasImage() && e.getGestureSource() instanceof ImageView source) {
source.setImage(imageView.getImage());
imageView.setImage(db.getImage());
success = true;
}
e.setDropCompleted(success);
e.consume();
});
}
private Image makeSmaller(Image image) {
ImageView resizeView = new ImageView(image);
resizeView.setFitHeight(image.getHeight() * 3 / 4);
resizeView.setFitWidth(image.getWidth() * 3 / 4);
SnapshotParameters snapshotParameters = new SnapshotParameters();
return resizeView.snapshot(snapshotParameters, null);
}
private Effect createHighlightEffect(Node n) {
ColorAdjust monochrome = new ColorAdjust();
monochrome.setSaturation(-1.0);
return new Blend(
BlendMode.MULTIPLY,
monochrome,
new ColorInput(
0,
0,
n.getLayoutBounds().getWidth(),
n.getLayoutBounds().getHeight(),
Color.PALEGREEN
)
);
}
}
我有一个由 Tiles 组成的 4x4 gridpane 对象,它扩展了 ImageView,我想创建一种方法来通过鼠标拖放来更改连接的 Tiles 的位置。我已经想出如何获取拖动开始的第一个元素,但我无法获得拖放中的 ImageView 的引用。
平铺Class
public class Tile extends ImageView {
protected int xCoordinate;
protected int yCoordinate;
protected boolean isMoveable;
protected ArrayList<Point2D> points = new ArrayList<>();
public Tile(int xCoordinate, int yCoordinate) {
this.xCoordinate = xCoordinate;
this.yCoordinate = yCoordinate;
super.setFitHeight(125);
super.setFitWidth(125);
}}
GridPane 代码
GridPane gridPane = new GridPane();
for (Tile tile : tiles){
gridPane.add(tile,tile.getXCoordinate(),tile.getYCoordinate());
}
StackPane centerPane = new StackPane();
centerPane.setStyle("-fx-background-color: white;");
centerPane.getChildren().add(gridPane);
centerPane.setPadding(new Insets(0,50,0,50));
我已经试过了,但我不知道如何获取已连接 Tile 的参考
gridPane.setOnMouseDragged(e->{
System.out.println(e.getTarget());
gridPane.setOnMouseReleased(e1->{
System.out.println(e1.getTarget());
});
});
我已经创建了更改位置的代码,但我应该在松开鼠标时获取连接的 Tile 的参考。
Oracle 提供了关于使用 drag and drop in JavaFX 的优秀教程。
您可能想使用 Dragboard, which is a special kind of Clipboard。
注意事项
您实际上可能不需要移动您创建的图块。它们是 ImageView。
您可以将与源关联的图像放在拖板中,并在拖放时更改目标视图中的图像。
可以在拖板上设置dragView,用于拖拽操作的视觉反馈
您可以为拖板使用自定义内容类型而不是图像,链接的 Oracle 教程中对此进行了解释。
private static final DataFormat customFormat = new DataFormat("helloworld.custom");
When putting a custom data onto a dragboard, specify the data type. Note that the data must be serializable.
When reading the data from the dragboard, a proper casting is needed.
潜在方法
您可以通过两种方式处理磁贴显示。
- 平铺视图和模型。
创建单独的磁贴视图和磁贴模型界面。
当图块改变时,不改变视图,只改变支持视图的模型实例。视图观察其模型的变化并自动更新自身。不创建新节点并且不移动现有节点。
这就是下面示例中的方法。视图是一个ImageView,模型是一个Image。
- 将视图和模型封装在一起。
在这种情况下,您将有关模型的信息放在视图中。
当一个节点被放置在网格中时,您记录它的网格位置,例如通过节点中的成员值或通过在节点上设置用户数据。
当一个节点被拖到一个新的位置时,您查询源节点的位置,然后使用以下方法交换网格中的源节点和目标节点:
GridPane.setConstraints(node, columnIndex, rowIndex)
这基本上就是您在问题中提出的方法。
我不提供第二种可能方法的实现。
例子
这些图像是手动拖动图块重新排序前后的网格图像。
该示例不会完全符合您的要求,它纯粹是作为示例提供的,如果您希望使用其中的某些概念,则需要对其进行调整。
ImageViewFactory只是用来创建测试图片,你可以忽略这部分。
拖动特定内容在 DragUtil class。
代码使用 Java 18 和更新的 Java 语言功能,因此要编译它,您需要启用适当的语言级别。
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Label;
import javafx.scene.effect.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class TileDragApp extends Application {
private static final String TEXT = "abcdefghijklmnop";
private static final int GRID_SIZE = 4;
private static final int TILE_SIZE = 60;
@Override
public void start(Stage stage) {
ImageViewFactory imageViewFactory = new ImageViewFactory();
ImageView[] imageViews = imageViewFactory.makeImageViews(
TEXT,
TILE_SIZE
);
DragUtil dragUtil = new DragUtil();
GridPane grid = new GridPane();
for (int i = 0; i < TEXT.length(); i++) {
dragUtil.makeDraggable(imageViews[i]);
grid.add(imageViews[i], i % GRID_SIZE, i / GRID_SIZE);
}
grid.setGridLinesVisible(true);
grid.setPadding(new Insets(20));
stage.setScene(new Scene(grid));
stage.setResizable(false);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
class ImageViewFactory {
private static final String CSS =
"""
data:text/css,
""" +
// language=CSS
"""
.root {
-fx-background-color: azure;
}
.label {
-fx-font-size: 40px;
-fx-text-fill: navy;
}
""";
public ImageView[] makeImageViews(String text, int tileSize) {
List<Character> chars =
text.chars()
.mapToObj(
c -> (char) c
).collect(
Collectors.toList()
);
Collections.shuffle(chars);
return chars.stream()
.map(
c -> makeImageView(c, tileSize)
).toArray(
ImageView[]::new
);
}
private ImageView makeImageView(char c, int tileSize) {
Label label = new Label(Character.toString(c));
StackPane layout = new StackPane(label);
layout.setPrefSize(tileSize, tileSize);
Scene scene = new Scene(layout);
scene.getStylesheets().add(CSS);
SnapshotParameters snapshotParameters = new SnapshotParameters();
snapshotParameters.setFill(Color.AZURE);
Image image = layout.snapshot(snapshotParameters,null);
return new ImageView(image);
}
}
class DragUtil {
public void makeDraggable(ImageView imageView) {
Effect highlight = createHighlightEffect(imageView);
imageView.setOnDragDetected(e -> {
Dragboard db = imageView.startDragAndDrop(TransferMode.MOVE);
ClipboardContent content = new ClipboardContent();
content.putImage(imageView.getImage());
db.setContent(content);
db.setDragView(makeSmaller(imageView.getImage()));
e.consume();
});
imageView.setOnDragOver(e -> {
if (e.getGestureSource() != imageView
&& e.getDragboard().hasImage()
) {
e.acceptTransferModes(TransferMode.MOVE);
}
imageView.setEffect(highlight);
e.consume();
});
imageView.setOnDragExited(e -> {
imageView.setEffect(null);
e.consume();
});
imageView.setOnDragDropped(e -> {
Dragboard db = e.getDragboard();
boolean success = false;
if (db.hasImage() && e.getGestureSource() instanceof ImageView source) {
source.setImage(imageView.getImage());
imageView.setImage(db.getImage());
success = true;
}
e.setDropCompleted(success);
e.consume();
});
}
private Image makeSmaller(Image image) {
ImageView resizeView = new ImageView(image);
resizeView.setFitHeight(image.getHeight() * 3 / 4);
resizeView.setFitWidth(image.getWidth() * 3 / 4);
SnapshotParameters snapshotParameters = new SnapshotParameters();
return resizeView.snapshot(snapshotParameters, null);
}
private Effect createHighlightEffect(Node n) {
ColorAdjust monochrome = new ColorAdjust();
monochrome.setSaturation(-1.0);
return new Blend(
BlendMode.MULTIPLY,
monochrome,
new ColorInput(
0,
0,
n.getLayoutBounds().getWidth(),
n.getLayoutBounds().getHeight(),
Color.PALEGREEN
)
);
}
}