Các JavaFX docs trạng thái đó một WebView
đã sẵn sàng khi Worker.State.SUCCEEDED
đạt được tuy nhiên, trừ khi bạn chờ một thời gian (ví dụ Animation
, Transition
,PauseTransition
, vv), một trang trống được trả lại.
Điều này cho thấy rằng có một sự kiện xảy ra bên trong WebView đã sẵn sàng để chụp, nhưng đó là gì?
Có hơn 7.000 đoạn mã trên GitHub sử dụngSwingFXUtils.fromFXImage
nhưng hầu hết chúng có vẻ không liên quan đếnWebView
, đều tương tác (mặt nạ con người trong điều kiện cuộc đua) hoặc sử dụng Chuyển đổi tùy ý (bất cứ nơi nào từ 100ms đến 2.000ms).
Tôi đã thử:
Lắng nghe
changed(...)
từ bên trong cácWebView
kích thước (DoubleProperty
thực hiện các thuộc tính chiều cao và chiều rộngObservableValue
, có thể giám sát những điều này)- Không khả thi. Đôi khi, giá trị dường như thay đổi tách biệt với thói quen sơn, dẫn đến nội dung một phần.
Nói một cách mù quáng bất cứ điều gì và mọi thứ
runLater(...)
về Chủ đề ứng dụng FX.- TechnanyNhiều kỹ thuật sử dụng điều này, nhưng các thử nghiệm đơn vị của riêng tôi (cũng như một số phản hồi tuyệt vời từ các nhà phát triển khác) giải thích rằng các sự kiện thường đã ở đúng luồng và cuộc gọi này là không cần thiết. Điều tốt nhất tôi có thể nghĩ đến là thêm một độ trễ vừa đủ thông qua việc xếp hàng mà nó hoạt động đối với một số người.
Thêm trình nghe / kích hoạt DOM hoặc trình nghe / kích hoạt JavaScript vào
WebView
- OthBoth JavaScript và DOM dường như được tải đúng cách khi
SUCCEEDED
được gọi mặc dù chụp trống. Người nghe DOM / JavaScript dường như không giúp đỡ.
- OthBoth JavaScript và DOM dường như được tải đúng cách khi
Sử dụng một
Animation
hoặcTransition
để "ngủ" một cách hiệu quả mà không chặn luồng FX chính.- Approach Cách tiếp cận này hoạt động và nếu độ trễ đủ dài, có thể mang lại tới 100% các bài kiểm tra đơn vị, nhưng thời gian Chuyển tiếp dường như là một thời điểm trong tương lai mà chúng ta chỉ đoán và thiết kế tồi. Đối với các ứng dụng biểu diễn hoặc nhiệm vụ quan trọng, điều này buộc lập trình viên phải đánh đổi giữa tốc độ hoặc độ tin cậy, cả hai đều là một trải nghiệm tồi tệ cho người dùng.
Khi nào là thời điểm tốt để gọi WebView.snapshot(...)
?
Sử dụng:
SnapshotRaceCondition.initialize();
BufferedImage bufferedImage = SnapshotRaceCondition.capture("<html style='background-color: red;'><h1>TEST</h1></html>");
/**
* Notes:
* - The color is to observe the otherwise non-obvious cropping that occurs
* with some techniques, such as `setPrefWidth`, `autosize`, etc.
* - Call this function in a loop and then display/write `BufferedImage` to
* to see strange behavior on subsequent calls.
* - Recommended, modify `<h1>TEST</h1` with a counter to see content from
* previous captures render much later.
*/
Đoạn mã:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.WritableImage;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger;
public class SnapshotRaceCondition extends Application {
private static final Logger log = Logger.getLogger(SnapshotRaceCondition.class.getName());
// self reference
private static SnapshotRaceCondition instance = null;
// concurrent-safe containers for flags/exceptions/image data
private static AtomicBoolean started = new AtomicBoolean(false);
private static AtomicBoolean finished = new AtomicBoolean(true);
private static AtomicReference<Throwable> thrown = new AtomicReference<>(null);
private static AtomicReference<BufferedImage> capture = new AtomicReference<>(null);
// main javafx objects
private static WebView webView = null;
private static Stage stage = null;
// frequency for checking fx is started
private static final int STARTUP_TIMEOUT= 10; // seconds
private static final int STARTUP_SLEEP_INTERVAL = 250; // millis
// frequency for checking capture has occured
private static final int CAPTURE_SLEEP_INTERVAL = 10; // millis
/** Called by JavaFX thread */
public SnapshotRaceCondition() {
instance = this;
}
/** Starts JavaFX thread if not already running */
public static synchronized void initialize() throws IOException {
if (instance == null) {
new Thread(() -> Application.launch(SnapshotRaceCondition.class)).start();
}
for(int i = 0; i < (STARTUP_TIMEOUT * 1000); i += STARTUP_SLEEP_INTERVAL) {
if (started.get()) { break; }
log.fine("Waiting for JavaFX...");
try { Thread.sleep(STARTUP_SLEEP_INTERVAL); } catch(Exception ignore) {}
}
if (!started.get()) {
throw new IOException("JavaFX did not start");
}
}
@Override
public void start(Stage primaryStage) {
started.set(true);
log.fine("Started JavaFX, creating WebView...");
stage = primaryStage;
primaryStage.setScene(new Scene(webView = new WebView()));
// Add listener for SUCCEEDED
Worker<Void> worker = webView.getEngine().getLoadWorker();
worker.stateProperty().addListener(stateListener);
// Prevents JavaFX from shutting down when hiding window, useful for calling capture(...) in succession
Platform.setImplicitExit(false);
}
/** Listens for a SUCCEEDED state to activate image capture **/
private static ChangeListener<Worker.State> stateListener = (ov, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
WritableImage snapshot = webView.snapshot(new SnapshotParameters(), null);
capture.set(SwingFXUtils.fromFXImage(snapshot, null));
finished.set(true);
stage.hide();
}
};
/** Listen for failures **/
private static ChangeListener<Throwable> exceptListener = new ChangeListener<Throwable>() {
@Override
public void changed(ObservableValue<? extends Throwable> obs, Throwable oldExc, Throwable newExc) {
if (newExc != null) { thrown.set(newExc); }
}
};
/** Loads the specified HTML, triggering stateListener above **/
public static synchronized BufferedImage capture(final String html) throws Throwable {
capture.set(null);
thrown.set(null);
finished.set(false);
// run these actions on the JavaFX thread
Platform.runLater(new Thread(() -> {
try {
webView.getEngine().loadContent(html, "text/html");
stage.show(); // JDK-8087569: will not capture without showing stage
stage.toBack();
}
catch(Throwable t) {
thrown.set(t);
}
}));
// wait for capture to complete by monitoring our own finished flag
while(!finished.get() && thrown.get() == null) {
log.fine("Waiting on capture...");
try {
Thread.sleep(CAPTURE_SLEEP_INTERVAL);
}
catch(InterruptedException e) {
log.warning(e.getLocalizedMessage());
}
}
if (thrown.get() != null) {
throw thrown.get();
}
return capture.get();
}
}
Liên quan:
- Ảnh chụp màn hình của trang web đầy đủ được tải vào thành phần JavaView WebView, không chỉ phần hiển thị
- Tôi có thể chụp ảnh nhanh của chương trình không?
- Ảnh chụp màn hình toàn trang, Java
- JavaFX 2.0+ WebView / WebEngine kết xuất trang web thành hình ảnh
- Đặt chiều cao và chiều rộng của sân khấu và cảnh trong javafx
- JavaFX: cách thay đổi kích thước giai đoạn khi sử dụng webview
- Kích thước chính xác của Webview được nhúng trong Tabelcell
- https://docs.oracle.com/javase/8/javafx/embedded-browser-tutorial/add-browser.htmlm#CEGDIBBI
- http://docs.oracle.com/javafx/2/swing/swing-fx-interoperability.htmlm#CHDIEEJE
- https://bugs.openjdk.java.net/browse/JDK-8126854
- https://bugs.openjdk.java.net/browse/JDK-8087569
Platform.runLater
đã được thử nghiệm và không sửa nó. Hãy thử nó cho chính mình nếu bạn không đồng ý. Tôi rất vui khi được sai, nó sẽ đóng vấn đề.
SUCCEEDED
trạng thái (trong đó người nghe kích hoạt trên luồng FX) là kỹ thuật phù hợp. Nếu có một cách để hiển thị các sự kiện được xếp hàng, tôi sẽ sẵn sàng thử. Tôi đã tìm thấy các đề xuất thưa thớt thông qua các bình luận trên các diễn đàn của Oracle và một số câu hỏi SO WebView
phải chạy theo chủ đề riêng của nó theo thiết kế, vì vậy sau nhiều ngày thử nghiệm tôi đang tập trung năng lượng ở đó. Nếu giả định đó là sai, tuyệt vời. Tôi sẵn sàng cho bất kỳ đề xuất hợp lý nào khắc phục sự cố mà không có thời gian chờ tùy ý.
loadContent
phương thức hoặc khi tải URL tệp.