在表格单元格嵌入Webview的正确尺寸调整

7

此处此处所述,目前还没有简单的方法来确定WebView所需的高度,直到"RT-25005 Automatic preferred sizing of WebView"被实现。

是否有任何解决此问题的方法?我在SO或其他地方都找不到解决方案。然而,由于我认为这不是一个罕见的问题,所以需要有解决方法。有什么想法吗?

对于嵌入到舞台中的Webviews,我找到了以下解决方案(请参见此处):

webView.getEngine().executeScript(
    "window.getComputedStyle(document.body, null).getPropertyValue('height')"
);

或者

Double.parseDouble(webView.getEngine().executeScript("document.height").toString())

但是,这似乎不适用于嵌入到treecell中的Webviews,比如这里。在这种情况下,我总是得到太大的数字作为结果。

最小运行示例(包括jewelsea的建议):

import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.util.Callback;
import org.w3c.dom.Document;

public class TableViewSampleHTML extends Application {

    private final ObservableList<MyData> data = FXCollections.observableArrayList(new MyData(1L), new MyData(3L), new MyData(2L), new MyData(4L), new MyData(1L));

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

    @Override
    public void start(final Stage stage) {
        final Scene scene = new Scene(new Group());

        TableView<MyData> table = new TableView<>();
        table.setPrefHeight(700);

        final TableColumn<MyData, Long> nameCol = new TableColumn("So So");
        nameCol.setMinWidth(200);
        nameCol.setCellValueFactory(new PropertyValueFactory<>("i"));

        // Allow to display Textflow in Column
        nameCol.setCellFactory(new Callback<TableColumn<MyData, Long>, TableCell<MyData, Long>>() {
            @Override
            public TableCell<MyData, Long> call(TableColumn<MyData, Long> column) {
                return new TableCell<MyData, Long>() {
                    @Override
                    protected void updateItem(Long item, boolean empty) {
                        super.updateItem(item, empty);

                        if (item == null || empty) {

                            setText(null);
                            setGraphic(null);
                            setStyle("");

                        } else {

                            WebView webview = new WebView();
                            webview.setPrefWidth(700.0);
                            WebEngine engine = webview.getEngine();

                            String textHTML = new String(new char[item.intValue()]).replace("\0", " <b> bold </b> normal, ");
                         //   textHTML = "<body>" 
                           //         + textHTML + "</body>";
                            engine.loadContent(textHTML);


                           setGraphic(webview);



                            engine.documentProperty().addListener((obj, prev, newv) -> {

                                    String heightText = engine.executeScript(
                                         "window.getComputedStyle(document.body, null).getPropertyValue('height')"
                                    ).toString();

                                    System.out.println("heighttext: " + heightText);
                                    webview.setPrefHeight(Double.parseDouble(heightText.replace("px", "")));
                                    this.setPrefHeight(Double.parseDouble(heightText.replace("px", "")));
                                    setGraphic(webview);

                            });





                        }
                    }
                };
            }
        });

        table.setItems(data);
        table.getColumns().addAll(nameCol);

        ((Group) scene.getRoot()).getChildren().addAll(table);

        stage.setScene(scene);
        stage.show();
    }

    public static class MyData {

        private Long i;

        public MyData(Long i) {
            this.i = i;
        }

        public Long getI() {
            return i;
        }
    }

}

现在的输出结果是:

heighttext: 581px
heighttext: 581px

然而这些值似乎太大了。请看屏幕截图:

enter image description here

3个回答

3

已经取得了一些进展,现在可以更加真实地计算单元格高度。请查看以下相关的修改后的代码。

相关变化:

  • 似乎在执行JavaScript之前调用webview.setPrefHeight(-1);是必要的。
  • Javascript已被修改。没有看到很大的变化,但可能结果更加普遍。

