同步两个JavaFx WebView

时间:2018-03-27 09:39:12

标签: java javafx webview javafx-8

我使用两个WebView来显示两种版本的HTML格式文本以进行比较。两者显示相同数量的文本(相同行数和相应行总长度相同)。

当显示的文本超出节点的大小时,WebView将获得滚动条。当然我希望这些滚动条同步滚动,以便始终显示相应的文本。

为了提供一个最小,完整和可验证的示例,我将代码修改为:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.layout.GridPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class SynchronizedWebViewsTest extends Application {

  protected class DifferencePanel extends GridPane {
    private WebView actualPane;
    private WebView expectedPane;

    public DifferencePanel() {
      setPadding(new Insets(20, 20, 20, 20));
      actualPane = new WebView();
      expectedPane = new WebView();
      setResultPanes();
      addRow(0, actualPane, expectedPane);
    }

    public void setHtml(WebView webView) {
      Platform.runLater(() -> {
        webView.getEngine().loadContent(createHtml());
      });
    }

    public void synchronizeScrolls() {
      final ScrollBar actualScrollBarV = (ScrollBar)actualPane.lookup(".scroll-bar:vertical");
      final ScrollBar expectedScrollBarV = (ScrollBar)expectedPane.lookup(".scroll-bar:vertical");
      actualScrollBarV.valueProperty().bindBidirectional(expectedScrollBarV.valueProperty());
      final ScrollBar actualScrollBarH = (ScrollBar)actualPane.lookup(".scroll-bar:horizontal");
      final ScrollBar expectedScrollBarH = (ScrollBar)expectedPane.lookup(".scroll-bar:horizontal");
      actualScrollBarH.valueProperty().bindBidirectional(expectedScrollBarH.valueProperty());
    }

    private String createHtml() {
      StringBuilder sb = new StringBuilder(1000000);
      for (int i = 0; i < 100; i++) {
        sb.append(String.format("<nobr>%03d %2$s%2$s%2$s%2$s%2$s%2$s%2$s%2$s</nobr><br/>\n",
                                Integer.valueOf(i), "Lorem ipsum dolor sit amet "));
      }
      return sb.toString();
    }

    private void setResultPanes() {
      setHtml(actualPane);
      setHtml(expectedPane);
    }
  } // ---------------------------- end of DifferencePanel ----------------------------


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

  @Override
  public void start(Stage dummy)  throws Exception {
    Stage stage = new Stage();
    stage.setTitle(this.getClass().getSimpleName());
    DifferencePanel differencePanel = new DifferencePanel();
    Scene scene = new Scene(differencePanel);
    stage.setScene(scene);
    differencePanel.synchronizeScrolls();
    stage.showAndWait();
  }
}

我尝试使用添加侦听器:

actualScrollBarV.onScrollFinishedProperty().addListener(event -> {
  System.out.println(event);
});

但是从不调用听众。

我使用的是Java版本1.8.0_92,但是使用版本9.0.4,我得到了相同的结果。

有人可以告诉我,我在这里失踪了吗?

2 个答案:

答案 0 :(得分:1)

我会发表评论,但遗憾的是我没有足够的声誉。

您是否尝试过以下解决方案?在值更改事件上创建侦听器,而不是绑定。 Synchronizing two scroll bars JavaFX

答案 1 :(得分:1)

