【问题标题】:How can I access a variable that is out of a class' scope?如何访问超出类范围的变量?
【发布时间】:2021-03-27 06:32:58
【问题描述】:

我正在尝试编写一个 JavaFX 数独程序,用户可以通过以下方式填充单元格:

  1. 单击九个按钮之一(对应于 1-9)
  2. 单击其中一个单元格

代码的工作方式是,当单击按钮时,与该按钮对应的 int 被分配给selectedNum。当单击单元格时,单元格会将其文本分配给selectedNum。但是,这就是问题所在——在 Cell 构造函数中实现 setOnAction 函数。 Cell 无法访问 selectedNum。当 selectedNum 是一个超出其类范围的变量时,如何传递?

细胞类:

import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;

/**
 * The Cell class inherits StackPane, representing one of the cells in Sudoku.
 * @author rkuni
 *
 */
public class Cell extends StackPane{
    
    private Text text; //displays the number inside the cell
    private Rectangle rect; //visual element for the cell
    
    /**
     * Default Constructor for Cell
     */
    public Cell() {
        text = new Text("");
        text.setFont(Font.font(30));
        rect = new Rectangle(50, 50);
        rect.setStyle("-fx-fill: white; -fx-stroke: black; -fx-stroke-width: 1;");
        this.getChildren().add(rect);
        this.getChildren().add(text);
        
        this.setOnMouseClicked(new EventHandler<MouseEvent>() { 
            @Override
            public void handle(MouseEvent t) {
                text.setText("" + 1); //when clicked, I want to set text to the number that is selected.
            }
        });
    }
}

主类

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.Font;

public class Main extends Application {
    
    private int selectedNum = 0;
    
    @Override
    public void start(Stage primaryStage) {
        try {
            
            Group root = new Group();
            Cell cell1 = new Cell();
            cell1.setLayoutX(100);
            cell1.setLayoutY(100);
            root.getChildren().add(cell1);
            
            Button numSelection[] = new Button[9];
            for (int i = 0; i < 9; i++) {
                final int j = i; 
                numSelection[i] = new Button("" + (i + 1));
                numSelection[i].setLayoutY((i / 3) * 55 + 200);
                numSelection[i].setLayoutX((i % 3) * 55 + 630);
                numSelection[i].setMinHeight(50);
                numSelection[i].setMinWidth(50);
                numSelection[i].setFont(Font.font(20));
                root.getChildren().add(numSelection[i]);

                numSelection[i].setOnAction(new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent arg0) {
                        selectedNum = j + 1;
                    }
                });
            }
            
            Scene scene = new Scene(root,900,500);
            primaryStage.setScene(scene);
            primaryStage.show();
            
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

可能的解决方案

一个潜在的解决方案是根本没有 Cell 类,并且代码可以工作。问题不再存在,因为所有变量都在同一范围内。但是,我想保留 Cell 类,因为它使代码更有条理。反正就是这样:

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;


public class Main extends Application {
    
    private int selectedNum; //will take the value of the last pressed button (1-9)
    
    @Override
    public void start(Stage primaryStage) {
        try {
            Group root = new Group();
            
            //setting up a stackpane that will contain a rect and text, making for a "cell"
            StackPane stack1 = new StackPane();
            Text text1 = new Text("");
            Rectangle rect1 = new Rectangle(50,50);
            rect1.setStyle("-fx-fill: white; -fx-stroke: black; -fx-stroke-width: 1;");
            stack1.getChildren().add(text1);
            stack1.getChildren().add(rect1);
            stack1.setLayoutX(100);
            stack1.setLayoutY(100);
            stack1.setOnMouseClicked(new EventHandler<MouseEvent>() { 
                @Override
                public void handle(MouseEvent t) {
                    text1.setText("" + selectedNum); //when clicked, set text to the number that is selected.
                }
            });
            
            //setting up the buttons that will designate which number will go into the cell.
            Button numButtons[] = new Button[9];
            for (int i = 0; i < 9; i++) {
                final int j = i;
                numButtons[i] = new Button("" + (i + 1));
                numButtons[i].setLayoutY((i / 3) * 55 + 200);
                numButtons[i].setLayoutX((i % 3) * 55 + 630);
                numButtons[i].setMinHeight(50);
                numButtons[i].setMinWidth(50);
                numButtons[i].setFont(Font.font(20));
                root.getChildren().add(numButtons[i]);

                numButtons[i].setOnAction(new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent arg0) {
                        selectedNum = j + 1;
                    }
                });
            }
            
