2016-10-25 5 views
1

Im folgenden Beispiel gibt es einen FXML-Controller, der einen separaten Thread ausführt, um Komponenten regelmäßig zu aktualisieren.JavaFX verwaltet Abfragestrukturen, die an den Szenenlebenszyklus gebunden sind

Wenn der Benutzer klickt, um von diesem Controller zu navigieren und zu diesem Controller zurückkehrt, werden 2 Threads im Hintergrund ausgeführt. (Der erste hat nie aufgehört).

Wie kann ich sicherstellen, dass es immer nur 1 Thread gibt und dass dieser Thread an die Lebensdauer des Controllers gebunden ist?

<BorderPane xmlns:fx="http://javafx.com/fxml/1" 
      fx:controller="example.BorderController" 
      fx:id="rootPane"> 
    <left> 
     <VBox> 
     <Button text="Pane1" onAction"#goToPane1"/> 
     <Button text=Pane2" onAction="goToPane2"/> 
     </VBox> 
    </left> 
</BorderPane> 

.

public class BorderController implements Initializable { 

    @FXML private BorderPane rootPane; 

    @FXML 
    public void goToPane1(ActionEvent event) { 
     showPage("fxml/Pane1"); 
    } 

    @FXML 
    public void goToPane2(ActionEvent event) { 
     showPage("fxml/Pane2"); 
    } 

    private void showPage(final Stringpage) { 
     try { 
      rootPane.setCenter((Node) FXMLLoader.load(getClass().getResource(page))); 

     } catch (final IOException ex) { 
      LOGGER.log(Level.INFO, "An error occurred loading the page.", ex); 
     } 
    } 
} 

.

public class Pane1Controller implements Initializable { 

    @FXML private Label toUpdate; 


    @Override 
    public void initialize(URL url, ResourceBundle rb) { 
       final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); 
     scheduler.scheduleAtFixedRate(() -> { 
      Platform.runLater(() -> { 
       System.out.println("Pane1 " + this); 
       toUpdate.setText(Instant.now().toString()); 
      }); 
     }, 2, 2, SECONDS); 
    } 
} 

der Ausgang zwischen den 2 Scheiben nach dem Navigieren ist wie folgt:

Pane1 [email protected] 
Pane1 [email protected] 
Pane1 [email protected] 
Pane1 [email protected] 
// Repeats 

Antwort

1

Sie die sceneProperty() eines Knotens ScheduledExecutor entsprechend der in der Scheibe, und Start oder Abschalten beobachten:

import java.net.URL; 
import java.time.Instant; 
import java.util.ResourceBundle; 
import java.util.concurrent.Executors; 
import java.util.concurrent.ScheduledExecutorService; 
import java.util.concurrent.TimeUnit; 

import javafx.application.Platform; 
import javafx.fxml.FXML; 
import javafx.fxml.Initializable; 
import javafx.scene.control.Label; 
import javafx.scene.layout.Pane; 

public class Pane1Controller implements Initializable { 

    @FXML 
    private Label toUpdate; 

    @FXML 
    private Pane root; 

    private ScheduledExecutorService scheduler; 

    @Override 
    public void initialize(URL url, ResourceBundle rb) { 

     root.sceneProperty().addListener((obs, oldScene, newScene) -> { 
      if (newScene == null && scheduler != null) { 
       scheduler.shutdown(); 
      } 
      if (newScene != null) { 

       scheduler = Executors.newScheduledThreadPool(1, runnable -> { 
        Thread t = new Thread(runnable); 
        t.setDaemon(true); 
        return t ; 
       }); 

       scheduler.scheduleAtFixedRate(() -> { 
        Platform.runLater(() -> { 
         System.out.println("Pane1 " + this); 
         toUpdate.setText(Instant.now().toString()); 
        }); 
       } , 2, 2, TimeUnit.SECONDS); 
      } 
     }); 

    } 
} 

Jeder Knoten im Bereich wird im Allgemeinen funktionieren, obwohl es wahrscheinlich am sinnvollsten ist, den Bereich selbst zu verwenden (den ich hier fx:id="root" gab). Hinweis: Ich habe den Thread, der vom Executor verwendet wird, auch zu einem Daemon-Thread gemacht, so dass das Beenden der Anwendung nicht verhindert wird.

Beachten Sie, dass die Animations-API für die im Beispiel verwendete Funktionalität (in der alle geplanten Aufgaben im FX-Anwendungs-Thread ausgeführt werden) sauberer ist als die API java.util.concurrent, obwohl Ihre tatsächliche Anwendung dies erfordern könnte Letzteres:

import java.net.URL; 
import java.time.Instant; 
import java.util.ResourceBundle; 

import javafx.animation.Animation; 
import javafx.animation.KeyFrame; 
import javafx.animation.Timeline; 
import javafx.fxml.FXML; 
import javafx.fxml.Initializable; 
import javafx.scene.control.Label; 
import javafx.scene.layout.Pane; 
import javafx.util.Duration; 

public class Pane1Controller implements Initializable { 

    @FXML 
    private Label toUpdate; 

    @FXML 
    private Pane root; 

    private Timeline timeline ; 

    @Override 
    public void initialize(URL url, ResourceBundle rb) { 

     timeline = new Timeline(new KeyFrame(Duration.seconds(2), e-> { 
      System.out.println("Pane1 " + this); 
      toUpdate.setText(Instant.now().toString());   
     })); 
     timeline.setCycleCount(Animation.INDEFINITE); 

     root.sceneProperty().addListener((obs, oldScene, newScene) -> { 
      if (newScene == null) { 
       timeline.pause(); 
      } else { 
       timeline.play(); 
      } 
     }); 

    } 
} 
Verwandte Themen