2017-04-04 87 views
1

简介:JavaFX从画布中删除最后绘制的物体

我创建了一个画布,我的目标是在您点击画布的地方创建圆圈。但我也希望能够删除我绘制的圈子或至少最后绘制的圈子。在圆圈背景颜色相同的地方重画不是一个好的解决方案。因为如果圈子下还有其他东西,他们也会被删除。例如,在这个应用程序中,2个圆可能有交点,当您尝试删除第2个圆时,这些交点将是空的,这意味着也会删除第一个圆的某个部分。所以我想创建第二个画布。每次我在绘制圆圈前点击画布上的某处,我都会将主画布分配给备份画布。当我想删除最后绘制的对象时,我可以加载备份画布。

现在我试图做我上面解释过的,不能按照我打算做的那样工作。事实上,我甚至在其他事情上看到了一些奇怪的现象(至少对我来说)。

我所做的是基本上创建2个相同大小的画布。

public class Main extends Application { 
    double ldx, ldy, diameter; 
    Canvas lastCanvas = new Canvas(800,600); 
    Canvas pane1 = new Canvas(800,600); 

    @Override 
    public void start(Stage primaryStage) throws Exception{ 
     System.out.println("Hello"); 
     HBox root = new HBox(); 

     GraphicsContext gc = pane1.getGraphicsContext2D(); 
     pane1.setOnMouseClicked(new EventHandler<MouseEvent>() { 
      @Override 
      public void handle(MouseEvent event) { 
       // Draw circle around x,y 
       lastCanvas = pane1; 
       diameter = 25.0; 
       ldx = event.getSceneX() - (diameter/2); 
       ldy = event.getSceneY() - (diameter/2); 
       gc.strokeOval(ldx, ldy, diameter, diameter); 
       System.out.println("Clicked"); 
      } 
     }); 
     VBox pane2 = new VBox(10); 
     pane2.setPrefSize(224,600); 
     Button button1 = new Button("Clear Last Drawn"); 
     button1.setOnMouseClicked(new EventHandler<MouseEvent>() { 
      @Override 
      public void handle(MouseEvent event) { 
       pane1 = lastCanvas; 
       System.out.println("Last canvas loaded"); 
      } 
     }); 
     pane2.getChildren().addAll(button1); 
     pane1.setStyle("-fx-background-color: #224488"); 
     pane2.setStyle("-fx-background-color: #224488"); 
     root.getChildren().addAll(pane1, pane2); 
     Scene scene1 = new Scene(root, 1024, 600); 
     primaryStage.setScene(scene1); 
     primaryStage.setTitle("Mouse Clicker"); 
     primaryStage.show(); 

    } 
} 

预期的行为是,我每次点击按钮,它会加载lastCanvas这是画布,然后绘制最后一个对象。但事实并非如此。我在button1的mouseevent里面试过类似的东西

root.getChildren().removeAll(); 
root.getChildren().addAll(lastCanvas, pane2); 

这会在每次点击按钮时在控制台上产生错误。另一个问题是我试图创建第二个场景。

HBox root2 = new HBox(); 
root2.getChildren().addAll(lastCanvas, pane2); 

虽然我没有在任何地方使用root2,但是这样做令人惊讶地删除了pane2上的所有内容。我在这里所做的就是创建一个新的HBox并将pane2添加到该Hbox中。为什么这样做会从我的primaryStage中删除pane2而不是留下空白区域?

此外,如果您有其他建议删除画布上绘制的最后一个对象-once或直到canvas完全为空,您也可以告诉我,而不是试图解决我在上面的代码中做错了什么。

+0

“这样做令人惊讶地删除了pane2上的所有内容,尽管我没有在任何地方使用root2,我在这里所做的只是创建一个新的HBox并将pane2添加到该hbox中。”=>从[Node doc](https:/ /docs.oracle.com/javase/8/javafx/api/javafx/scene/Node。如果一个程序向一个Parent(包括Group,Region等)添加一个子节点并且该节点已经是一个不同的Parent或一个Scene的根的子节点,那么该节点会自动(并且默默)从其前父母“。每个问题的一个问题是好的:-) – jewelsea

回答

0

没有分析这个问题,我建议你使用如下代码解决方案为基础

