我正在创建一个javafx程序,我的程序导航菜单通过更改场景的根来工作。根源都来自Pane类。一些窗格具有运行的后台线程。但是当事件处理程序更改根窗格时,窗格会切换,但后台线程不会停止。当线程读取NFC并导致多个线程试图从NFC读取器读取时,这会导致问题 如何关闭后台线程? (从创建它们的窗格外部)或者我是否需要以不同的方式设置线程。 (线程设置为守护进程)。 线程在Pane构造函数中创建,如下所示: (我曾假设,当切换窗格时,它们属于窗格,线程将停止。事实并非如此)。
Runnable r = new Runnable() {
@Override
public void run () {
boolean cont = true;
while(cont){
try {
NFCcard create1 = new NFCcard();
String staffID=create1.getCardID().toString();
staffID = staffID.replaceAll("\\D+","");
signIn.setText("Welcome "+getUserName(staffID)+getPhotoSrc(create1.getCardID().toString()));
Thread.sleep(1000);
} catch (CardException e) {
} catch (InterruptedException e) {
}
signIn.setText("Scan your card to sign in/out");
if(getScene().getRoot().isDisable());
cont=false;
}
}};
Thread nfcCheckThread = new Thread(r);
nfcCheckThread.setDaemon(true);
nfcCheckThread.start();
我以这样的静态方式切换Panes :(这个方法在自己的类中)。
public static void homeButtonhandler(Stage stage){
HomePane mainPane1=new HomePane(stage, new HomeContent(stage));
stage.getScene().setRoot(mainPane1);
}
public static void adminButtonhandler(Stage stage){
DialogBox dialog = new DialogBox();
try{
Optional<String> result = dialog.showAndWait();
if (result.get().equals("115")){
AdminPane adminPane1 = new AdminPane(stage,new Content(stage));
stage.getScene().setRoot(adminPane1);
}}
catch(NoSuchElementException Exception){
}
}
public static void workingTodayButtonhandler(Stage stage){
//TODO trying to make the content change when when buttons are clicked
HomePane mainPane2=new HomePane(stage,new WorkingTodayContent(stage));
stage.getScene().setRoot(mainPane2);
// System.out.println(mainPane2.content);
}
首先是:
HomePane myPane = new HomePane(primaryStage,new HomeContent(primaryStage));
Scene homeScene = new Scene (myPane);
primaryStage.setMinHeight(1000);
primaryStage.setMinWidth(1700);
primaryStage.setScene(homeScene);
primaryStage.show();
答案 0 :(得分:0)
您应该添加一种方法来将监听器注册到负责替换场景根的类。这允许您通知这些更改,并通过使线程终止来做出反应。
示例强>
@FunctionalInterface
public interface NodeReplaceListener {
public void onNodeReplace();
}
private Parent root;
private NodeReplaceListener listener;
private Scene scene;
public void setRoot(Parent root, NodeReplaceListener listener) {
if (root != this.root && this.listener != null) {
this.listener.onNodeReplace();
}
this.root = root;
this.listener = listener;
scene.setRoot(root);
}
@Override
public void start(Stage primaryStage) {
Button btn = new Button("Next Scene");
btn.setOnAction((ActionEvent event) -> {
// replace root
setRoot(new StackPane(new Rectangle(100, 100)), null);
});
StackPane root = new StackPane();
root.getChildren().add(btn);
scene = new Scene(new Group(), 100, 100);
class MyRunnable implements Runnable {
// running volatile to guarantee that visibility of written values to all threads
volatile boolean running = true;
int i;
@Override
public void run() {
while (running) {
System.out.println(i++);
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
}
}
public void cancel() {
running = false;
}
}
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.setDaemon(true);
t.start();
primaryStage.setScene(scene);
// cancel runnable when root is replaced
setRoot(root, r::cancel);
primaryStage.show();
}
顺便说一句:请注意,不应该从应用程序线程的任何线程进行对场景的修改。使用Platform.runLater
获取来自其他线程的更新:
Platform.runLater(() -> signIn.setText("Scan your card to sign in/out"));
答案 1 :(得分:0)
正如你所说:
有些窗格有后台线程,但是它们运行。
我假设您在自己的Pane
实现中创建了这些线程。我将向您展示一个带有抽象类的解决方案,每个窗格(或者至少是作为根插入的窗格)都应该扩展。如果你没有,我强烈建议你这样做。虽然你没有提供关于这些窗格的大量信息,所以我将把你的答案集成到你的代码中(如果你决定遵循它)。
public abstract class OwnPane extends Pane {
protected volatile boolean isRoot = false;
public void setAsRoot(){
isRoot = true;
}
public void unsetAsRoot(){
isRoot = false;
}
}
我稍后会回到volatile
关键字。
现在您可以在OwnPane
- 方法中创建您的主题,最好是在activateThread
或其子类中,例如:
public void activateNFCThread(){
Runnable r = new Runnable(){
@Override
public void run () {
while(isRoot){
// what the thread has to do ...
}
}
};
Thread nfcCheckThread = new Thread(r);
nfcCheckThread.setDaemon(true);
nfcCheckThread.start();
}
现在我可以解释为什么必须使用volatile
关键字:字段isRoot
将由不同的线程使用。使用volatile
关键字,我们确保所有线程都能访问相同的&#34;变量&#34; (出于性能原因,每个线程都有自己的版本)
线程在OwnPane
(或子类)中的方法中创建的事实允许从isRoot
对象中访问Runnable
字段。在OwnPane
的子类中,您甚至可以覆盖setAsRoot
方法,以便在调用setAsRoot
方法时(如果需要)直接启动NFS线程:
public class PaneWithNFCReader extends OwnPane {
@Override
public void setAsRoot(){
super.setAsRoot();
activateNFCThread();
}
}
最后,您可以使用这些方法更改阶段中场景的根窗格:
// All your methods regarding stage changes are static, so I'll leave this one static too
public static void changeRoot(Stage stage, OwnPane newRoot){
OwnPane oldStage = (OwnPane)stage.getScene().getRoot();
oldStage.unsetAsRoot();
Platform.runLater(() -> { //Platform.runLater to be sure the main thread that will execute this
//(only main thread is allowed to change something in the JavaFX nodes)
stage.getScene().setRoot(newRoot);
newRoot.setAsRoot();
});
}