【问题标题】:Buttons not working on Program按钮在程序上不起作用
【发布时间】:2016-08-14 01:43:10
【问题描述】:

我正在尝试在以下程序上设置按钮,但它们无法正确控制程序。我不确定他们为什么不工作。反向按钮有效,但开始和停止按钮无效。

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class ch30 extends Application {
  @Override // Override the start method in the Application class
  public void start(Stage primaryStage) {       
    FanPane fan = new FanPane();

    HBox hBox = new HBox(5);
    Button btPause = new Button("Pause");
    Button btResume = new Button("Resume");
    Button btReverse = new Button("Reverse");
    hBox.setAlignment(Pos.CENTER);
    hBox.getChildren().addAll(btPause, btResume, btReverse);

    BorderPane pane = new BorderPane();
    pane.setCenter(fan);
    pane.setBottom(hBox);

    // Create a scene and place it in the stage
    Scene scene = new Scene(pane, 200, 200);
    primaryStage.setTitle("Exercise15_28"); // Set the stage title
    primaryStage.setScene(scene); // Place the scene in the stage
    primaryStage.show(); // Display the stage

     //Runnable first = new Begin();

     //Thread first = new Thread();

     //t1.start();


        Thread first = new Thread(new Runnable() {
            @Override public void run() {
                while (true) {
                    try {
                        //Pause
                        Thread.sleep(100);

                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                    Platform.runLater(new Runnable() {
                        @Override public void run() {

                          fan.move(); 

                        }
                    });
                }
            }
        });

                first.start();



   //Timeline animation = new Timeline(
      //new KeyFrame(Duration.millis(100), e -> fan.move()));
    //animation.setCycleCount(Timeline.INDEFINITE);
    //animation.play(); // Start animation

    scene.widthProperty().addListener(e -> fan.setW(fan.getWidth()));
    scene.heightProperty().addListener(e -> fan.setH(fan.getHeight()));

   //btPause.setOnAction(e -> first.wait());
    btResume.setOnAction(e -> first.start());
    btReverse.setOnAction(e -> fan.reverse());
  }

  /**
   * The main method is only needed for the IDE with limited
   * JavaFX support. Not needed for running from the command line.
   */
  public static void main(String[] args) {
    launch(args);


  }
} 

class FanPane extends Pane {
  private double w = 200;
  private double h = 200;
  private double radius = Math.min(w, h) * 0.45;
  private Arc arc[] = new Arc[4];   
  private double startAngle = 30;
  private Circle circle = new Circle(w / 2, h / 2, radius);

  public FanPane() {
    circle.setStroke(Color.BLACK);
    circle.setFill(Color.WHITE);
    getChildren().add(circle);

    for (int i = 0; i < 4; i++) {
      arc[i] = new Arc(w / 2, h / 2, radius * 0.9, radius * 0.9, startAngle + i * 90, 35);
      arc[i].setFill(Color.RED); // Set fill color
      arc[i].setType(ArcType.ROUND);
      getChildren().addAll(arc[i]); 
    } 
  }

  private double increment = 5;

  public void reverse() {
    increment = -increment;
  }

  public void move() {
    setStartAngle(startAngle + increment);
  }

  public void setStartAngle(double angle) {
    startAngle = angle;
    setValues();
  }

  public void setValues() {
    radius = Math.min(w, h) * 0.45;
    circle.setRadius(radius);
    circle.setCenterX(w / 2);
    circle.setCenterY(h / 2);

    for (int i = 0; i < 4; i++) {
      arc[i].setRadiusX(radius * 0.9);
      arc[i].setRadiusY(radius * 0.9);
      arc[i].setCenterX(w / 2);
      arc[i].setCenterY(h / 2);
      arc[i].setStartAngle(startAngle + i * 90);
    }     
  }

  public void setW(double w) {
    this.w = w;
    setValues();
  }

  public void setH(double h) {
    this.h = h;
    setValues();
  }
}

【问题讨论】:

  • wait 不会停止线程,而是让当前(调用)线程等待,直到有人在同一对象上调用notify。看看this question

标签: multithreading javafx


【解决方案1】:

这应该使用时间轴来完成,我知道这是你的作业,并且出于某种疯狂的原因,你的作业被指定为不使用时间轴。但是对于其他任何人,不要这样做,只需使用时间轴。

也就是说……

您提到了您没有的开始和停止按钮。我假设开始意味着恢复,停止意味着暂停,因为这些是您拥有的按钮。所以我会相应地回答。

解决这个问题的最简单方法是使用布尔变量来控制风扇是否在移动。

定义应用程序的成员:

private boolean paused = false;

在你的线程中,如果没有暂停,只移动风扇:

Platform.runLater(() -> { if (!paused) fan.move(); });

配置你的按钮来设置你的标志:

btPause.setOnAction(e -> paused = true);
btResume.setOnAction(e -> paused = false);

我只是将暂停变量直接放在调用应用程序中,但如果您愿意,您可以将暂停状态封装在风扇对象中。

通常在处理多线程内容时,您必须小心数据因竞争条件而损坏。例如,您将使用 AtomicBoolean 之类的构造或同步语句。但是runLater 将所有内容都放在 JavaFX 应用程序线程中,因此您不必担心这一点。

您可以使用其他机制来确保您的线程不会一直循环和休眠,例如wait/notifyConditions,但对于这样的示例,您可能不需要这里。

更新的应用程序

更新的示例展示了建议的修改,在 JDK 8u60、OS X 10.11.4 上进行了测试。

public class ch30 extends Application {
    private boolean paused = false;

    @Override 
    public void start(Stage primaryStage) {
        FanPane fan = new FanPane();

        HBox hBox = new HBox(5);
        Button btPause = new Button("Pause");
        Button btResume = new Button("Resume");
        Button btReverse = new Button("Reverse");
        hBox.setAlignment(Pos.CENTER);
        hBox.getChildren().addAll(btPause, btResume, btReverse);

        BorderPane pane = new BorderPane();
        pane.setCenter(fan);
        pane.setBottom(hBox);

        Thread first = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    break;
                }
                Platform.runLater(() -> { if (!paused) fan.move(); });
            }
        });
        first.setDaemon(true);    
        first.start();

        btPause.setOnAction(e -> paused = true);
        btResume.setOnAction(e -> paused = false);
        btReverse.setOnAction(e -> fan.reverse());

        Scene scene = new Scene(pane, 200, 200);
        scene.widthProperty().addListener(e -> fan.setW(fan.getWidth()));
        scene.heightProperty().addListener(e -> fan.setH(fan.getHeight()));
        primaryStage.setScene(scene); 
        primaryStage.show(); 
    }
}

