2016-02-26 46 views
1

我寫了一個擴展了Text類JavaFX的小時鐘部件。要更新時間我正在使用Task,基本上將文本設置爲當前系統時間。當我在Eclipse中運行這個應用程序時,它有時會在我呼叫stage.show()的行中拋出一個NullpointerExceptionJavaFX時鐘部件隨機拋出異常

這是我的小工具源代碼是什麼樣子:

package clock; 

import java.time.LocalDateTime; 
import java.time.format.DateTimeFormatter; 

import javafx.concurrent.Task; 
import javafx.scene.text.Font; 
import javafx.scene.text.Text; 

public class CurrentClockText extends Text { 
    private final DateTimeFormatter formatter; 

    public static final int HOUR = 1; 
    public static final int HOUR_MINUTE = 2; 
    public static final int HOUR_MINUTE_SECOND = 3; 
    public static final int HOUR_MINUTE_SECOND_MILLISECOND = 4; 

    private final long updateInterval; 

    private final Task<Void> updater = new Task<Void>() { 
     @Override 
     protected Void call() throws Exception { 
      while (true) { 
       LocalDateTime now = LocalDateTime.now(); 
       String nowTextual = formatter.format(now); 
       setText(nowTextual); 

       try { 
        Thread.sleep(updateInterval); 
       } catch (InterruptedException ignore) { 
       } 
      } 
     } 
    }; 

    public CurrentClockText() { 
     this(HOUR_MINUTE); 
    } 

    public CurrentClockText(final int detailLevel) { 
     String timeFormat = ""; 
     switch (detailLevel) { 
     case HOUR: 
      timeFormat = "HH"; 
      updateInterval = 60000; 
      break; 
     case HOUR_MINUTE: 
      timeFormat = "HH:mm"; 
      updateInterval = 15000; 
      break; 
     case HOUR_MINUTE_SECOND: 
      timeFormat = "HH:mm:ss"; 
      updateInterval = 500; 
      break; 
     case HOUR_MINUTE_SECOND_MILLISECOND: 
      updateInterval = 1; 
      timeFormat = "HH:mm:ss.S"; 
      break; 
     default: 
      throw new IllegalArgumentException(
        "Unknown detail level for Clock: " + detailLevel); 
     } 

     setFont(new Font("Verdana", 28)); 

     formatter = DateTimeFormatter.ofPattern(timeFormat); 
     Thread updaterThread = new Thread(updater, "CurrentClockText.updaterThread"); 
     updaterThread.setDaemon(true); 
     updaterThread.start(); 
    } 
} 

這是主類:

package clock; 

import javafx.application.Application; 
import javafx.scene.Scene; 
import javafx.scene.layout.BorderPane; 
import javafx.stage.Stage; 

public class Boot extends Application { 

    @Override 
    public void start(Stage stage) throws Exception { 
     BorderPane pane = new BorderPane(); 

     CurrentClockText clock = new CurrentClockText(4); 
     pane.setCenter(clock); 

     Scene scene = new Scene(pane); 
     stage.setScene(scene); 
     stage.show(); 
    } 

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

} 

這是堆棧跟蹤:

Exception in Application start method 
java.lang.reflect.InvocationTargetException 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 
    at java.lang.reflect.Method.invoke(Unknown Source) 
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Unknown Source) 
    at com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 
    at java.lang.reflect.Method.invoke(Unknown Source) 
    at sun.launcher.LauncherHelper$FXHelper.main(Unknown Source) 
Caused by: java.lang.RuntimeException: Exception in Application start method 
    at com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown Source) 
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$152(Unknown Source) 
    at com.sun.javafx.application.LauncherImpl$$Lambda$50/14845382.run(Unknown Source) 
    at java.lang.Thread.run(Unknown Source) 