            text1.toFront();
            root.getChildren().add(stack1);
            Scene scene = new Scene(root,900,500);
            primaryStage.setScene(scene);
            primaryStage.show();
            
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

我也考虑过重写函数来添加参数,但还没有成功。

【问题讨论】:

  • 有多种方法可以做到这一点。您可以在Cell 中发布事件并在Main 中捕获它。或者你可以给 Cell 一个新的函数属性,在你的mouseclicked eventhandler 中调用。您可以在 Main 类的构造函数中设置它
  • 或者在您的主​​类中创建一个IntegerProperty 来保存选定的值,并将其传递给您的Cell

标签: java button javafx scope overriding


【解决方案1】:

此类事情的(或多或少)标准方法是使用某种 MVC 设计模式的变体。这里的关键是要有一个单独的类(“模型”)来保存数据;然后,您可以将该类的单个实例传递给需要访问它的任何参与者。

public class Model {

    private int currentlySelectedValue ;

    // probably other properties here...

    public void setCurrentlySelectedValue(int value) {
        currentlySelectedValue = value ;
    }

    public int getCurrentlySelectedValue() {
        return currentlySelectedValue ;
    }
}

那么你可以这样做:

public class Cell extends StackPane{

    private final Model model ;
    
    private Text text; //displays the number inside the cell
    private Rectangle rect; //visual element for the cell
    
    /**
     * Default Constructor for Cell
     */
    public Cell(Model model) {
        this.model = model ;
        text = new Text("");
        text.setFont(Font.font(30));
        rect = new Rectangle(50, 50);
        rect.setStyle("-fx-fill: white; -fx-stroke: black; -fx-stroke-width: 1;");
        this.getChildren().add(rect);
        this.getChildren().add(text);
        
        this.setOnMouseClicked(new EventHandler<MouseEvent>() { 
            @Override
            public void handle(MouseEvent t) {
                text.setText("" + model.getCurrentlySelectedValue()); 
            }
        });
    }
}

public class Main extends Application {
    
    private int selectedNum = 0;
    
    @Override
    public void start(Stage primaryStage) {

        Model model = new Model();

        try {
            
            Group root = new Group();
            Cell cell1 = new Cell(model);
            cell1.setLayoutX(100);
            cell1.setLayoutY(100);
            root.getChildren().add(cell1);
            
            Button numSelection[] = new Button[9];
            for (int i = 0; i < 9; i++) {
                final int j = i + 1; 
                numSelection[i] = new Button("" + j);
                numSelection[i].setLayoutY((i / 3) * 55 + 200);
                numSelection[i].setLayoutX((i % 3) * 55 + 630);
                numSelection[i].setMinHeight(50);
                numSelection[i].setMinWidth(50);
                numSelection[i].setFont(Font.font(20));
                root.getChildren().add(numSelection[i]);

                numSelection[i].setOnAction(new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent arg0) {
                        model.setCurrentlySelectedValue(j);
                    }
                });
            }
            
            Scene scene = new Scene(root,900,500);
            primaryStage.setScene(scene);
            primaryStage.show();
            
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

请注意,如果您只需要一个值,则可以使用一些“捷径”方法;例如您可以使用 IntegerProperty 而不是自定义模型类。结构和概念其实是一样的。

【讨论】:

  • 所以这是Java 引用的展示?每个单元格都有一个Model 对象,它是对Main 内的model 的引用。随着model 中的selectedNum 发生变化,每个单元格都可以访问它,因为它们有对它的引用。哇,这让我大吃一惊。
  • MVC 设计模式与 JavaFX 通用吗?
  • @RoyK。是的,我猜。这就是 Java 的工作原理……所有对象都通过引用访问。
  • @RoyK。是的,无论工具包如何,MVC 在任何 UI 开发中都很常见。这是一种可以追溯到 1970 年代的设计模式;现在有很多不同的变体(MVP、MVVM),它也被改编用于 Web 应用程序(尽管这种改编使它看起来非常不同)。见stackoverflow.com/questions/32342864/applying-mvc-with-javafx
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-26
  • 1970-01-01
  • 2016-04-30
  • 1970-01-01
  • 1970-01-01
  • 2012-11-20
  • 2016-03-05
相关资源
最近更新 更多