Bir o JavaFX dokümanlar devlet WebView
ne zaman hazır Worker.State.SUCCEEDED
ulaşıldığında Bir süre (yani beklemek sürece, bununla birlikte Animation
, Transition
, PauseTransition
vs.), boş bir sayfa oluşturulur.
Bu WebView içinde bir yakalama için hazır bir olay olduğunu gösterir, ama nedir?
Orada GitHub üzerinde 7.000 kod parçacıklarını kullanımıSwingFXUtils.fromFXImage
ancak bunların çoğu ya alakasız gibi görünen WebView
, interaktif (insan maskeleri yarış durumu) veya (2,000ms için 100ms yerden) keyfi Geçişleri kullanın.
Denedim:
Boyutları
changed(...)
içinden dinlemeWebView
( bu şeyleri izleyebilen yükseklik ve genişlik özellikleriDoubleProperty
uygularObservableValue
)- Vi Uygun değil. Bazen, değerin boya rutininden ayrı olarak değiştiği ve kısmi içeriğe neden olduğu görülmektedir.
runLater(...)
FX Application Thread'da her şeyi ve her şeyi körü körüne anlatmak .- TechniquesBirçok teknik bunu kullanıyor, ancak kendi birim testlerim (diğer geliştiricilerin bazı harika geri bildirimleri gibi), olayların genellikle doğru iş parçacığında olduğunu ve bu çağrının gereksiz olduğunu açıklıyor. Aklıma gelen en iyi bazıları için çalışıyor kuyrukta yeterli bir gecikme ekler olduğunu.
Bir DOM dinleyici / tetikleyici veya JavaScript dinleyici / tetikleyici ekleme
WebView
- CaptureKapalı
SUCCEEDED
yakalamaya rağmen, her iki JavaScript ve DOM da düzgün yüklenmiş gibi görünüyor . DOM / JavaScript dinleyicileri yardımcı görünmüyor.
- CaptureKapalı
Ana FX iş parçacığını engellemeden bir
Animation
veyaTransition
etkin bir şekilde "uyku" kullanmak.- Approach️ Bu yaklaşım işe yarıyor ve gecikme yeterince uzunsa, birim testlerin% 100'üne kadar çıkabilir, ancak Geçiş süreleri , sadece tahmin ettiğimiz ve kötü tasarımın gelecekteki bir anı gibi görünüyor . Performanslı veya görev açısından kritik uygulamalar için bu, programcıyı hem kullanıcı için potansiyel olarak kötü bir deneyim olan hız veya güvenilirlik arasında bir denge kurmaya zorlar.
Aramak için iyi bir zaman ne zaman WebView.snapshot(...)
?
Kullanımı:
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.
*/
Kod Parçacığı:
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şkili:
- JavaFX WebView bileşenine yüklenen tam web sayfasının ekran görüntüsü, yalnızca görünür bölüm değil
- Sahnenin anlık görüntüsünü programlı olarak yakalayabilir miyim?
- Tüm sayfa ekran görüntüsü, Java
- JavaFX 2.0+ WebView / WebEngine web sayfasını bir görüntüye dönüştürür
- Javafx'te Sahne ve Sahne Yüksekliğini ve Genişliğini Ayarlama
- JavaFX: WebView kullanılırken sahne alanı nasıl yeniden boyutlandırılır?
- Tabelcell'e katıştırılmış Web görünümünün doğru boyutlandırılması
- 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
test edildi ve düzeltilmedi. Kabul etmiyorsanız lütfen kendiniz deneyin. Yanlış yaptığım için mutlu olurum, sorunu kapatırdı.
SUCCEEDED
(dinleyicinin FX iş parçacığına ateş ettiği) durumu uygun teknik olarak hazırlar. Sıradaki etkinlikleri göstermenin bir yolu varsa, denemekten memnun olurum. Oracle forumlarındaki yorumlar ve WebView
tasarımla kendi başlığında çalışması gereken bazı SO soruları aracılığıyla seyrek öneriler buldum , bu yüzden test günlerinden sonra orada enerjiye odaklanıyorum. Bu varsayım yanlışsa, harika. Sorunu keyfi bekleme süreleri olmadan düzelten makul önerilere açığım.
loadContent
yöntem kullanılırken veya bir dosya URL'si yüklenirken olur .