2017-12-20 11 views
12

は私がのJavaFXのWebViewにとのようなもので、スクリーンショットを撮るしばらく後にウェブサイトをロードしています:別のタブのために表示されない場合でも、JavaFXのWebviewレンダーを作成するにはどうすればよいですか?

WritableImage image = webView.snapshot(null, null); 

は私が正常に動作しているのWebViewで探していた場合、それがあることによって隠されていた場合(私はそれを隠す他のケースについてはわからない)、その後、スクリーンショットは適切なサイトのものですが、完全に空白です。

WebViewが表示されなくてもレンダリングさせるにはどうすればよいですか?

この間、webView.isVisible()は真です。

私はそこisTreeReallyVisible()呼ばWebViewでの方法だと、現在それが含まれていた:returnステートメント内の他のすべての要因が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; 
} 

impl_isTreeVisible()が偽です(本当です)。その方法はNode上で、次のようになります。私は私の独自の実装を提供するために、impl_treeVisibleProperty()をオーバーライドしている可能性が

/** 
* @treatAsPrivate implementation detail 
* @deprecated This is an internal API that is not intended for use and will be removed in the next version 
*/ 
@Deprecated 
public final boolean impl_isTreeVisible() { 
    return impl_treeVisibleProperty().get(); 
} 

/** 
* @treatAsPrivate implementation detail 
* @deprecated This is an internal API that is not intended for use and will be removed in the next version 
*/ 
@Deprecated 
protected final BooleanExpression impl_treeVisibleProperty() { 
    if (treeVisibleRO == null) { 
     treeVisibleRO = new TreeVisiblePropertyReadOnly(); 
    } 
    return treeVisibleRO; 
} 

が、WebViewは最終的なもので、そう、私はそれを継承することはできません。

最小化(アイコン化)または非表示のタブ上の別の全く異なる状況は、ステージを完全に非表示にすることです(トレイバーで実行中)。そのモードでは、レンダリングを行うことができても、WebViewはサイズ変更されません。私はwebView.resize()と呼んでから、スクリーンショットを撮って、スクリーンショットは適切なサイズですが、実際にレンダリングされるページのサイズは、WebViewです。

private void addToSceneDirtyList() { 
    Scene s = getScene(); 
    if (s != null) { 
     s.addToDirtyList(this); 
     if (getSubScene() != null) { 
      getSubScene().setDirty(this); 
     } 
    } 
} 

隠しモードでは、getScene()戻っそれはショーであることだと何が起こるかとは異なりnull、:

が示され、隠された段階で、このサイジング動作のデバッグ、私は最終的に我々は含まれていNode.addToSceneDirtyList()に着くことがわかりました。つまり、s.addToDirtyList(this)は呼び出されません。これが適切にサイズ変更されない理由があるかどうかはわかりません。

これについてのバグは、非常に古いものです:https://bugs.openjdk.java.net/browse/JDK-8087569ですが、それはすべての問題だとは思いません。

私はJava 1.8.0_151でこれをやっています。私はWebKitがアップグレードされたと私が理解しているように、動作が違うかどうかを確認するために9.0.1を試しましたが、同じではありません。

+0

タブペインのスキンがどのように実装されているのか覚えていませんが、あなたが記述したシナリオのシーンにWebビューが存在しない可能性があります。この場合、スナップショットは機能しません(ただし、スナップショットを撮っている間に一時的にWebビューをシーンに追加できます)。まだシーンの一部であれば、私は考えていません... –

+3

@James_Dは考えていません...終焉。 – Mordechai

+1

ヘルプがあれば見てください。https://stackoverflow.com/questions/40642573/trying-to-render-javafx-webview-to-offscreen-buffer-or-fbo – AsifAli72090

答えて

-1

私は、このコマンドを使用して、適切な瞬間に、ページをリロード検討する:

webView.getEngine().reload(); 

また、それは、私はメモリに画像を保存検討する動作しない場合はこの方法でsnapShot

をパラメータSnapshotParametersを変更しようWebViewが画面上でレンダリングされているとき

+0

適切な瞬間は何ですか? WebViewが画面に表示されないことがあります。 – Pablo

3

パブロの問題を再現する:https://github.com/johanwitters/stackoverflow-javafx-webview

パブロは、WebViewをオーバーライドしていくつかの方法を調整することを提案しました。それはそれが最終的なクラスと私的なメンバーであるので、それは動作しません。代わりに、javassistを使用してメソッドの名前を変更し、コードを実行したいコードに置き換えました。以下に示すように、handleStagePulseメソッドの内容を「置換」しました。

public class WebViewChanges { 
// public static String MY_WEBVIEW_CLASSNAME = WebView.class.getName(); 

    public WebView newWebView() { 
     createSubclass(); 
     return new WebView(); 
    } 