待解决问题:

  • For some reason i still have to add + 15.0 to the calculated height. This is a hack. Seems like some additional lenght has to be considered somewhere.
  • Functionality on recalculation after resize of column has is not optimal. Using table.refresh() causes significant delay in rendering.

    public class TableViewSampleHTML extends Application {
    
    private final ObservableList<MyData> data = FXCollections.observableArrayList(new MyData(1L), new MyData(14L), new MyData(2L), new MyData(3L), new MyData(15L));
    
    public static void main(final String[] args) {
        launch(args);
    }
    
    @Override
    public void start(final Stage stage) {
        final Scene scene = new Scene(new Group(), 400, 800);
    
        TableView<MyData> table = new TableView<>();
    
        table.setPrefWidth(400);
        table.setPrefHeight(800);
    
        final TableColumn<MyData, Long> nameCol = new TableColumn("So So");
        final TableColumn<MyData, Long> col2 = new TableColumn("la la");
        nameCol.setPrefWidth(200);
        col2.setCellValueFactory(new PropertyValueFactory<>("i"));
        nameCol.setCellValueFactory(new PropertyValueFactory<>("i"));
        nameCol.widthProperty().addListener((ob,oldV,newV) -> {table.refresh();} );
    
        // Allow to display Textflow in Column
        nameCol.setCellFactory(new Callback<TableColumn<MyData, Long>, TableCell<MyData, Long>>() {
    
            @Override
            public TableCell<MyData, Long> call(TableColumn<MyData, Long> column) {
    
                return new TableCell<MyData, Long>() {
    
                    @Override
                    protected void updateItem(Long item, boolean empty) {
                        super.updateItem(item, empty);
    
                        if (item == null || empty) {
    
                            setText(null);
                            setGraphic(null);
                            setStyle("");
    
                        } else {
                            WebView webview = new WebView();
                            WebEngine engine = webview.getEngine();
    
                            webview.setPrefHeight(-1);   // <- Absolute must at this position (before calling the Javascript)
                            setGraphic(webview);
    
                            String textHTML = new String(new char[item.intValue()]).replace("\0", " <b> bold </b> normal, ");
                            textHTML = "<body>" 
                                    + textHTML + "</body>";
                            engine.loadContent(textHTML);
    
    
                            engine.documentProperty().addListener((obj, prev, newv) -> {
    
                            String heightText = engine.executeScript(   // <- Some modification, which gives moreless the same result than the original
                                    "var body = document.body,"
                                    + "html = document.documentElement;"
                                    + "Math.max( body.scrollHeight , body.offsetHeight, "
                                    + "html.clientHeight, html.scrollHeight , html.offsetHeight );"
                            ).toString();
    
                            System.out.println("heighttext: " + heightText);
                            Double height = Double.parseDouble(heightText.replace("px", "")) + 15.0;  // <- Why are this 15.0 required??
                            webview.setPrefHeight(height);
                            this.setPrefHeight(height);
                            });
                        }
    
                    }
                };
            }
        });
    
        table.setItems(data);
        table.getColumns().addAll(nameCol);
        table.getColumns().addAll(col2);
    
        ((Group) scene.getRoot()).getChildren().addAll(table);
    
        stage.setScene(scene);
        stage.show();
    }
    
    public static class MyData {
    
        private Long i;
    
        public MyData(Long i) {
            this.i = i;
        }
    
        public Long getI() {
            return i;
        }
    }
    }
    
现在的输出看起来像这样:

enter image description here


谢谢你的这段代码。HTML 的 body 标签默认带有上边距和左边距,所以你需要设置 topmargin=0 leftmargin=0 来去掉边距。(<body topmargin=0 leftmargin=0>) 现在为了没有垂直滚动条,你需要在这里加入额外的 10:Double height = Double.parseDouble(heightText.replace("px", "")) + 10.0; - Nadir Novruzov
我发现在我的 Mac 上计算出的高度漂移因子为 1.0054。将在其他机器上进行测试,但想分享一下,只需应用此乘数即可使尺寸完美。 - tresf

1
从您提供的示例中 (JavaFX webview, get document height) ,文档的高度是在文档的 ChangeListener 中计算的。
engine.documentProperty().addListener((prop, oldDoc, newDoc) -> {
    String heightText = engine.executeScript(
            "window.getComputedStyle(document.body, null).getPropertyValue('height')"
    ).toString();

    System.out.println("heighttext: " + heightText);
});

输出:

heighttext: 36px
heighttext: 581px
heighttext: 581px

在您提出的代码中,您没有基于ChangeListener执行高度检查。因此,在文档加载之前查询WebView文档的高度(这就是为什么您的代码返回零的原因)。

谢谢jewelsea。这让我更清楚了一些。现在数字不再是零了。但是它们现在似乎太高了。请检查我的编辑。 - BerndGit
抱歉BerndGit,我不知道如何获取更准确的WebView文档大小计数。 - jewelsea
没问题。谢谢反馈。我想我会发放赏金。 - BerndGit

0

基于BerndGirt的回答。

public class WebviewCellFactory<S,T> implements Callback<TableColumn<S,T>, TableCell<S,T>> {
    @Override
    public TableCell<S, T> call(TableColumn<S, T> column) {
        return new TableCell<S, T>() {
            @Override
            protected void updateItem(T item, boolean empty) {
                super.updateItem(item, empty);
                if (item == null || empty) {
                    setText(null);
                    setGraphic(null);
                    setStyle("");
                } else {
                    WebView webview = new WebView();
                    WebEngine engine = webview.getEngine();
                    webview.setPrefHeight(-1);   // <- Absolute must at this position (before calling the Javascript)
                    webview.setBlendMode(BlendMode.DARKEN);

                    setGraphic(webview);
                    engine.loadContent("<body topmargin=0 leftmargin=0 style=\"background-color: transparent;\">"+item+"</body>");
                    engine.documentProperty().addListener((obj, prev, newv) -> {
                        String heightText = engine.executeScript(   // <- Some modification, which gives moreless the same result than the original
                                "var body = document.body,"
                                        + "html = document.documentElement;"
                                        + "Math.max( body.scrollHeight , body.offsetHeight, "
                                        + "html.clientHeight, html.scrollHeight , html.offsetHeight );"
                        ).toString();
                        Double height = Double.parseDouble(heightText.replace("px", "")) + 10 ;  // <- Why are this 15.0 required??
                        webview.setPrefHeight(height);
                        this.setPrefHeight(height);
                    });
                }
            }
        };
    }
}

然后,您只需要设置

tableColumn.setCellFactory(new WebviewCellFactory());

如果有任何错误,请告诉我。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接