JavaFX - изменение размера простого приложения вызывает "мерцание" из-за очистки / перерисовки холста - PullRequest
1 голос
/ 10 июля 2020

У меня есть этот мертвенно простой проект, который в основном рисует aws черный кружок в середине холста сцены JavaFX и увеличивает его каждые 50 мс.

Вот мой контроллер:

public class PrimaryController {

    public StackPane theRootPane;
    public CanvasPane theCanvasPane;
    int i = 1;

    public void initialize() {
        theCanvasPane  = new CanvasPane(500, 300);
        theRootPane.getChildren().addAll(theCanvasPane);

        Timeline theTimeline =
            new Timeline(new KeyFrame(Duration.millis(50), actionEvent -> UpdateCanvas(i++)));
        theTimeline.setCycleCount(Timeline.INDEFINITE);
        theTimeline.play();
    }

    private void UpdateCanvas(int diameter) {
        theCanvasPane.DrawCircleAtCenterOfCanvas(diameter);
    }

Вот мой класс CanvasPane:

public class CanvasPane extends Pane {

private final Canvas theCanvas;
private GraphicsContext theGC;

public CanvasPane(double width, double height) {
    setWidth(width);
    setHeight(height);
    theCanvas = new Canvas(width, height);
    theGC = theCanvas.getGraphicsContext2D();

    getChildren().add(theCanvas);
    theCanvas.widthProperty().bind(this.widthProperty());
    theCanvas.heightProperty().bind(this.heightProperty());
    theCanvas.widthProperty().addListener(observable -> RedrawCanvas());
    theCanvas.heightProperty().addListener(observable -> RedrawCanvas());
}

private void RedrawCanvas() {
    ClearCanvas();
}

private void ClearCanvas() {
    theGC.clearRect(0, 0, theCanvas.widthProperty().doubleValue(),   theCanvas.heightProperty().doubleValue());
}

public void DrawCircleAtCenterOfCanvas(int diameter) {
    double centreX = theCanvas.widthProperty().doubleValue() / 2;
    double centreY = theCanvas.heightProperty().doubleValue() / 2;
    theGC.fillOval(centreX - diameter / 2.0, centreY - diameter / 2.0, diameter, diameter);
}

}

Наконец, вот мой класс App и мой .f xml

public class App extends Application {

    private static Scene scene;

    @Override
    public void start(Stage stage) throws IOException {
        scene = new Scene(loadFXML("primary"));
        stage.setScene(scene);
        //stage.setResizable(false);
        stage.show();
    }

    private static Parent loadFXML(String fxml) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
        return fxmlLoader.load();
    }

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

primary.f xml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<StackPane fx:id="theRootPane" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.xxx.PrimaryController" />

Он отлично работает, пока я не изменю размер окна, после чего холст перерисовывается, очищая его и затем рисуя новый большой круг на холсте. Эта «очистка» холста представляет собой мерцание при изменении размера формы.

Как лучше это сделать? Я возился с JavaFX после изучения Java и знакомства с пользовательским интерфейсом и анимацией. Я думаю, что холст - это не путь к go ...

Любой совет будет очень признателен.

1 Ответ

1 голос
/ 10 июля 2020

Вероятно, лучше обновить холст в ChangeListener, а не в InvalidationListener, что приведет к меньшему количеству redr aws. В любом случае вы должны либо:

  1. убедиться, что вы перерисовываете круг при изменении размера холста (с вашим текущим кодом вы очищаете холст, как только он меняет размер, но не перерисуйте круг до следующего ключевого кадра, чтобы между ними осталось несколько пустых холстов):

     public class CanvasPane extends Pane {
    
         private final Canvas theCanvas;
         private GraphicsContext theGC;
    
         private int currentDiameter ;
    
         public CanvasPane(double width, double height) {
             setWidth(width);
             setHeight(height);
             theCanvas = new Canvas(width, height);
             theGC = theCanvas.getGraphicsContext2D();
    
             getChildren().add(theCanvas);
             theCanvas.widthProperty().bind(this.widthProperty());
             theCanvas.heightProperty().bind(this.heightProperty());
             theCanvas.widthProperty().addListener((obs, oldWidth, newWidth) -> redrawCanvas());
             theCanvas.heightProperty().addListener((obs, oldHeight, newHeight) -> redrawCanvas());
         }
    
         public void increaseDiameter() {
             currentDiameter++;
             redrawCanvas();
         }
    
         private void redrawCanvas() {
             clearCanvas();
             drawCircleAtCenterOfCanvas();
         }
    
         private void clearCanvas() {
             theGC.clearRect(0, 0, theCanvas.widthProperty().doubleValue(), theCanvas.heightProperty().doubleValue());
         }
    
         public void drawCircleAtCenterOfCanvas() {
             currentDiameter = currentDiameter ;
             double centreX = theCanvas.widthProperty().doubleValue() / 2;
             double centreY = theCanvas.heightProperty().doubleValue() / 2;
             theGC.fillOval(centreX - currentDiameter / 2.0, centreY - currentDiameter / 2.0, currentDiameter, currentDiameter);
         }
     }
    

    и

    public class PrimaryController {
    
         @FXML
         private StackPane theRootPane;
         @FXML
         private CanvasPane theCanvasPane;
    
         public void initialize() {
             theCanvasPane  = new CanvasPane(500, 300);
             theRootPane.getChildren().addAll(theCanvasPane);
    
             Timeline theTimeline =
                 new Timeline(new KeyFrame(Duration.millis(50), actionEvent -> updateCanvas()));
             theTimeline.setCycleCount(Timeline.INDEFINITE);
             theTimeline.play();
         }
    
         private void updateCanvas() {
             theCanvasPane.increaseDiameter();
         }
     }
    
  2. или (возможно, лучше, и намного проще для этого примера) используйте Circle вместо холста:

     public class PrimaryController {
    
         @FXML
         private StackPane theRootPane;
    
         private Circle circle ;
    
         public void initialize() {
    
             circle = new Circle();
             theRootPane.getChildren().addAll(circle);
    
             Timeline theTimeline =
                 new Timeline(new KeyFrame(Duration.millis(50), actionEvent -> updateCircle()));
             theTimeline.setCycleCount(Timeline.INDEFINITE);
             theTimeline.play();
         }
    
         private void updateCircle() {
             circle.setRadius(circle.getRadius()+0.5);
         }
     }
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...