private final double diameter = 25; 
    private final LinkedList<Point2D> centers = new LinkedList<>(); 
    private final Canvas pane1 = new Canvas(800, 600); 

    @Override 
    public void start(Stage primaryStage) throws Exception 
    { 
     System.out.println("Hello"); 
     HBox root = new HBox(); 

     pane1.setOnMouseClicked((MouseEvent event) -> 
     { 
      centers.add(new Point2D(event.getSceneX(), event.getSceneY())); 
      render(); 
      System.out.println("Clicked"); 
     }); 
     VBox pane2 = new VBox(10); 
     pane2.setPrefSize(224, 600); 
     Button button1 = new Button("Clear Last Drawn"); 
     button1.setOnMouseClicked((MouseEvent event) -> 
     { 
      if (!centers.isEmpty()) 
      { 
       centers.removeLast(); 
       render(); 
       System.out.println("Last canvas loaded"); 
      } 
     }); 
     pane2.getChildren().addAll(button1); 
     pane1.setStyle("-fx-background-color: #224488"); 
     pane2.setStyle("-fx-background-color: #224488"); 
     root.getChildren().addAll(pane1, pane2); 
     Scene scene1 = new Scene(root, 1024, 600); 
     primaryStage.setScene(scene1); 
     primaryStage.setTitle("Mouse Clicker Project (Berk YATKIN)"); 
     primaryStage.show(); 

    } 

    private void render() 
    { 
     pane1.getGraphicsContext2D().clearRect(0, 0, pane1.getWidth(), pane1.getHeight()); 
     centers.forEach((p) -> 
     { 
      pane1.getGraphicsContext2D().strokeOval(
        p.getX() - (diameter/2), 
        p.getY() - (diameter/2), 
        diameter, 
        diameter); 
     }); 
    } 
+0

我看到您的实现存储每个圆圈,每次绘制一个圆时,它将清除整个画布,然后再次绘制每个圆。我不确定这是否是一个好的解决方案,但至少它能够正常工作,所以我非常感谢。顺便说一下,我铸造get.SceneX和get.SceneY浮动,因为Point2D函数期望浮动参数希望它可以做到这一点。同样在渲染函数中,它应该是“p.x”和“p.y”,而不是“p.getX()”和“p.getY”。再次感谢回复,这对我来说已经足够了。 – Vsky

+0

此外,您和我的创建MouseClick事件的方式有什么区别吗?我可能也会说IntelliJ的方式,因为整个onMouseClick事件是自动生成的。 – Vsky

+0

关于Point2D,我使用'javafx.geometry.Point2D',我认为你使用'com.sun.javafx.geom.Point2D'。 至于听众,我使用lamdas(Java 8)而不是匿名类。 [(差异)](http://stackoverflow.com/questions/22637900/java8-lambdas-vs-anonymous-classes)。最后,你应该把这个例子作为一个概念证明,场景图可能是一个更好的解决方案,但是画布应该能够每秒重绘数十万个圆圈。 – geh

1

还没有分析这个问题我想先问你为什么坚持用画布的问题。场景图更适合这类问题。您可以添加(几乎)任意数量的圈子,并按您喜欢的顺序删除尽可能多的圈子。有什么比这更容易?

+0

感谢您的回复。我没有坚持任何事情。我没有这方面的知识,当我第一次搜索Java GUI编程时看到了两件事:Swing和JavaFX。简单地看后,我看到JavaFX更新,摇摆老旧,所以想用新的。在JavaFX的文档中,我还看到它展示了如何在画布上绘制对象,并认为我可以使用它。没有看到你提到的“场景图”。但是如果你可以给我写一个使用场景图的话,我会很高兴看看你的实现。谢谢。 – Vsky

0

你问它所以这里是:

import javafx.application.Application; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.layout.BorderPane; 
import javafx.scene.layout.Pane; 
import javafx.scene.layout.VBox; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.Circle; 
import javafx.stage.Stage; 

public class Main extends Application { 
    @Override 
    public void start(Stage primaryStage) throws Exception{ 
     BorderPane root = new BorderPane(); 

     Pane graphicsPane = new Pane(); 
     root.setCenter(graphicsPane); 

     graphicsPane.setOnMouseClicked(e -> { 
      // Draw circle around x,y 
      Circle circle = new Circle(e.getX(), e.getY(), 50.0); 
      circle.setStroke(Color.RED); 
      circle.setStrokeWidth(4.0); 
      circle.setFill(null); 
      graphicsPane.getChildren().add(circle); 
     }); 

     VBox pane2 = new VBox(10); 
     pane2.setPrefWidth(224); 
     Button button1 = new Button("Clear Last Drawn"); 
     button1.setOnAction(e -> { 
      int numCircles = graphicsPane.getChildren().size(); 
      if (numCircles > 0) { 
       graphicsPane.getChildren().remove(numCircles - 1); 
      } 
     }); 

     pane2.getChildren().addAll(button1); 
     root.setRight(pane2); 
     graphicsPane.setStyle("-fx-background-color: #aaaaaa"); 
     pane2.setStyle("-fx-background-color: #224488"); 
     Scene scene1 = new Scene(root, 1024, 600); 
     primaryStage.setScene(scene1); 
     primaryStage.setTitle("Mouse Clicker"); 
     primaryStage.show(); 

    } 

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

} 

这是直接使用场景图形解决方案。此示例还可以通过图形选择轻松扩展以移动圆周或删除圆。所有用画布都不容易的东西。

+0

感谢您的代码。但有没有办法来过滤graphicsPane内的节点?因为这样它不会擦除最后绘制的圆,而是会擦除最后绘制的对象。那么有没有办法让graphicPane中只有Circle儿童? – Vsky

+0

当然。你只需遍历列表并找到最后一个圆。 – mipa

+0

应禁止未经评论的评论。 – mipa