JavaFX(部分)在网格窗格外隐藏图像
JavaFX (partially) hide image outside gridpane
基本上我有这样的布局:Image of the GUI layout。
它是一个带有菜单栏(红色)的 BorderPane(黑色),一个包含一些东西的容器,位于左侧边栏(绿色)和一个嵌套在中心(橙色)内的 GridPane(灰色)
GridPane 的单元格中填充了可以消失的 ImageView(蓝色),当重新填充时,它们可能堆叠在一起,通过平行过渡从顶部落入它们的位置。到目前为止一切顺利,但我想隐藏 GridPane 和菜单栏之间的图像部分,但无法让它工作。有没有人知道如何处理这个问题?我也不介意只隐藏整个图片,而只显示其中的一部分。
//编辑:
主布局是使用 fxml 文件创建的。图片视图已在启动后添加到网格窗格中。
如果您查看 GridPane 的 JavaDoc,您将看到:
GridPane 默认不裁剪其内容,因此如果 child 的最小尺寸阻止,childrens 的边界可能会超出其自身边界它适合它 space.
class Pane
中的每个 child 也是如此。所以你需要手动处理剪辑,为此有方法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
。主要原因是你可以操纵(点击、拖动等)你的控件better.Also,一些免责声明,我不是游戏开发者,所以下面的例子只是一个例子。
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;
}
}
结果:
这是 JKostikiadis 解决方案的一个简短版本,它使用了绑定:
private void clipChildren(Region region) {
Rectangle clip = new Rectangle();
clip.widthProperty().bind(region.widthProperty());
clip.heightProperty().bind(region.heightProperty());
region.setClip(clip);
}
基本上我有这样的布局:Image of the GUI layout。 它是一个带有菜单栏(红色)的 BorderPane(黑色),一个包含一些东西的容器,位于左侧边栏(绿色)和一个嵌套在中心(橙色)内的 GridPane(灰色)
GridPane 的单元格中填充了可以消失的 ImageView(蓝色),当重新填充时,它们可能堆叠在一起,通过平行过渡从顶部落入它们的位置。到目前为止一切顺利,但我想隐藏 GridPane 和菜单栏之间的图像部分,但无法让它工作。有没有人知道如何处理这个问题?我也不介意只隐藏整个图片,而只显示其中的一部分。
//编辑:
如果您查看 GridPane 的 JavaDoc,您将看到:
GridPane 默认不裁剪其内容,因此如果 child 的最小尺寸阻止,childrens 的边界可能会超出其自身边界它适合它 space.
class Pane
中的每个 child 也是如此。所以你需要手动处理剪辑,为此有方法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
。主要原因是你可以操纵(点击、拖动等)你的控件better.Also,一些免责声明,我不是游戏开发者,所以下面的例子只是一个例子。
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;
}
}
结果:
这是 JKostikiadis 解决方案的一个简短版本,它使用了绑定:
private void clipChildren(Region region) {
Rectangle clip = new Rectangle();
clip.widthProperty().bind(region.widthProperty());
clip.heightProperty().bind(region.heightProperty());
region.setClip(clip);
}