Скриншот узла или сцены в JavaFX (scene or node snapshot)

Java

Начиная с версии 2.2 в JavaFX появился инструмент, обеспечивающий удобный способ производства снимка экрана. Более того, «снимаемый» объект не обязательно должен быть отображен на экране, что позволяет использовать этот инструмент для компоновки отчетов с различными рисунками, графиками, таблицами и текстовыми областями с дальнейшим сохранением в виде рисунка.

Методы, определенные в классе javafx.scene.Node, отвечающие за производство скриншотов (наверное вернее говорить снапшотов):

  • public WritableImage snapshot(SnapshotParameters params, WritableImage image)
  • public void snapshot(Callback<SnapshotResult,java.lang.Void> callback, SnapshotParameters params, WritableImage image)

Оба метода производят снапшот узла на котором они были вызваны. В первом варианте метод просто возвращает сгенерированное изображение. Во втором же случае после того как будет готов снапшот происходит вызов метода void call(SnapshotResult snapshotResult) класса указанного в качестве параметра callback. Передаваемый в вызываемый метод экземпляр SnapshotResult содержит в себе как само сгенерированное изображение (WritableImage), так и ссылку на снимаемый узел (Node) и информацию о параметрах рендеринга (SnapshotParameters).

Если в качестве параметра image передается null, то создается новый WritableImage с необходимым размером изображения, в противном случае используется переданный параметром объект, используя для визуализации только доступную область определенную параметрами объекта image.

Настройки рендеринга задаются экземпляром SnapshotParameters переданного параметром params. Основные параметры задаются при помощи:

  • void setFill(Paint fill) — фоновая заливка, включая градиенты. ВАЖНО! Границы градиента, определяются размерами генерируемого изображения, а не размерами узла/сцены (т.е. задается фон для генерируемого изображения, а не снимаемого объекта).
  • void setTransform(Transform transform) — различные преобразования — вращение, масштабирование, сдвиг и перенос (для комбинации преобразований используется аффинное преобразование).
  • void setViewport(Rectangle2D viewport) — задает область для отображения. ВАЖНО! Сначала идут все преобразования, после чего задается область отображения.

Как уже было сказано, узел не обязан отображаться на экране, чтобы можно было сделать скриншот, однако для правильного отображения все же придется сделать его частью сцены.

пруф

NOTE: In order for CSS and layout to function correctly, the node must be part of a Scene (the Scene may be attached to a Stage, but need not be).

ПРИМЕЧАНИЕ. Для правильной работы CSS и макета узел должен быть частью сцены (сцена может быть прикреплена к сцене, но не обязательно).

ПРИМЕР

Для примера используется часть сцены из оракловского туториала «Getting Started with JavaFX». Импорты были удалены, для того чтобы уменьшить размер кода в примере.

public class SnapshotExample extends Application {
	@Override
	public void start(Stage arg0) throws Exception {
		
		GridPane grid = new GridPane();
			grid.setAlignment(Pos.CENTER);
			grid.setHgap(10);
			grid.setVgap(10);
			grid.setPadding(new Insets(25, 25, 25, 25));
			grid.setStyle("-fx-background: ORANGE");
		
		Text scenetitle = new Text("Welcome");
			scenetitle.setFont(Font.font("Tahoma", FontWeight.NORMAL, 20));
			grid.add(scenetitle, 0, 0, 2, 1);

		Label userName = new Label("User Name:");
			grid.add(userName, 0, 1);

		TextField userTextField = new TextField();
			grid.add(userTextField, 1, 1);

		Label pw = new Label("Password:");
			grid.add(pw, 0, 2);

		PasswordField pwBox = new PasswordField();
			grid.add(pwBox, 1, 2);
		
		Scene scene = new Scene(grid, 350, 150);
	
	// 1 - Скриншот сцены
		saveAsPng(scene.getRoot(), "snapshot");
		
	// 2 - Скриншот отдельного узла
		saveAsPng(scenetitle, "title");
	
	// 3 - Установка фона для узла
		SnapshotParameters ssp = new SnapshotParameters();
				//горизонтальный градиент, от желтого до белого
			ssp.setFill(new LinearGradient(0, 0, 1, 0, 
								true,
								CycleMethod.NO_CYCLE,
								new Stop(0, Color.YELLOW), 
								new Stop(1, Color.WHITE)
							)
					);
		saveAsPng(scenetitle,"title_grad", ssp);
		
	// 4 - Масштабирование узла и установка области рендеринга
		ssp = new SnapshotParameters();
			ssp.setViewport(new Rectangle2D(30, 55, 200, 200));
			ssp.setTransform(new Scale(0.75,2));
		saveAsPng(grid, "snapshot_transform", ssp);
		
		System.exit(0);
	}

	public void saveAsPng(Node node, String fname) {
		saveAsPng(node, fname, new SnapshotParameters());
	}
	
	public void saveAsPng(Node node, String fname, SnapshotParameters ssp) {
		WritableImage image = node.snapshot(ssp, null);
		File file = new File(fname+".png");
		try {
	        ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file);
	    } catch (IOException e) {
	        // TODO: handle exception here
	    }
	}
	
}

РЕЗУЛЬТАТ РАБОТЫ

1 — Скриншот всей сцены (snapshot.png):

2 — Скриншот отдельного узла (title.png):

3 — Установка фона для узла (title_grad.png):

4 — Масштабирование узла и установка области рендеринга (snapshot_transform.png):

Примечание: в представленном примере при снапшоте сцены используется вызов метода snapshot() на её корневом элементе. Это не значит, что сама сцена не поддерживает такую возможность. Так было сделано из-за экономии места в коде примера.

Отличие сигнатуры методов для снятия скриншота со сцены заключается в том, что в них нет параметра SnapshotParameters params :

  • public WritableImage snapshot(WritableImage image)
  • public void snapshot(Callback<SnapshotResult,java.lang.Void> callback, WritableImage image)

Из-за этого теряется возможность определить настройки рендеринга, что делает прямой способ снапшота сцены менее гибким по сравнению со снапшотом её корневого узла.

Михаил Миронов

Живу в Нижнем Новгороде, работаю программистом с 2017 года, основная специализация Java, но также хорошо знаю PHP, Python, XML, HTML/CSS.

Добавить комментарий