我无法使ScrollBar方法正常工作。事实证明,实际上是调用了侦听器(lambdas中的断点并不总是有效?)。设置另一个WebView的滚动条值没有倾斜更改滚动条或视图端口。 : - (

WebView中的事件有些奇怪;这可能是因为有一个本地图书馆......

但是,使用WebView的事件处理程序的方法有效。每个WebView的事件处理程序只使用同步字段WebView将所有事件镜像到另一个Boolean scrolling,以避免递归。

import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

    public class SynchronizedWebViewsTest extends Application {

      protected class DifferencePanel extends GridPane {
        private Boolean scrolling = Boolean.FALSE;
        private WebView actualPane;
        private WebView expectedPane;

        public DifferencePanel() {
          setPadding(new Insets(20, 20, 20, 20));
          actualPane = new WebView();
          expectedPane = new WebView();
          setResultPanes();
          addRow(0, actualPane, expectedPane);
        }

        public void setHtml(WebView webView) {
          Platform.runLater(() -> {
            webView.getEngine().loadContent(createHtml());
          });
        }

        public void synchronizeScrolls() {
          wireViews(actualPane, expectedPane);
          wireViews(expectedPane, actualPane);
        }

        private void wireViews(WebView webView, WebView otherWebView) {
          webView.addEventHandler(Event.ANY, event -> {
            if (!scrolling.booleanValue()) {
              synchronized (scrolling) {
                scrolling = Boolean.TRUE;
                if (event instanceof MouseEvent) {
                  MouseEvent mouseEvent = (MouseEvent) event;
                  Point2D origin = webView.localToScreen(0, 0);
                  Point2D otherOrigin = otherWebView.localToScreen(0, 0);
                  double offsetX = otherOrigin.getX() - origin.getX();
                  double offsetY = otherOrigin.getY() - origin.getY();
                  double x = mouseEvent.getX();
                  double y = mouseEvent.getY();
                  double screenX = mouseEvent.getScreenX() + offsetX;
                  double screenY = mouseEvent.getScreenY() + offsetY;
                  MouseButton button = mouseEvent.getButton();
                  int clickCount = mouseEvent.getClickCount();
                  boolean shiftDown = mouseEvent.isShiftDown();
                  boolean controlDown = mouseEvent.isControlDown();
                  boolean altDown = mouseEvent.isAltDown();
                  boolean metaDown = mouseEvent.isMetaDown();
                  boolean primaryButtonDown = mouseEvent.isPrimaryButtonDown();
                  boolean middleButtonDown = mouseEvent.isMiddleButtonDown();
                  boolean secondaryButtonDown = mouseEvent.isSecondaryButtonDown();
                  boolean synthesized = mouseEvent.isSynthesized();
                  boolean popupTrigger = mouseEvent.isPopupTrigger();
                  boolean stillSincePress = mouseEvent.isStillSincePress();
                  MouseEvent otherMouseEvent =
                      new MouseEvent(otherWebView, otherWebView, mouseEvent.getEventType(), x, y, screenX,
                                     screenY, button, clickCount, shiftDown, controlDown, altDown, metaDown,
                                     primaryButtonDown, middleButtonDown, secondaryButtonDown, synthesized,
                                     popupTrigger, stillSincePress, null);
                  otherWebView.fireEvent(otherMouseEvent);
                }
                else {
                  otherWebView.fireEvent(event.copyFor(otherWebView, otherWebView));
                }
                scrolling = Boolean.FALSE;
              }
            }
          });
        }

        private String createHtml() {
          StringBuilder sb = new StringBuilder(1000000);
          for (int i = 0; i < 100; i++) {
            sb.append(String.format("<nobr>%03d %2$s%2$s%2$s%2$s%2$s%2$s%2$s%2$s</nobr><br/>\n",
                                    Integer.valueOf(i), "Lorem ipsum dolor sit amet "));
          }
          return sb.toString();
        }

        private void setResultPanes() {
          setHtml(actualPane);
          setHtml(expectedPane);
        }
      }


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

      @Override
      public void start(Stage dummy)  throws Exception {
        Stage stage = new Stage();
        stage.setTitle(this.getClass().getSimpleName());
        DifferencePanel differencePanel = new DifferencePanel();
        Scene scene = new Scene(differencePanel);
        stage.setScene(scene);
        differencePanel.synchronizeScrolls();
        stage.showAndWait();
      }
    }

这适用于我感兴趣的所有输入法:

  • 键盘:PageUp,PageDown,全部4个箭头键,“空格键”(与PageDown相同)和shift-“空格键”(与PageUp相同),Home和End
  • 鼠标滚轮:RollDown和RollUp以及shift-RollUp(向左滚动)和shift-RollDown(向右滚动)
  • 使用鼠标单击或拖动滚动条。
  • 使用鼠标选择当前视口外的文字。

镜像鼠标事件具有额外的好处,即在两个WebView中都选择了文本。