3

我正在尝试使用 javafx 2 创建一个画布,用户可以在其中平移和缩放。对于静态内容,我的解决方案有效,但是一旦在用户平移时更新内容,鼠标事件就会停止工作,直到释放并再次按下鼠标按钮。

以下是演示该问题的最小示例。如果单击白色区域,则可以平移,但如果通过单击红色矩形开始平移,则内容更新时会中断。

class Test extends StackPane
{
    private Timer timer = new Timer();
    private Rectangle rect;
    private double pressedX, pressedY;

    public Test()
    {
        setMinSize(600, 600);
        setStyle("-fx-border-color: blue;");

        timer.schedule(new TimerTask()
        {
            @Override
            public void run()
            {
                Platform.runLater(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        if (rect != null)
                            getChildren().remove(rect);

                        rect = new Rectangle(10, 10, 200, 200);
                        rect.setFill(Color.RED);
                        getChildren().add(rect);
                    }
                });
            }
        }, 0, 100);

        setOnMousePressed(new EventHandler<MouseEvent>()
        {
            public void handle(MouseEvent event)
            {
                pressedX = event.getX();
                pressedY = event.getY();
            }
        });

        setOnMouseDragged(new EventHandler<MouseEvent>()
        {
            public void handle(MouseEvent event)
            {
                setTranslateX(getTranslateX() + event.getX() - pressedX);
                setTranslateY(getTranslateY() + event.getY() - pressedY);

                event.consume();
            }
        });
    }
}

public class TestApp extends Application
{
    public static void main(String[] args)
    {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage)
    {
        Scene scene = new Scene(new Test());
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

我在带有 JDJ7u7 的 Windows 8 64 位上。

4

1 回答 1

3

Well I think your code is OK, seems to run fine for me and operate without any clear issue using jdk7u7 on windows 7.

I think maybe you want to call rect.setMouseTransparent(true), so that the rectangles don't catch the clicks. The mouse transparency thing isn't to do with the adding and removing of rectangles, it's just the way that picking works in JavaFX.

You may want to consider placing your Test node in a pannable ScrollPane rather than implementing the panning yourself - might need to wrap it in a Group to get the appropriate behaviour. But the code you have is simple and seems to work fine, so perhaps use of a ScrollPane is unnecessary and may even confuse things more.

There is a Canvas class in Java, which is something different from what you have, so calling your node something other than Canvas is probably a good idea.

Not to do with your question, but using a Timeline is my preferred way to handle animation rather than a Timer, though the Timer method will still work as long as you correctly use Platform.runLater as you are doing.

Here's a modified sample using a Timeline and .mouseTransparent() with an added frame counter so that it is clear that animation is happening.

import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

class Test extends StackPane {
  private Rectangle rect;
  private double pressedX, pressedY;
  private LongProperty frame = new SimpleLongProperty();

  public Test() {
    setMinSize(600, 600);
    setStyle("-fx-border-color: blue;");
    Label count = new Label();
    count.textProperty().bind(Bindings.convert(frame));
    getChildren().add(count);
    count.setMouseTransparent(true);

    setOnMousePressed(new EventHandler<MouseEvent>() {
      public void handle(MouseEvent event) {
        pressedX = event.getX();
        pressedY = event.getY();
      }
    });

    setOnMouseDragged(new EventHandler<MouseEvent>() {
      public void handle(MouseEvent event) {
        setTranslateX(getTranslateX() + event.getX() - pressedX);
        setTranslateY(getTranslateY() + event.getY() - pressedY);

        event.consume();
      }
    });

    Timeline t = new Timeline(new KeyFrame(Duration.millis(100), new EventHandler<ActionEvent>() {
      @Override public void handle(ActionEvent event) {
        frame.set(frame.get() + 1);

        if (rect != null) {
          getChildren().remove(rect);
        }

        rect = new Rectangle(10, 10, 200, 200);
        rect.setFill(Color.RED);
        rect.setMouseTransparent(true);
        getChildren().add(0, rect);
      }
    }));
    t.setCycleCount(Timeline.INDEFINITE);
    t.play();
  }
}

public class TestApplication extends Application {
  public static void main(String[] args) { launch(args); }
  @Override public void start(Stage stage) {
    stage.setScene(new Scene(new Test()));
    stage.show();
  }
}

Sample program output

于 2012-09-11T20:33:07.900 回答