JavaFX-如何创建(不可见)WebView的快照/屏幕截图
我想从JavaFX(8)中的网络视图创建快照/屏幕截图/图像。JavaFX-如何创建(不可见)WebView的快照/屏幕截图,java,webview,javafx,javafx-8,javafx-webengine,Java,Webview,Javafx,Javafx 8,Javafx Webengine,我想从JavaFX(8)中的网络视图创建快照/屏幕截图/图像。此网络视图不需要可见(在我的情况下)。 我的问题: 当网络视图不可见(或未添加到任何可见容器中)时,是否可以(以任何方式)从网络视图创建屏幕截图/图像 请参阅我的示例代码,当WebView(或其父滚动窗格)为visible=false, 屏幕截图不起作用(分别为emtpy/blank) 示例代码: package test; import javafx.animation.KeyFrame; import javafx.animat
此网络视图不需要可见(在我的情况下)。
我的问题:
当网络视图不可见(或未添加到任何可见容器中)时,是否可以(以任何方式)从网络视图创建屏幕截图/图像 请参阅我的示例代码,当WebView(或其父滚动窗格)为visible=false,
屏幕截图不起作用(分别为emtpy/blank) 示例代码:
package test;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.SnapshotResult;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.util.Duration;
public class JavaFXApplication extends Application
{
@Override
public void start(Stage primaryStage)
{
ImageView webviewPreviewImage = new ImageView();
Label waitLabel = new Label("Please wait...");
WebView webView = new WebView();
webView.setMaxHeight(480d);
webView.setMinHeight(480d);
webView.setMaxWidth(640d);
webView.setMinWidth(640d);
webView.setZoom(0.4);
ScrollPane scrollpane = new ScrollPane(webView);
scrollpane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollpane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrollpane.setMaxWidth(0); //WORKAROUND: hide the WebView/ScrollPane
scrollpane.setMaxHeight(0); //WORKAROUND: hide the WebView/ScrollPane
scrollpane.setMinWidth(0); //WORKAROUND: hide the WebView/ScrollPane
scrollpane.setMinHeight(0); //WORKAROUND: hide the WebView/ScrollPane
//scrollpane.setVisible(false); //when WebView is invisible, SnapShot doesn't work!
webView.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>()
{
@Override
public void changed(ObservableValue ov, Worker.State oldState, Worker.State newState)
{
if (newState == Worker.State.SUCCEEDED)
{
//When SUCCEEDED is called, the WebPage may not has fully finished rendering!
//so, wait for few seceonds before making the screenshot...
Timeline timeline = new Timeline(new KeyFrame(
Duration.millis(1500),
ae -> takeSnapshot()));
timeline.play();
}
}
private KeyFrame takeSnapshot()
{
webView.snapshot((SnapshotResult param) ->
{
webviewPreviewImage.setImage(param.getImage());
webviewPreviewImage.setFitHeight(240d);
webviewPreviewImage.setFitWidth(320d);
webviewPreviewImage.setPreserveRatio(true);
waitLabel.setVisible(false);
return null;
}, null, null);
return null;
}
});
webView.getEngine().load("http://www.bing.com");
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
root.setSpacing(10d);
root.getChildren().add(waitLabel);
root.getChildren().add(scrollpane);
root.getChildren().add(webviewPreviewImage);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
封装测试;
导入javafx.animation.KeyFrame;
导入javafx.animation.Timeline;
导入javafx.application.application;
导入javafx.beans.value.ChangeListener;
导入javafx.beans.value.observeValue;
导入javafx.concurrent.Worker;
导入javafx.geometry.Pos;
导入javafx.scene.scene;
导入javafx.scene.SnapshotResult;
导入javafx.scene.control.Label;
导入javafx.scene.control.ScrollPane;
导入javafx.scene.image.ImageView;
导入javafx.scene.layout.VBox;
导入javafx.scene.web.WebView;
导入javafx.stage.stage;
导入javafx.util.Duration;
公共类JavaFXApplication扩展了应用程序
{
@凌驾
公共无效开始(阶段primaryStage)
{
ImageView WebViewPreviewiImage=新建ImageView();
Label waitLabel=新标签(“请稍候…”);
WebView WebView=新建WebView();
webView.setMaxHeight(480d);
webView.setMinHeight(480d);
webView.setMaxWidth(640d);
webView.setMinWidth(640d);
webView.setZoom(0.4);
ScrollPane ScrollPane=新的滚动窗格(webView);
scrollpane.setHbarPolicy(scrollpane.ScrollBarPolicy.NEVER);
scrollpane.setVbarPolicy(scrollpane.ScrollBarPolicy.NEVER);
scrollpane.setMaxWidth(0);//解决方法:隐藏WebView/scrollpane
scrollpane.setMaxHeight(0);//解决方法:隐藏WebView/scrollpane
scrollpane.setMinWidth(0);//解决方法:隐藏WebView/scrollpane
scrollpane.setMinHeight(0);//解决方法:隐藏WebView/scrollpane
//scrollpane.setVisible(false);//当WebView不可见时,快照不工作!
webView.getEngine().getLoadWorker().stateProperty().addListener(新的ChangeListener())
{
@凌驾
公共无效已更改(observeValue ov,Worker.State oldState,Worker.State newState)
{
if(newState==Worker.State.successed)
{
//调用Succeed时,网页可能尚未完全完成渲染!
//所以,等几秒钟再做截图。。。
时间轴=新时间轴(新关键帧(
持续时间。毫秒(1500),
ae->takeSnapshot());
timeline.play();
}
}
私有关键帧快照()
{
webView.snapshot((快照结果参数)->
{
webviewPreviewImage.setImage(param.getImage());
webviewPreviewImage.setFitHeight(240d);
webviewPreviewImage.setFitWidth(320d);
webviewPreviewImage.setPreserveRatio(true);
waitLabel.setVisible(false);
返回null;
},空,空);
返回null;
}
});
webView.getEngine().load(“http://www.bing.com");
VBox root=新的VBox();
根部设置对齐(位置中心);
根定距(10d);
root.getChildren().add(waitLabel);
root.getChildren().add(滚动窗格);
root.getChildren().add(webviewPreviewImage);
场景=新场景(根,800600);
初级阶段。场景(场景);
primaryStage.show();
}
公共静态void main(字符串[]args)
{
发射(args);
}
}
类WebView包含以下功能:
private boolean isTreeReallyVisible() {
if (getScene() == null) {
return false;
}
final Window window = getScene().getWindow();
if (window == null) {
return false;
}
boolean iconified = (window instanceof Stage) ? ((Stage)window).isIconified() : false;
return impl_isTreeVisible()
&& window.isShowing()
&& window.getWidth() > 0
&& window.getHeight() > 0
&& !iconified;
}
只要函数返回false,渲染就会被阻止。因此,让它进行渲染可能相当棘手。通常,对于快照,您根本不需要将节点放在场景中。经过大量搜索和拼凑,我在oracle论坛上找到了一个SO海报示例,该海报只存在以下问题:webview的大小是固定的,html中使用的css(不是JavaFX)需要: 隐藏最后一个滚动条 因此,我提出了以下快照方法(带有动画的应用程序作为应用程序的示例):
包应用;
导入java.io.File;
导入java.io.IOException;
导入javax.imageio.imageio;
导入javafx.animation.animation;
导入javafx.animation.PauseTransition;
导入javafx.animation.TranslateTransition;
导入javafx.application.application;
导入javafx.embed.swing.SwingFXUtils;
导入javafx.event.ActionEvent;
导入javafx.event.EventHandler;
导入javafx.geometry.Insets;
导入javafx.geometry.Pos;
导入javafx.scene.scene;
导入javafx.scene.control.Button;
导入javafx.scene.control.Label;
导入javafx.scene.effect.GaussianBlur;
导入javafx.scene.image.WritableImage;
导入javafx.scene.layout.ancorpane;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.layout.Pane;
导入javafx.scene.paint.Color;
导入javafx.scene.shape.Rectangle;
导入javafx.scene.web.WebView;
导入javafx.stage.model;
导入javafx.stage.stage;
导入javafx.stage.StageStyle;
导入javafx.util.Duration;
公共类WebViewSnapshot扩展了应用程序{
边界窗格根窗格;
翻译动画;
@凌驾
公共无效开始(阶段primaryStage){
矩形rect=新矩形(50,50,50,50);
珊瑚色;
动画=createAnimation(rect);
按钮快照按钮=新按钮(“拍摄快照”);
窗格=新窗格(矩形);
窗格。设置大小(600150);
根窗格=新边框窗格(窗格,nu
overflow-x: hidden;
overflow-y: hidden;
package application;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javafx.animation.Animation;
import javafx.animation.PauseTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.web.WebView;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
public class WebViewSnapshot extends Application {
BorderPane rootPane;
TranslateTransition animation;
@Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(50, 50, 50, 50);
rect.setFill(Color.CORAL);
animation = createAnimation(rect);
Button snapshotButton = new Button("Take snapshot");
Pane pane = new Pane(rect);
pane.setMinSize(600, 150);
rootPane = new BorderPane(pane, null, null, snapshotButton, new Label("This is the main scene"));
snapshotButton.setOnAction(e -> {
// html file being shown in webview
File htmlFile = new File ("generated/template.html");
// the resulting snapshot png file
File aboutFile = new File ("generated/about.png");
generate(htmlFile, aboutFile, 1280, 720);
});
BorderPane.setAlignment(snapshotButton, Pos.CENTER);
BorderPane.setMargin(snapshotButton, new Insets(5));
Scene scene = new Scene(rootPane);
primaryStage.setScene(scene);
primaryStage.show();
}
private TranslateTransition createAnimation(Rectangle rect) {
TranslateTransition animation = new TranslateTransition(Duration.seconds(1), rect);
animation.setByX(400);
animation.setCycleCount(Animation.INDEFINITE);
animation.setAutoReverse(true);
animation.play();
return animation;
}
public void generate(File htmlFile, File outputFile, double width, double height) {
animation.pause();
// rootPane is the root of original scene in an FXML controller you get this when you assign it an id
rootPane.setEffect(new GaussianBlur());
Stage primaryStage = (Stage)rootPane.getScene().getWindow();
// creating separate webview holding same html content as in original scene
WebView webView = new WebView();
// with the size I want the snapshot
webView.setPrefSize(width, height);
AnchorPane snapshotRoot = new AnchorPane(webView);
webView.getEngine().load(htmlFile.toURI().toString());
Stage popupStage = new Stage(StageStyle.TRANSPARENT);
popupStage.initOwner(primaryStage);
popupStage.initModality(Modality.APPLICATION_MODAL);
// this popup doesn't really show anything size = 1x1, it just holds the snapshot-webview
popupStage.setScene(new Scene(snapshotRoot, 1, 1));
// pausing to make sure the webview/picture is completely rendered
PauseTransition pt = new PauseTransition(Duration.seconds(2));
pt.setOnFinished(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent event) {
WritableImage image = webView.snapshot(null, null);
// writing a png to outputFile
// writing a JPG like this will result in a pink JPG, see other posts
// if somebody can scrape me simple code to convert it ARGB to RGB or something
String format = "png";
try {
ImageIO.write(SwingFXUtils.fromFXImage(image, null), format, outputFile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
rootPane.setEffect(null);
popupStage.hide();
animation.play();
}
}
});
// pausing, after pause onFinished event will take + write snapshot
pt.play();
// GO!
popupStage.show();
}
public static void main(String[] args) {
launch(args);
}
}