Search code examples
javafx

Can't update the label IN JAVAFX


at first thank you for watch my question though my English is not good.I want to make a minesweeper by using javafx,ant i want to add a colck to get the time.but the problem is that the label's setText() is not working.

this is the mistake area ,i cannot use update() to update the label

public void startTime() {
        new java.lang.Thread(new Runnable() {
            @Override
            public void run() {
                while (running){
                    try{
                        Thread.sleep(1000);
                    }catch (Exception e){}
                    time++;
                    label2.setText(String.valueOf(time));
                }
            }
        }).start();
}

And this is the whole code Controller.java

package sample;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;

import java.net.URL;
import java.util.Random;
import java.util.ResourceBundle;

public class Controller implements Initializable {

    @FXML
    private ChoiceBox<String> choiceBox;
    @FXML
    private GridPane pane;
    @FXML
    private Label label1;
    @FXML
    private Label label2;
    @FXML
    private HBox hBox;

    Button[][] btns;            //按钮存储
    boolean[][] pressed;        //对应是否按下存储
    boolean[][] genbomb;         //对应是否为雷存储
    int [][] nums;              //按钮上显示的数,如1,说明周围有1个雷
    boolean[][] flags;          //用户标记存储

    int width;
    int height;
    int bombNum;                 //雷数
    int flagNum;                //标记数
    int pressedNum;             //已被打开的按钮数

    int time;
    boolean running = true;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        choiceBox.setOnAction(e -> {
            System.out.println("changed");

            //从sample中取得边框大小、地雷数
            String value = choiceBox.getValue();
            String[] first = value.split("\\*");  //去除*
            String[]  second= first[1].split("\\|");//去除|
            height = Integer.parseInt(first[0]);   //获取行数
            width = Integer.parseInt(second[0]);  //获取列数
            bombNum = Integer.parseInt(second[1]);
            play();
        });

        //选择初始难度 0 1 2
        choiceBox.getSelectionModel().select(1);
    }
    //开始游戏
    public  void play(){
        System.out.println("strat");
        startTime();
        //初始化所有按钮的属性
        genbomb = new boolean[height][width];
        pressed = new boolean[height][width];
        btns = new Button[height][width];
        nums = new int[height][width];
        flags = new boolean[height][width];
        pane.getChildren().clear();

        //标记数和排过数置零
        flagNum = 0;
        pressedNum = 0;
        label1.setText(String.valueOf(bombNum));//把地雷数写入label中
        //埋雷
        int rest = bombNum;
        //用Random的r随机生成一个小于按钮总数的数
        Random r = new Random(System.nanoTime());
        while (rest > 0) {
            int n = r.nextInt(height * width);
            if(!genbomb[n / width][n % width]){
                genbomb[n / width][n % width] = true;
                rest--;
            }
        }

        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                Button btn = getBtn(j, i);
                btns[i][j] = btn;
                nums[i][j] = countLeisAround(j, i);//记录周围雷数
                pane.add(btn, j, i);
            }
        }
    }

    //计算周围9格雷的数量
    public int countLeisAround(int x, int y){
        int res = 0;
        for (int i = Math.max(0, y - 1); i <= Math.min(y + 1, height - 1); i++) {
            for (int j = Math.max(0, x - 1); j <= Math.min(x + 1, width - 1); j++) {
                if((y != i || x != j) && genbomb[i][j])
                    res++;
            }
        }
        return res;
    }

    //设置按钮
    public Button getBtn(int x, int y){
        Button button = new Button();
        //设置按钮属性
        button.setPrefSize(30, 30);
        button.setMinSize(30, 30);//最小大小
        button.setStyle("-fx-background-color: darkcyan;");
        //设置按钮动作
        button.setOnMouseClicked(e -> {
            if(e.getClickCount()== 2  && pressed[y][x]){
                int count = 0;      //附近9格标记数量
                for (int i = Math.max(0, y - 1); i <= Math.min(y + 1, height - 1); i++) {
                    for (int j = Math.max(0, x - 1); j <= Math.min(x + 1, width - 1); j++) {
                        if(flags[i][j])
                            count++;
                    }
                }
                if (count != nums[y][x]) return;//如果标记数量和雷数不一样则返回
                for (int i = Math.max(0, y - 1); i <= Math.min(y + 1, height - 1); i++) {
                    for (int j = Math.max(0, x - 1); j <= Math.min(x + 1, width - 1); j++) {
                        if(!flags[i][j])
                            open(j, i);
                    }
                }
                return;
            }
            //右键标记设置
            if (MouseButton.SECONDARY == e.getButton()) {
                if(!flags[y][x]) {
                    flagNum++;
                    flags[y][x] = true;
                    Shape shape = new Circle(4, Color.RED);
                    button.setGraphic(shape);
                }else {
                    flagNum--;
                    flags[y][x] = false;
                    button.setGraphic(null);
                }
            }
            //左键点击标记
            else {
                if(flags[y][x]) {
                    flagNum--;
                    button.setGraphic(null);
                }
                open(x, y);
            }
            updateRemain();//更新label
        });
        return button;
    }
    //更新雷数
    public void updateRemain(){
        label1.setText(String.valueOf(bombNum - flagNum));
    }

    //判定结果
    public void open(int x, int y){
        //已经点过的情况
        if (pressed[y][x])
            return;
        //第一次点击
        pressed[y][x] = true;
        Button button = btns[y][x];
        //踩到雷的情况
        if (genbomb[y][x]) {
            button.setText("*");
            button.setStyle("-fx-background-color: crimson");
            //弹窗部分
            Alert alert = new Alert(Alert.AlertType.INFORMATION);
            alert.setHeaderText("真遗憾!\n请再来一次 ...");
            alert.setTitle("提示");
            alert.showAndWait();
            play();
        }
        //没踩到雷的情况
        else {
            button.setStyle("-fx-background-color: dimgrey");
            pressedNum++;
            if (pressedNum == height * width - bombNum){
                Alert alert = new Alert(Alert.AlertType.INFORMATION);
                alert.setHeaderText("你赢了!");
                alert.setTitle("提示");
                alert.showAndWait();
                return;
            }
            //判断打开的按钮是否为零,若是则打开周围9个没被标记过的按钮
            int count = nums[y][x];
            if (count > 0)
                button.setText(String.valueOf(count));//显示雷数
            else {
                for (int i = Math.max(0, y - 1); i <= Math.min(y + 1, height - 1); i++) {
                    for (int j = Math.max(0, x - 1); j <= Math.min(x + 1, width - 1); j++) {
                        if(!flags[i][j])
                            open(j, i);
                    }
                }
            }
        }
    }

    public void help(){
        Alert alert = new Alert(Alert.AlertType.INFORMATION);
        alert.setHeaderText("");
        alert.setTitle("帮助");
        alert.showAndWait();
    }

    public void startTime() {
            new java.lang.Thread(new Runnable() {
                @Override
                public void run() {
                    while (running){
                        try{
                            Thread.sleep(1000);
                        }catch (Exception e){}
                        time++;
                        label2.setText(String.valueOf(time));
                    }
                }
            }).start();
    }
    //设置全屏
    public void fullScreen(ActionEvent e){
        Stage window = (Stage) pane.getScene().getWindow();
        window.setFullScreen(!window.isFullScreen());
    }
}

sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.String?>
<?import javafx.collections.FXCollections?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>

<BorderPane prefHeight="315.0" prefWidth="439.0" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
    <top>
        <HBox fx:id="hBox" alignment="CENTER">
            <padding><Insets bottom="10" top="10" /> </padding>
         <Button onAction="#help" text="帮助" />
            <ChoiceBox fx:id="choiceBox" prefHeight="30" prefWidth="120">
                <items>
                    <FXCollections fx:factory="observableArrayList">
                        <String fx:value="9*9|10" />
                        <String fx:value="16*16|40" />
                        <String fx:value="16*30|99" />
                    </FXCollections>
                </items>
            <HBox.margin>
               <Insets left="10.0" />
            </HBox.margin>
            </ChoiceBox>
            <Label fx:id="label1" style="-fx-background-color: burlywood;                                              -fx-background-radius: 10;   -fx-font-size: 2em" text="">
                <HBox.margin><Insets left="30" right="30" /> </HBox.margin>
                <padding><Insets bottom="1" left="10" right="10" top="1" /> </padding>
            </Label>
            <Button onMousePressed="#play" prefHeight="35.0" text="重置">
            <HBox.margin>
               <Insets right="10.0" />
            </HBox.margin></Button>
            <Button onAction="#fullScreen" prefHeight="30" text="切换全屏">
            <HBox.margin>
               <Insets left="30.0" />
            </HBox.margin></Button>
         <Label fx:id="label2" style="-fx-background-color: burlywood;                               -fx-background-radius: 10;    -fx-font-size: 2em">
            <HBox.margin>
               <Insets left="20.0" />
            </HBox.margin>
            <padding>
               <Insets left="20.0" right="20.0" top="5.0" />
            </padding>
         </Label>
        </HBox>
    </top>
    <center>
        <ScrollPane fitToHeight="true" fitToWidth="true">
            <BorderPane.margin><Insets bottom="30" left="30" right="30" top="0" /> </BorderPane.margin>
            <GridPane fx:id="pane" alignment="CENTER" hgap="2" vgap="2">
                <!--<padding><Insets top="30"></Insets></padding>-->
            </GridPane>
        </ScrollPane>
    </center>
</BorderPane>

Main.java

package sample;

        import javafx.application.Application;
        import javafx.fxml.FXMLLoader;
        import javafx.scene.Parent;
        import javafx.scene.Scene;
        import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("扫雷");
        primaryStage.setScene(new Scene(root, 1000, 600));
        primaryStage.show();
    }


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

in the end the bug enter image description here


Solution

  • For a JavaFX control to be updated, you must update it on the Application Thread.

    You just need to do the following:

    Platform.runLater(() -> label2.setText(String.valueOf(time)));
    

    This will allow your thread to update the label.

    However you may want to explore other JavaFX ways of doing this, rather than your own thread.