A가 있다는 자바 FX 문서 상태 WebView
때 준비가 Worker.State.SUCCEEDED
도달 한 동안 (즉, 대기하지 않는 한, 그러나 Animation
, Transition
, PauseTransition
, 등), 빈 페이지가 렌더링됩니다.
이것은 WebView 내부에서 캡처 준비를하는 이벤트가 있음을 시사하지만, 무엇입니까?
GitHubSwingFXUtils.fromFXImage
에는 7,000 개가 넘는 코드 스 니펫이 있지만 대부분은 관련이 없으며 WebView
대화 형 (인간 마스크 경쟁 조건) 또는 임의의 전환 (100ms ~ 2,000ms)을 사용합니다.
난 노력 했어:
차원
changed(...)
내에서 청취WebView
(높이 및 너비 속성DoubleProperty
구현ObservableValue
,이를 모니터링 할 수 있음)- 🚫 생존하지 않습니다. 때로는 값이 페인트 루틴과 별도로 변경되어 부분 내용물이 생성되는 것처럼 보입니다.
runLater(...)
FX Application Thread에서 맹목적으로 모든 것을 말하십시오 .- 🚫 많은 기술들이 이것을 사용하지만, 내 자신의 단위 테스트 (및 다른 개발자들의 훌륭한 피드백)는 이벤트가 이미 올바른 스레드에 있고이 호출이 중복된다고 설명합니다. 내가 생각할 수있는 최선은 큐잉을 통해 지연 될 수 있다는 것입니다.
에 DOM 리스너 / 트리거 또는 JavaScript 리스너 / 트리거 추가
WebView
- 🚫
SUCCEEDED
빈 캡처에도 불구하고 JavaScript를 호출하면 JavaScript와 DOM이 모두 제대로로드 된 것 같습니다 . DOM / JavaScript 리스너는 도움이되지 않는 것 같습니다.
- 🚫
사용
Animation
또는Transition
주요 FX 스레드를 차단하지 않고 효율적으로 "절전"에 있습니다.- ⚠️이 접근법은 효과가 있으며 지연 시간이 충분히 길면 최대 100 %의 단위 테스트를 생성 할 수 있지만 전환 시간은 우리가 추측 하고 나쁜 설계에 불과한 미래의 순간으로 보입니다 . 성능이 뛰어나거나 업무상 중요한 응용 프로그램의 경우 프로그래머가 잠재적으로 나쁜 경험 인 속도 나 안정성을 절충해야합니다.
전화하기 좋은 시간은 언제 WebView.snapshot(...)
입니까?
용법:
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.
*/
코드 스 니펫 :
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();
}
}
관련 :
- JavaFX WebView 구성 요소에로드 된 전체 웹 페이지의 스크린 샷
- 프로그래밍 방식으로 장면의 스냅 샷을 캡처 할 수 있습니까?
- 전체 페이지 스크린 샷, Java
- JavaFX 2.0 이상 WebView / WebEngine은 웹 페이지를 이미지로 렌더링
- javafx에서 스테이지 및 장면의 높이와 너비 설정
- JavaFX : webview를 사용할 때 스테이지 크기를 조정하는 방법
- Tabelcell에 포함 된 Webview의 올바른 크기 조정
- https://docs.oracle.com/javase/8/javafx/embedded-browser-tutorial/add-browser.htm#CEGDIBBI
- http://docs.oracle.com/javafx/2/swing/swing-fx-interoperability.htm#CHDIEEJE
- https://bugs.openjdk.java.net/browse/JDK-8126854
- https://bugs.openjdk.java.net/browse/JDK-8087569
Platform.runLater
테스트되었으며 수정하지 않습니다. 동의하지 않으면 직접 사용해보십시오. 나는 잘못되어서 기쁘다. 문제가 끝났다.
SUCCEEDED
는 리스너가 FX 스레드에서 발생 하는 상태가 적절한 기술입니다. 대기중인 이벤트를 표시하는 방법이 있다면 시도해 볼 수 있습니다. 오라클 포럼에 대한 의견과 WebView
디자인에 의해 자체 스레드에서 실행되어야하는 SO 질문에 대한 희소 한 제안을 찾았 으므로 테스트 후 며칠 동안 에너지에 집중하고 있습니다. 그 가정이 틀렸다면 위대합니다. 나는 임의의 대기 시간없이 문제를 해결하는 합리적인 제안을 할 수 있습니다.
loadContent
메소드를 사용하거나 파일 URL을로드 할 때만 발생 합니다.