我有一个4x4 gridpane对象,它由Tiles组成,扩展了ImageView,我想创建一个方法,通过鼠标拖放来改变连接的Tiles的位置.我已经知道了如何获取开始拖动的第一个元素,但我无法获取拖放的ImageView的引用.

瓷砖类

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 = 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));

我已经试过了,但我不知道如何获得连接瓷砖的参考

        gridPane.setOnMouseDragged(e->{
        System.out.println(e.getTarget());

        gridPane.setOnMouseReleased(e1->{
            System.out.println(e1.getTarget());
        });
    });

我已经创建了更改位置的代码,但我应该在鼠标释放时获得连接的互动程序的引用.

推荐答案

Oracle提供了一个关于使用drag and drop in JavaFX的优秀教程.

你可能想使用Dragboard,这是一种特殊的Clipboard.

Points to note

  1. 实际上,您可能不需要移动已创建的平铺.它们是图像视图.

  2. 您可以将与源关联的图像放置在dragboard中,并在图像被删除时更改目标视图中的图像.

  3. 可以在拖动板上设置拖动视图,以获得拖动操作的视觉反馈.

  4. 您可以为拖板使用自定义内容类型,而不是图像,这在链接的Oracle教程中进行了解释.

    private static final DataFormat customFormat =
       new DataFormat("helloworld.custom");
    

    将自定义数据放到拖板上时,请指定数据类型.请注意,数据必须是可序列化的.

    从拖板读取数据时,需要进行适当的转换.

Potential Approaches

有两种方法可以处理平铺显示.

  1. 平铺视图和模型.

创建单独的平铺视图和平铺模型界面.

当平铺更改时,不要更改视图,只更改支持视图的模型实例.视图观察其模型的变化并自动更新自身.不会创建新 node ,也不会移动现有 node .

这就是下面例子中的方法.视图是图像视图,模型是图像.

  1. 将视图和模型封装在一起.

在本例中,将在视图中放置有关模型的信息.

将 node 放置在网格中时,可以记录其网格位置,例如通过 node 中的成员值或通过在 node 上设置用户数据.

将 node 拖动到新位置时,可以查询源 node 的位置,然后使用以下命令交换网格中的源 node 和目标 node :

GridPane.setConstraints(node, columnIndex, rowIndex)

这基本上就是你在问题中提出的方法.

我不提供第二种潜在方法的实现.

Example

这些图像是网格在手动拖动平铺以重新排序之前和之后的图像.

before after

这个例子并不完全是你想要的,它只是作为一个例子提供的,如果你想利用其中的一些概念,你需要修改它.

ImageViewFactory仅用于创建测试图像,您可以忽略该部分.

拖动特定的内容属于DragUtil类.

代码使用Java18和更新的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
                )
        );
    }
}

Java相关问答推荐

为什么我的画布没有显示在PFA应用程序中?

同时运行JUnit测试和Selenium/Cucumber测试时出现问题

Java函数式编程中的双值单值映射

存根基类的受保护方法

如何创建同一类的另一个对象,该对象位于变量中?

呈现文本和四舍五入矩形时出现的JavaFX窗格白色瑕疵

Java中的死锁及其重入锁和锁

通过移动一个类解决了潜在的StubbingProblem.它怎麽工作?

使用UTC时区将startDatetime转换为本地时间

使用PDFBox从PDF中删除图像

使用Jackson库反序列化json

为什么Collectors.toList()不能保证易变性

如何在Spring Boot中创建可以将值传递给配置的&Enable&Quot;注释?

在settings.gradle.kts和Build.gradle.kts中使用公共变量

为什么StandardOpenOption.CREATE不能通过Ubuntu在中小企业上运行?

通过/失败的参数化junit测试方法执行数

在Spring Boot中使用咖啡因进行缓存-根据输出控制缓存

";home/runner/work/中没有文件...匹配到[**/pom.xml];Maven项目的构建过程中出现错误

多线程、并发和睡眠未按预期工作

如何使用stream.allMatch()为空流返回false?