    // https://www.ibm.com/developerworks/library/j-dyn0916/index.html 
    boolean created = false; 
    private void createSubclass() { 
     if (created) return; 
     created = true; 
     try 
     { 
      String methodName = "handleStagePulse"; 

      // get the super class 
      CtClass webViewClass = ClassPool.getDefault().get("javafx.scene.web.WebView"); 

      // get the method you want to override 
      CtMethod handleStagePulseMethod = webViewClass.getDeclaredMethod(methodName); 

      // Rename the previous handleStagePulse method 
      String newName = methodName+"Old"; 
      handleStagePulseMethod.setName(newName); 

      // mnew.setBody(body.toString()); 
      CtMethod newMethod = CtNewMethod.copy(handleStagePulseMethod, methodName, webViewClass, null); 
      String body = "{" + 
        " " + Scene.class.getName() + ".impl_setAllowPGAccess(true);\n" + 
        " " + "final " + NGWebView.class.getName() + " peer = impl_getPeer();\n" + 
        " " + "peer.update(); // creates new render queues\n" + 
//     " " + "if (page.isRepaintPending()) {\n" + 
        " " + " impl_markDirty(" + DirtyBits.class.getName() + ".WEBVIEW_VIEW);\n" + 
//     " " + "}\n" + 
        " " + Scene.class.getName() + ".impl_setAllowPGAccess(false);\n" + 
        "}\n"; 
      System.out.println(body); 
      newMethod.setBody(body); 
      webViewClass.addMethod(newMethod); 


      CtMethod isTreeReallyVisibleMethod = webViewClass.getDeclaredMethod("isTreeReallyVisible"); 
     } 
     catch (Exception e) 
     { 
      e.printStackTrace(); 
     } 
    } 
} 

このスニペットは、2つのタブを開くWebViewSampleから呼び出されます。 1つは「スナップショット」ボタン、もう1つはWebViewです。 Pabloが指摘するように、WebViewのタブは再現可能な第2のタブである必要があります。

package com.johanw.stackoverflow; 

import javafx.application.Application; 
import javafx.embed.swing.SwingFXUtils; 
import javafx.event.EventHandler; 
import javafx.geometry.HPos; 
import javafx.geometry.Pos; 
import javafx.geometry.VPos; 
import javafx.scene.Group; 
import javafx.scene.Node; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.Label; 
import javafx.scene.control.Tab; 
import javafx.scene.control.TabPane; 
import javafx.scene.image.WritableImage; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.layout.*; 
import javafx.scene.paint.Color; 
import javafx.scene.web.WebEngine; 
import javafx.scene.web.WebView; 
import javafx.stage.Stage; 

import javax.imageio.ImageIO; 
import java.awt.image.RenderedImage; 
import java.io.File; 
import java.io.IOException; 


public class WebViewSample extends Application { 
    private Scene scene; 
    private TheBrowser theBrowser; 

    private void setLabel(Label label) { 
     label.setText("" + theBrowser.browser.isVisible()); 
    } 

    @Override 
    public void start(Stage primaryStage) { 
     primaryStage.setTitle("Tabs"); 
     Group root = new Group(); 
     Scene scene = new Scene(root, 400, 250, Color.WHITE); 

     TabPane tabPane = new TabPane(); 

     BorderPane borderPane = new BorderPane(); 

     theBrowser = new TheBrowser(); 
     { 
      Tab tab = new Tab(); 
      tab.setText("Other tab"); 

      HBox hbox0 = new HBox(); 
      { 
       Button button = new Button("Screenshot"); 
       button.addEventHandler(MouseEvent.MOUSE_PRESSED, 
         new EventHandler<MouseEvent>() { 
          @Override 
          public void handle(MouseEvent e) { 
           WritableImage image = theBrowser.getBrowser().snapshot(null, null); 
           File file = new File("test.png"); 
           RenderedImage renderedImage = SwingFXUtils.fromFXImage(image, null); 
           try { 
            ImageIO.write(
              renderedImage, 
              "png", 
              file); 
           } catch (IOException e1) { 
            e1.printStackTrace(); 
           } 

          } 
         }); 
       hbox0.getChildren().add(button); 
       hbox0.setAlignment(Pos.CENTER); 
      } 

      HBox hbox1 = new HBox(); 
      Label visibleLabel = new Label(""); 
      { 
       hbox1.getChildren().add(new Label("webView.isVisible() = ")); 
       hbox1.getChildren().add(visibleLabel); 
       hbox1.setAlignment(Pos.CENTER); 
       setLabel(visibleLabel); 
      } 
      HBox hbox2 = new HBox(); 
      { 
       Button button = new Button("Refresh"); 
       button.addEventHandler(MouseEvent.MOUSE_PRESSED, 
         new EventHandler<MouseEvent>() { 
          @Override 
          public void handle(MouseEvent e) { 
           setLabel(visibleLabel); 
          } 
         }); 
       hbox2.getChildren().add(button); 
       hbox2.setAlignment(Pos.CENTER); 
      } 
      VBox vbox = new VBox(); 
      vbox.getChildren().addAll(hbox0); 
      vbox.getChildren().addAll(hbox1); 
      vbox.getChildren().addAll(hbox2); 
      tab.setContent(vbox); 
      tabPane.getTabs().add(tab); 
     } 
     { 
      Tab tab = new Tab(); 
      tab.setText("Browser tab"); 
      HBox hbox = new HBox(); 
      hbox.getChildren().add(theBrowser); 
      hbox.setAlignment(Pos.CENTER); 
      tab.setContent(hbox); 
      tabPane.getTabs().add(tab); 
     } 

     // bind to take available space 
     borderPane.prefHeightProperty().bind(scene.heightProperty()); 
     borderPane.prefWidthProperty().bind(scene.widthProperty()); 

     borderPane.setCenter(tabPane); 
     root.getChildren().add(borderPane); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

    public static void main(String[] args){ 
     launch(args); 
    } 
} 

class TheBrowser extends Region { 

