JavaFX(部分)在gridpane外部隐藏图像

2020年4月16日 88点热度 0条评论

基本上,我有这样的布局:Image of the GUI layout
它是一个BorderPane(黑色),带有一个菜单栏(红色),一个其中包含一些东西的容器,在左侧边栏(绿色)和一个嵌套在中心(橙色)内的GridPane(灰色)。

GridPane的单元格充满了可以消失的ImageViews(蓝色),当重新填充时,它们可能堆叠在一起,通过平行过渡从顶部掉入其位置。到目前为止,一切都很好,但是我想隐藏GridPane和菜单栏之间的图像部分,但无法使其正常工作。有人知道如何解决这个问题吗?我也不会介意只在隐藏一部分时隐藏整个图片。

//编辑:


主布局是使用fxml文件创建的。启动后,图像视图已添加到网格窗格中。

解决方案如下:

如果您检查GridPane的JavaDoc,您将看到:

GridPane默认情况下不会裁剪其内容,因此,如果孩子的最小尺寸使其无法适应其空间,则孩子的边界可能会超出其自身的边界。

对于Pane类的每个孩子也是如此。因此,您将需要手动处理裁剪,为此需要使用setClip()方法。
一种简单的实现方法是,使用带有窗格尺寸的Rectangle进行裁剪。下面有一个代码完全可以做到这一点:

public void clipChildren(Region region) {
    final Rectangle clipPane = new Rectangle();
    region.setClip(clipPane);

    region.layoutBoundsProperty().addListener((ov, oldValue, newValue) -> {
        clipPane.setWidth(newValue.getWidth());
        clipPane.setHeight(newValue.getHeight());
    });
}

老实说,我也没有意识到这一点。您可以阅读一篇很棒的文章,其中详细介绍了所有内容:
JavaFX Pane Clipping

现在,在我举一个完整的例子之前,我建议您使用
Pane而不是
GridPane。主要原因是您可以更好地操纵(单击,拖动等)控件。此外,一些免责声明,我不是游戏开发人员,因此下面的示例只是一个示例。

MainApp.java

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class MainApp extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        GameBoard gameBoard = new GameBoard();

        BorderPane mainPane = new BorderPane();

        Button restartButton = new Button("Restart");
        restartButton.setOnAction(e -> {
            gameBoard.restartGame();
        });

        FlowPane controlPane = new FlowPane();
        controlPane.setAlignment(Pos.CENTER);
        controlPane.getChildren().add(restartButton);

        mainPane.setCenter(gameBoard);
        mainPane.setBottom(controlPane);

        stage.setScene(new Scene(mainPane, 600, 600));
        stage.show();
    }

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

}

GameBoard.java

import java.util.ArrayList;
import java.util.Random;

import javafx.animation.ParallelTransition;
import javafx.animation.RotateTransition;
import javafx.animation.TranslateTransition;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;

public class GameBoard extends StackPane {

    private final int TILE_WIDTH = 60;
    private final int TILE_HEIGHT = 60;

    private final int WIDTH;
    private final int HEIGHT;

    private int rows;
    private int columns;

    private ArrayList<Image> tileImages = new ArrayList<Image>();

    private final Pane gamePane = new Pane();

    public GameBoard() {
        this(9, 9);
    }

    public GameBoard(int rows, int columns) {

        this.rows = rows;
        this.columns = columns;

        WIDTH = columns * TILE_WIDTH;
        HEIGHT = rows * TILE_HEIGHT;

        // set the Clipping
        clipChildren(gamePane);

        // set the background color and also fix the dimensions 
        gamePane.setStyle("-fx-background-color : #52033D");
        gamePane.setMinSize(WIDTH, HEIGHT);
        gamePane.setPrefSize(WIDTH, HEIGHT);
        gamePane.setMaxSize(WIDTH, HEIGHT);
        getChildren().add(gamePane);

        initTilesImages();
        fillTiles();
    }

    public void clipChildren(Region region) {
        final Rectangle clipPane = new Rectangle();
        region.setClip(clipPane);

        // In case we want to make a resizable pane we need to update
        // our clipPane dimensions
        region.layoutBoundsProperty().addListener((ov, oldValue, newValue) -> {
            clipPane.setWidth(newValue.getWidth());
            clipPane.setHeight(newValue.getHeight());
        });
    }

    public void restartGame() {
        gamePane.getChildren().clear();
        fillTiles();
    }

    private void initTilesImages() {
        tileImages.add(new Image(this.getClass().getResource("/resources/redTile.png").toExternalForm()));
        tileImages.add(new Image(this.getClass().getResource("/resources/greenTile.png").toExternalForm()));
        tileImages.add(new Image(this.getClass().getResource("/resources/blueTile.png").toExternalForm()));
        tileImages.add(new Image(this.getClass().getResource("/resources/purpleTile.png").toExternalForm()));
        tileImages.add(new Image(this.getClass().getResource("/resources/whiteTile.png").toExternalForm()));
        tileImages.add(new Image(this.getClass().getResource("/resources/yellowTile.png").toExternalForm()));
    }

    // Fill with random images
    private void fillTiles() {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < columns; j++) {
                ImageView tile = createTile(j, i);
                gamePane.getChildren().add(tile);
            }
        }
    }

    // Create the ImageView which I call "tile"
    private ImageView createTile(int x, int y) {
        Random rand = new Random();

        int index = rand.nextInt(tileImages.size());
        ImageView img = new ImageView(tileImages.get(index));

        img.setFitWidth(TILE_WIDTH);
        img.setFitHeight(TILE_HEIGHT);

        img.setTranslateX(x * TILE_WIDTH);

        // set some rotation and transition

        RotateTransition rt = new RotateTransition(Duration.millis(2000));
        rt.setFromAngle(0);
        rt.setToAngle(360);



TranslateTransition tt = new TranslateTransition(Duration.millis(2000));
    tt.setFromY(TILE_HEIGHT * (y - rows));
    tt.setToY(y * TILE_HEIGHT);
    tt.play();

    ParallelTransition pt = new ParallelTransition(img, tt, rt);
    pt.play();

    return img;
}

}

结果: