Я не веду блог некоторое время, и я скучаю по тому, как делюсь с другими информацией обо всем, что связано с JavaFX (моя дневная работа и семья, вероятно, оправдывают себя). Для тех, кто является новичком в этом блоге, я являюсь автором JavaFX 2 Введение путем примера (JIBE) , соавтором Java 7 Recipes и техническим рецензентом книг Pro JavaFX 2 из издательства Apress . Для тех, кто уже знает меня, я хотел бы поблагодарить вас за поддержку меня и других авторов в приобретении этих книг. Что еще более важно, я надеюсь связаться с энтузиастами Java и поделиться идеями.
Книга « Введение в JavaFX 2 на примере» , был опубликован в ноябре 2011 года, и с тех пор было добавлено еще много API. Во время написания книги я работал над ранними выпусками JavaFX 2.0 вплоть до анонса на JavaOne, октябрь 2011 года. Попытка обновить книгу на основе изменений API была довольно сумасшедшей, так как все было почти не за горами. Я думал, что это было удивительно, как он даже вышел за дверь. Тем не менее, я был очень доволен. Некоторые из вас, кто читал начало книги ( JIBE ), понимают, что главы JIBE также содержатся в книге « Рецепты Java 7» (фактически она изначально взята из рецептов Java 7 ). Этот небольшой факт объясняет, почему книга JavaFX 2 Введение путем примера напоминает технические книги по рецептам или по рецептам. Мое намерение состояло в том, чтобы помочь читателю познакомиться быстро без большого количества технической болтовни. Вместо того, чтобы пытаться убедить людей в платформе JavaFX, я бы лучше продемонстрировал вещи с полезными примерами. Я нахожу контрпродуктивным обсуждение глубоких философских дискуссий о том, почему одна конкретная технология превосходит другую ( ссылка на Cheesy 80’s Highlander ).
После выпуска JavaFX 2.0 появились последующие версии, такие как JavaFX 2.1, 2.2 и предстоящий выпуск JavaFX 8 (январь 2014 г.). В этой записи блога я приведу рецепт API печати JavaFX 8. Как и в моей книге (JIBE), я буду следовать той же схеме, что и раньше, где я представляю проблему, решение, код и раздел «Как это работает».
Заявитель : В этом блоге вы познакомитесь с функциональными интерфейсами Java с использованием лямбда-выражений. Я не буду обсуждать их здесь, но отсылаю вас к учебным пособиям Oracle по Project Lambda .
Необходимое программное обеспечение:
JDK 8 — https://jdk8.java.net/download.html
проблема
Вы хотите создать приложение JavaFX, которое распечатывает посещенный веб-сайт.
Решение
Используйте API JavaFX 8 PrintJob и Printer для печати любого узла графа сцены JavaFX. Кроме того, используйте API-интерфейсы WebView и WebEngine для отображения веб-сайта или веб-страницы.
инструкции
Предполагая, что вы скомпилировали и запустили приложение, следуйте инструкциям ниже:
- Введите адрес веб-сайта или URL в текстовое поле.
- Нажмите клавишу ввода
- После загрузки страницы нажмите кнопку «Печать»
- Подойдите к принтеру, чтобы получить распечатанную веб-страницу.
Код
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
package org.carlfx; import javafx.application.Application; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.concurrent.Worker.State; import javafx.print.*; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.TextField; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.transform.Scale; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import javafx.stage.Stage; /** * Demo to use JavaFX 8 Printer API. * * @author cdea */ public class PrintDemo extends Application { @Override public void start(Stage primaryStage) { final TextField urlTextField = new TextField(); final Button printButton = new Button( "Print" ); final WebView webPage = new WebView(); final WebEngine webEngine = webPage.getEngine(); HBox hbox = new HBox(); hbox.getChildren().addAll(urlTextField, printButton); BorderPane borderPane = new BorderPane(); borderPane.setTop(hbox); borderPane.setCenter(webPage); Scene scene = new Scene(borderPane, 300 , 250 ); primaryStage.setTitle( "Print Demo" ); primaryStage.setScene(scene); // print button pressed, page loaded final BooleanProperty printButtonClickedProperty = new SimpleBooleanProperty( false ); final BooleanProperty pageLoadedProperty = new SimpleBooleanProperty( false ); // when the a page is loaded and the button was pressed call the print() method. final BooleanProperty printActionProperty = new SimpleBooleanProperty( false ); printActionProperty.bind(pageLoadedProperty.and(printButtonClickedProperty)); // WebEngine updates flag when finished loading web page. webEngine.getLoadWorker() .stateProperty() .addListener( (ChangeListener) (obsValue, oldState, newState) -> { if (newState == State.SUCCEEDED) { pageLoadedProperty.set( true ); } }); // When user enters a url and hits the enter key. urlTextField.setOnAction( aEvent -> { pageLoadedProperty.set( false ); printButtonClickedProperty.set( false ); webEngine.load(urlTextField.getText()); }); // When the user clicks the print button the webview node is printed printButton.setOnAction( aEvent -> { printButtonClickedProperty.set( true ); }); // Once the print action hears a true go print the WebView node. printActionProperty.addListener( (ChangeListener) (obsValue, oldState, newState) -> { if (newState) { print(webPage); } }); primaryStage.show(); } /** Scales the node based on the standard letter, portrait paper to be printed. * @param node The scene node to be printed. */ public void print( final Node node) { Printer printer = Printer.getDefaultPrinter(); PageLayout pageLayout = printer.createPageLayout(Paper.NA_LETTER, PageOrientation.PORTRAIT, Printer.MarginType.DEFAULT); double scaleX = pageLayout.getPrintableWidth() / node.getBoundsInParent().getWidth(); double scaleY = pageLayout.getPrintableHeight() / node.getBoundsInParent().getHeight(); node.getTransforms().add( new Scale(scaleX, scaleY)); PrinterJob job = PrinterJob.createPrinterJob(); if (job != null ) { boolean success = job.printPage(node); if (success) { job.endJob(); } } } /** * The main() method is ignored in correctly deployed JavaFX application. * main() serves only as fallback in case the application can not be * launched through deployment artifacts, e.g., in IDEs with limited FX * support. NetBeans ignores main(). * * @param args the command line arguments */ public static void main(String[] args) { launch(args); } } |
Как это работает
Код начинается с создания TextField , Button и элемента управления WebView для размещения в BorderPane . Используя макет BorderPane , вы сможете размещать элементы управления в следующих регионах: сверху, справа, слева, снизу и по центру.
Подобно веб-браузеру, текстовое поле позволяет пользователю ввести URL-адрес веб-сайта. После ввода URL-адреса пользователь нажимает клавишу ввода, чтобы загрузить веб-страницу в узел WebView. При размещении элементов управления в любой из боковых областей макет BorderPane будет иметь предпочтительную высоту всех добавляемых элементов управления. Центральная область позволит узлу занимать доступное пространство минус оставшееся пространство, занимаемое шириной и высотой граничащих боковых областей. Другими словами, если боковые области не содержат узлов (пустых), узел в центральной области имеет возможность взять все доступное пространство ширины и высоты, предоставленное его родителем (сцена). Поскольку узел WebView будет занимать центральную область, он будет занимать всю доступную ширину и высоту (минус верхняя область), когда веб-страница полностью загружена. Вы также заметите полосы прокрутки, позволяющие пользователю просматривать страницы больше, чем текущий порт просмотра.
После размещения всех компонентов для пользовательского интерфейса, вам нужно будет подключить все. Здесь вы просто создадите три логических свойства
(javafx.beans.property.SimpleBooleanProperty) экземпляры. Первая переменная свойства printButtonClickedProperty — это флаг, указывающий, когда была нажата кнопка печати. Второе свойство pageLoadedProperty — это флаг, указывающий, что загрузка веб-страницы завершена. Наконец, вы захотите обратить внимание на printActionProperty, который связывает printButtonClickedProperty и pageLoadedProperty, используя свободный API. При оценке printActionProperty будет иметь значение true, если оба свойства printLoadedProperty и printLoadedProperty являются истинными значениями.
1
2
3
4
5
6
7
|
// print button pressed, page loaded final BooleanProperty printButtonClickedProperty = new SimpleBooleanProperty( false ); final BooleanProperty pageLoadedProperty = new SimpleBooleanProperty( false ); // when the a page is loaded and the button was pressed call the print() method. final BooleanProperty printActionProperty = new SimpleBooleanProperty( false ); printActionProperty.bind(pageLoadedProperty.and(printButtonClickedProperty)); |
Продолжая подключение пользовательского интерфейса, я выбрал подход, основанный на событиях, когда код обработчика будет реагировать на события и изменения свойств. Начиная с узла WebView, я прикрепил код обработчика к экземпляру statePropery (ChangeListener), чтобы для pageLoadedProperty было установлено значение true после успешной загрузки веб-страницы.
1
2
3
4
5
6
7
8
|
// WebEngine updates flag when finished loading web page. webEngine.getLoadWorker() .stateProperty() .addListener( (ChangeListener) (obsValue, oldState, newState) -> { if (newState == State.SUCCEEDED) { pageLoadedProperty.set( true ); } }); |
N ext вы увидите метод setOnAction текстового поля, содержащий код обработчика, который сбрасывает объекты pageLoadedProperty и printButtonClickedProperty . Кроме того, код будет инициировать загрузку страницы с помощью метода WebEngine load () WebView .
1
2
3
4
5
6
|
// When user enters a url and hits the enter key. urlTextField.setOnAction( aEvent -> { pageLoadedProperty.set( false ); printButtonClickedProperty.set( false ); webEngine.load(urlTextField.getText()); }); |
После того, как код действия элемента управления TextField подключен, кнопке печати также потребуется код обработчика, чтобы установить для флага printButtonClickedProperty значение true. Наконец, свойство printActionProperty будет нуждаться в ChangeListener для ответа, когда его состояние оценивается как true. Когда это оценивается как true, вызывается мой метод print ().
01
02
03
04
05
06
07
08
09
10
11
|
// When the user clicks the print button the webview node is printed printButton.setOnAction( aEvent -> { printButtonClickedProperty.set( true ); }); // Once the print action hears a true go print the WebView node. printActionProperty.addListener( (ChangeListener) (obsValue, oldState, newState) -> { if (newState) { print(webPage); } }); |
Наконец, метод print () принимает объект узла JavaFX для печати. У объекта Printer есть метод, который возвращает принтер по умолчанию, установленный на вашем компьютере. Перед фактической печатью мы можем получить макет страницы по умолчанию для масштабирования узла перед печатью узла. Если вы этого не сделаете, будет напечатана только часть веб-страницы. Получив принтер по умолчанию, метод createPrinterJob () вызывается для возврата экземпляра PrinterJob, который выполняет фактическую печать. Чтобы напечатать узел отображаемого типа JavaFX, вы просто вызываете метод printPage () объекта PrinterJob, передавая экземпляр Node в качестве параметра.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
/** Scales the node based on the standard letter, portrait paper to be printed. * @param node The scene node to be printed. */ public void print( final Node node) { Printer printer = Printer.getDefaultPrinter(); PageLayout pageLayout = printer.createPageLayout(Paper.NA_LETTER, PageOrientation.PORTRAIT, Printer.MarginType.DEFAULT); double scaleX = pageLayout.getPrintableWidth() / node.getBoundsInParent().getWidth(); double scaleY = pageLayout.getPrintableHeight() / node.getBoundsInParent().getHeight(); node.getTransforms().add( new Scale(scaleX, scaleY)); PrinterJob job = PrinterJob.createPrinterJob(); if (job != null ) { boolean success = job.printPage(node); if (success) { job.endJob(); } } } |
В заключение я считаю, что API проще в использовании по сравнению с API Java Swing / AWT. Я хотел бы отметить, что есть много функций, с которыми вы можете поиграть, так как эта запись в блоге лишь поверхностно освещает API, доступные в настоящее время.
ПРИМЕЧАНИЕ. API принтера JavaFX 8 все еще находится на ранних стадиях, и остаются нерешенные проблемы (проблемы Jira).