一边

设置您的线程的daemon status,以便当有人关闭主阶段时您的应用程序干净地关闭。

first.setDaemon(true);

【讨论】:

  • 这实际上是行不通的。现在我正在使用 first.stop() 来暂停程序。但是,我停止线程后无法重新启动它。你知道为什么恢复按钮不起作用吗?
  • 此答案中提出的解决方案确实有效。你永远不应该调用thread.stop(),它已被弃用,并且文档指出它的使用是不安全的。此外,如果你这样做了,线程状态将转到Thread.State.TERMINATED。这是end state。终止的线程将永远终止,它永远无法恢复处理。
  • 当我尝试使用您的解决方案时,它说我不能使用布尔变量,除非它是最终的。当我将其声明为 final 时,我无法在按钮区域将其设置为 true 或 false。你知道我为什么会收到这个错误吗?
  • 如果没有完整的错误文本,我无法确定您的错误原因。可能是因为您正在创建一个effectively final variable。如果您使变量成为应用程序的成员,它可以正常工作(至少对我而言)。如果您将变量声明为方法的本地变量,则会失败。如果您仍然遇到问题,我稍后会发布完整的解决方案。您可能还需要使用 Java 8(我就是这样做的),使用 javac -target 1.8 编译应用程序(尽管我不这么认为)。
猜你喜欢
  • 1970-01-01
  • 2018-04-22
  • 1970-01-01
  • 1970-01-01
  • 2020-06-30
  • 1970-01-01
  • 2015-10-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多