Caused by: java.lang.NullPointerException 
    at com.sun.javafx.text.PrismTextLayout.addTextRun(Unknown Source) 
    at com.sun.javafx.text.GlyphLayout.addTextRun(Unknown Source) 
    at com.sun.javafx.text.GlyphLayout.breakRuns(Unknown Source) 
    at com.sun.javafx.text.PrismTextLayout.buildRuns(Unknown Source) 
    at com.sun.javafx.text.PrismTextLayout.layout(Unknown Source) 
    at com.sun.javafx.text.PrismTextLayout.ensureLayout(Unknown Source) 
    at com.sun.javafx.text.PrismTextLayout.getBounds(Unknown Source) 
    at javafx.scene.text.Text.getLogicalBounds(Unknown Source) 
    at javafx.scene.text.Text.impl_computeLayoutBounds(Unknown Source) 
    at javafx.scene.Node$12.computeBounds(Unknown Source) 
    at javafx.scene.Node$LazyBoundsProperty.get(Unknown Source) 
    at javafx.scene.Node$LazyBoundsProperty.get(Unknown Source) 
    at javafx.scene.Node.getLayoutBounds(Unknown Source) 
    at javafx.scene.Node.prefWidth(Unknown Source) 
    at javafx.scene.Node.minWidth(Unknown Source) 
    at javafx.scene.layout.Region.computeChildPrefAreaWidth(Unknown Source) 
    at javafx.scene.layout.BorderPane.getAreaWidth(Unknown Source) 
    at javafx.scene.layout.BorderPane.computePrefWidth(Unknown Source) 
    at javafx.scene.Parent.prefWidth(Unknown Source) 
    at javafx.scene.layout.Region.prefWidth(Unknown Source) 
    at javafx.scene.Scene.getPreferredWidth(Unknown Source) 
    at javafx.scene.Scene.resizeRootToPreferredSize(Unknown Source) 
    at javafx.scene.Scene.preferredSize(Unknown Source) 
    at javafx.scene.Scene.impl_preferredSize(Unknown Source) 
    at javafx.stage.Window$9.invalidated(Unknown Source) 
    at javafx.beans.property.BooleanPropertyBase.markInvalid(Unknown Source) 
    at javafx.beans.property.BooleanPropertyBase.set(Unknown Source) 
    at javafx.stage.Window.setShowing(Unknown Source) 
    at javafx.stage.Window.show(Unknown Source) 
    at javafx.stage.Stage.show(Unknown Source) 
    at clock.Boot.start(Boot.java:19) 
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159(Unknown Source) 
    at com.sun.javafx.application.LauncherImpl$$Lambda$53/19600960.run(Unknown Source) 
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(Unknown Source) 
    at com.sun.javafx.application.PlatformImpl$$Lambda$45/18503843.run(Unknown Source) 
    at com.sun.javafx.application.PlatformImpl.lambda$null$170(Unknown Source) 
    at com.sun.javafx.application.PlatformImpl$$Lambda$48/27167109.run(Unknown Source) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(Unknown Source) 
    at com.sun.javafx.application.PlatformImpl$$Lambda$47/2180324.run(Unknown Source) 
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source) 
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) 
    at com.sun.glass.ui.win.WinApplication.lambda$null$145(Unknown Source) 
    at com.sun.glass.ui.win.WinApplication$$Lambda$36/3326003.run(Unknown Source) 
    ... 1 more 
Exception running application clock.Boot 

正如我所說的這例外只發生在一些運行中,並不總是。這種情況主要發生在我更改detailLevel = 4的格式模式時,但在將其重新更改後仍保留另一次運行。我猜這實際上可能與Eclipse有關,但我無法調試代碼,因爲異常被拋出了stage.show調用,我絕對不明白。這個隨機異常的原因是什麼,我該如何解決它?

+2

https://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Task.html:[...]您必須確保您使用'Platform.runLater',以便進行任何修改場景圖形出現在FX應用程序線程中。 –

+0

@AndreasFester,實際上似乎已經解決了這個問題。你可以將它作爲答案發布,以便我可以將其標記爲已完成?謝謝!我現在稱之爲'setText':'Platform.runLater(() - > setText(nowTextual));'。但是,這似乎已經打破了文本的首選寬度。 – Selim

回答

1

在JavaFX中,與其他大多數GUI工具包一樣,有一個特定的線程可處理所有與UI相關的操作。應用程序不得更新此線程之外的用戶界面。如果需要從另一個線程更新UI,通常會提供API以確保代碼在UI線程的上下文中執行。有關JavaFX的更多信息,請參見Concurrency in JavaFX Tutorial

在你的情況下,最簡單的解決辦法是,以確保您的setText()調用JavaFX應用程序線程上執行,而不是與你Task相關的線程:

... 
Platform.runLater(() -> setText(nowTextual)); 
... 

還有其他可用的API在JavaFX中做動畫,可以用來在特定的時間間隔調用處理程序方法 - 這將從您的循環中刪除Thread.sleep()調用。有關更多信息,請參閱Timeline