how can one detect a finished resizing operation i

2019-04-26 16:38发布

I have a stage, a scene and a WebView node. When I expand the window to a larger size - things get pretty sluggish due to WebView. What I want to do is fill the new space for WebView only when the resizing of the window has been finished (this is me releasing left mouse button on the resizable control/edge of the window). For now I can just set the max. size of this node to what it is by default - this will stop it from expansion. But how can I detect the actual event of a completed resizing operation on the window? With binding, I can verify that the resizing takes place - but it's instantaneous (properties for W & D change immediately w/o releasing LMB), while I only require an action when the LMB has been released. Suggestions?


I tried using an addEventFilter on the stage for Event.ANY, just to see if this event type is recognizable - sadly with no avail.

I've also stumbled upon this unanswered post.

3条回答
干净又极端
2楼-- · 2019-04-26 17:00

I am making an application where I have to periodically fetch satellite imagery (rather intensive), so I had to find a way to capture only the last event as well. I settled on spawning a thread to count down for some length of time before executing the intensive task and a resize-listener that resets the count. It seems more efficient to me than scheduling and unscheduling tasks hundreds of times.

Note I've also got some logic in here to capture the first window-size-change with System.currentTimeMillis();

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;

public class ResizeListener implements ChangeListener {
    long lastdragtime = System.currentTimeMillis();
    double xi, yi, dx, dy, wid, hei;
    GuiModel model;
    TimerThread timebomb;

    public ResizeListener(GuiModel model) {
        this.model = model;
        timebomb = new TimerThread(350);
        timebomb.start();
    }

    public void changed(ObservableValue observable, Object oldValue, Object newValue) {
        if (System.currentTimeMillis() - lastdragtime > 350) { //new drag
            xi = model.stage.getWidth();
            yi = model.stage.getHeight();
            model.snapshot = model.canvas.snapshot(null, null);
        }

        timebomb.active = true;//start timer
        timebomb.ms = timebomb.starttime;//reset timer

        wid = model.stage.getWidth()-72;
        hei = model.stage.getHeight()-98;
        dx = model.stage.getWidth() - xi;
        dy = model.stage.getHeight() - yi;
        if (dx < 0 && dy < 0) {
            model.canvas.setWidth(wid);
            model.canvas.setHeight(hei);
            model.graphics.drawImage(model.snapshot, -dx/2, -dy/2, wid, hei, 0, 0, wid, hei);
        } else if (dx < 0 && dy >= 0) {
            model.canvas.setWidth(wid);
            model.graphics.drawImage(model.snapshot, -dx/2, 0, wid, hei, 0, 0, wid, hei);
        } else if (dx >= 0 && dy < 0) {
            model.canvas.setHeight(hei);
            model.graphics.drawImage(model.snapshot, 0, -dy/2, wid, hei, 0, 0, wid, hei);
        }
        lastdragtime = System.currentTimeMillis();
    }

    private class TimerThread extends Thread {
        public final int starttime;//multiple of 25
        public int ms = 0;
        public boolean active = false;

        public TimerThread(int starttime) {
            this.setDaemon(true);
            this.starttime = starttime;
        }

        public void run() {
            while (true) {
                try {
                    Thread.sleep(25);
                } catch (InterruptedException x) {
                    break;
                }

                if (active) {
                    ms -= 25;
                    if (ms <= 0) {
                        active = false;
                        Platform.runLater(() -> {
                            model.canvas.setWidth(wid);
                            model.canvas.setHeight(hei);
                            model.fetchSatelliteImagery();
                            model.refresh();
                        });
                    }
                }
            }
        }
    }//end TimerThread class
}//end listener class
查看更多
啃猪蹄的小仙女
3楼-- · 2019-04-26 17:08

This answer is only applies if you are able to use an undecorated Stage for your application.

With an undecorated stage you can handle the resizing decorations and operations yourself; allowing access to the right hooks for handling the completion of a resize operation.

See the WindowResizeButton class in the Ensemble sample application source code for a demo of how to implement a resize handle for an undecorated stage. Modify this class to add a setOnMouseReleased handler and implement your webView size modifications in there.

I do wonder why this is necessary though as I haven't really had any sluggishness resizing a Window containing WebView - perhaps this is due to your app having different content usage in the WebView from what I have been using.

查看更多
Explosion°爆炸
4楼-- · 2019-04-26 17:11

This is not a direct answer to your question. If I understood the sluggishness correctly you are facing somekind weird flashings and renderings. To reduce the sluggishness the size of the webView can be updated manually and more sparsely:

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.layout.PaneBuilder;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.util.Duration;

public class KSO_Demo extends Application {

    @Override
    public void start(Stage primaryStage) {

        final WebView webView = new WebView();
        webView.getEngine().loadContent("<div style='background-color: gray; height: 100%'>Some content</div>");

        final Pane pane = PaneBuilder.create().children(webView).style("-fx-border-color: blue").build();

        final Timeline animation = new Timeline(
                new KeyFrame(Duration.seconds(.5),
                new EventHandler<ActionEvent>() {

                    @Override
                    public void handle(ActionEvent actionEvent) {
                        webView.setPrefSize(pane.getWidth(), pane.getHeight());
                    }
                }));
        animation.setCycleCount(1);

        primaryStage.setScene(new Scene(pane, 300, 250));

        primaryStage.widthProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> arg0, Number arg1, Number arg2) {
                animation.play();
            }
        });

        primaryStage.heightProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> arg0, Number arg1, Number arg2) {
                animation.play();
            }
        });
        primaryStage.show();
    }

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

The webView is in the Pane which does not layout its children automatically. The webView's size updating is delayed for .5 second ignoring updates in interval.

查看更多
登录 后发表回答