    final WebView browser; 
    final WebEngine webEngine; 

    public TheBrowser() { 
     browser = new WebViewChanges().newWebView(); 
     webEngine = browser.getEngine(); 
     getStyleClass().add("browser"); 
     webEngine.load("http://www.google.com"); 
     getChildren().add(browser); 

    } 
    private Node createSpacer() { 
     Region spacer = new Region(); 
     HBox.setHgrow(spacer, Priority.ALWAYS); 
     return spacer; 
    } 

    @Override protected void layoutChildren() { 
     double w = getWidth(); 
     double h = getHeight(); 
     layoutInArea(browser,0,0,w,h,0, HPos.CENTER, VPos.CENTER); 
    } 

    @Override protected double computePrefWidth(double height) { 
     return 750; 
    } 

    @Override protected double computePrefHeight(double width) { 
     return 500; 
    } 

    public WebView getBrowser() { 
     return browser; 
    } 

    public WebEngine getWebEngine() { 
     return webEngine; 
    } 
} 

私はパブロの問題を修正することに成功していませんが、javassistを使用することをお勧めします。

私は確信している:あなたは、スナップショットの論理的な実装で行く場合は、画面上に表示されている唯一のものは、スナップショットとして撮影している...

+0

上記以外にも、最後のメソッドをオーバーライドしたい場合に備えて、これを追加したいと思います。https://stackoverflow.com/questions/748362/override-java-final-methods -via-reflection-or-other-means –

+0

もちろん......... Webビューは表示されていないときはレンダリングされません。ブラウザを2番目のタブに置こうとしましたが、決してそれを選択しませんでした。まだ画像を出力しますか? – Mordechai

+0

あなたのコードはまだテストされていませんが、私の質問で指摘しておかなければならないことと、私が行っていることとの大きな違いは、スクリーンショットを撮る前にタブを表示していないことです。 2番目のタブのWebビューにページをロードし、最初のタブにボタンがあり、2番目のタブが表示されない場合は、私の状況に近づきます。 – Pablo

-1

継続します。 Webビューのスナップショットを作成するには、スナップショットを作成する直前にビューがレンダリングされているタブをクリックして、自動的に表示させるか、または、タブを手動でクリックしてスクリーンショットを撮ることもできます。 APIは、非表示部分のスナップショットを論理的に隠すことはできないと考えています。 スナップショットを取得するためのコードは、既にご利用いただけます。クリックするか、次のようなタブをロードします。

selectionChangedListenerを追加することも、スナップショットの直前にロードすることもできます。

addItemTab.setOnSelectionChanged(event -> loadTabBasedFXML(addItemTab, "/view/AddItem.fxml")); 

private void loadTabBasedFXML(Tab tab, String fxmlPath) { 
     try { 
      AnchorPane anchorPane = FXMLLoader.load(this.getClass().getResource(fxmlPath)); 
      tab.setContent(anchorPane); 
     } catch (IOException e) { 
     } 
    } 
+0

詳細についてはhttps://stackoverflow.com/help/how-to-answerをご覧ください。 –

+0

私は明確な答えとして期待されるすべての詳細がそこにあると思う。コメントを指す前に具体的にしてください。 –

+0

隠し要素のスナップショットができないというあなたの主張は間違っています。場面にない場合でもキャプチャすることができます(可視プロパティが明示的にfalseに設定されていない限り)。何らかの理由でWebビューがここでは例外であり、それが問題です。 – Mordechai

関連する問題