首页 > 解决方案 > 更新 GUI 以响应模型更改

问题描述

我正在做我的第一个包含 GUI 的 Java 项目。我知道程序的逻辑应该与程序的视图(GUI)分开。我的问题是,我不明白如何在游戏逻辑发生变化后更新 GUI,而无需对其进行两次编码,一次在 GUI 中,一次在逻辑中。

这是我尝试做的一个非常简化的示例:

逻辑类:

public class Logic {

    private int importantVariable;

    public Logic(int importantVariable){
        this.importantVariable = importantVariable;
    }

    public int getImportantVariable() {
        return importantVariable;
    }

    public void increaseImportantVariable() {
        this.importantVariable++;
    }
}

GUI 类

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class GUI extends JFrame {

    private Logic logic;

    public GUI(Logic logic){
        this.logic = logic;

        this.setTitle("GUI");
        this.setSize(1200, 800);
        this.setLocationRelativeTo(null);//set the location to the middle of the display
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JPanel guiPanel = new JPanel();

        //create label
        JLabel showVariableLabel = new JLabel(String.valueOf(logic.getImportantVariable()));
        guiPanel.add(showVariableLabel);

        //create Button
        JButton button = new JButton();
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                logic.increaseImportantVariable();
            }
        });
        guiPanel.add(button);

        this.add(guiPanel);
    }
}

主类:

public class Main{

    public static void main(String[] args){
        Logic logic = new Logic(0);

        GUI gui = new GUI(logic);

        gui.setVisible(true);
    }
}

在本例中,GUI 有一个按钮和一个显示数字的标签。如果按下按钮,数字会增加。但是如何使标签显示增加的数字?我认为更改 ActionListener 以更新 JLabel 和逻辑并不好,因为程序几乎已经知道它必须知道的一切:逻辑如何工作以及逻辑如何显示。这意味着我的感觉是将 ActionListener 实现为

button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                logic.increaseImportantVariable();
                showVariableLabel.setText(String.valueOf(logic.getImportantVariable()));
            }
        });

不是最好的方法,即使它有效(我错了吗?)。我认为这是因为一个真正的项目会更加复杂,并且许多变量可能会在逻辑中发生变化,从而更容易更新整个 GUI 而不仅仅是更改的组件。

所以我要寻找的是要么只更改逻辑并不断更新整个 GUI(它知道如何显示整个逻辑),要么在逻辑发生更改时调用类似 gui.update() 的东西来更新整个 GUI。但是对于这两种方式,我都不知道该怎么做。我希望我的问题是可以理解的,并且我在思考如何以最佳方式分离逻辑和 GUI 方面没有大错误。

标签: javaswinguser-interfacemodel-view-controllerobservers

解决方案


要监听模型更改,请引入监听器接口:

interface ModelObserver {
    void modelChanged();
}

修改Logic使其可以接受侦听器并使用它:

class Logic {

    ModelObserver observer;

    private int importantVariable;

    public Logic(int importantVariable, ModelObserver observer){
        this.importantVariable = importantVariable;
        this.observer = observer;
    }

    public int getImportantVariable() {
        return importantVariable;
    }

    public void increaseImportantVariable() {
        importantVariable++;
        observer.modelChanged();
    }
}

Main类充当控制器,因此让它将观察者添加到Model并响应模型更改:

public class Main implements ModelObserver{

    private final GUI gui;

    Main(){
        Logic logic = new Logic(0, this);
        gui = new GUI(logic);
        gui.setVisible(true);
    }

    @Override
    public void modelChanged() {
      gui.refresh();
    }

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

把它们放在一起(为了说服整个代码可以复制粘贴到Main.java并运行):

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.WindowConstants;

public class Main implements ModelObserver{

    private final GUI gui;

    Main(){
        Logic logic = new Logic(0, this);
        gui = new GUI(logic);
        gui.setVisible(true);
    }

    @Override
    public void modelChanged() {
      gui.refresh();
    }

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

interface ModelObserver {
    void modelChanged();
}

class GUI extends JFrame {

    private final Logic logic;
    private final  JLabel showVariableLabel;

    public GUI(Logic logic){

        this.logic = logic;

        this.setTitle("GUI");
        this.setLocationRelativeTo(null);//set the location to the middle of the display
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JPanel guiPanel = new JPanel();

        //create label
        showVariableLabel = new JLabel(String.valueOf(logic.getImportantVariable()));
        guiPanel.add(showVariableLabel);

        //create Button
        JButton button = new JButton("Increment");
        button.addActionListener(e -> logic.increaseImportantVariable());
        guiPanel.add(button);

        this.add(guiPanel);
        pack();
    }

    public void refresh() {
        showVariableLabel.setText(String.valueOf(logic.getImportantVariable()));
    }
}

class Logic {

    ModelObserver observer;

    private int importantVariable;

    public Logic(int importantVariable, ModelObserver observer){
        this.importantVariable = importantVariable;
        this.observer = observer;
    }

    public int getImportantVariable() {
        return importantVariable;
    }

    public void increaseImportantVariable() {
        importantVariable++;
        observer.modelChanged();
    }
}

推荐阅读