【问题标题】:Updating GUI from another class从另一个类更新 GUI
【发布时间】:2020-05-03 21:42:39
【问题描述】:

我正在使用 NetBeans 作为 IDE 使用 Swing 在 Java 中构建一个 Snake and Ladders 游戏。问题是当我编写一个处理 GUI 某些方面的函数(例如设置标签的图标)在另一个类的主类之外时,应用程序运行但必须使用该函数中的代码进行的更改不会t 申请。

    import javax.swing.JLabel;
    import javax.swing.JOptionPane;

    public class Game extends javax.swing.JFrame
    {
            protected javax.swing.JLabel diceLabel;
            private static final Dice diceObj = new Dice();
            protected int diceNumber;

            private void dicePlayer1ButtonActionPerformed(java.awt.event.ActionEvent evt) {

            diceObj.setDiceIMG(diceNumber);

            }
            public static void main(String args[]){
        java.awt.EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                new Game().setVisible(true);    
            }
        });
        }
    }
    class Dice extends Game
    {
            public void setDiceIMG(int dice)
            {
                  if(dice == 1)
                  {
                       diceLabel.setIcon(new javax.swing.ImageIcon("D:\\dice\\dice11.jpg"));
                  }
                  else if(dice == 2)
                  {
                       diceLabel.setIcon(new javax.swing.ImageIcon("D:\\dice\\dice22.jpg"));
                  }

                  else
                  {
                       diceLabel.setIcon(new javax.swing.ImageIcon("D:\\dice\\dice66.jpg"));
                  }
            } 

    }

如果我运行此代码,图像将不会设置在标签上,但如果我在 Game 类中创建 setDiceIMG 函数,它会完美运行。任何有关这方面的信息都会有所帮助。

【问题讨论】:

  • 这里没有看到 main 方法。这段代码如何运行?请出示完整的代码。
  • @markspace 对刚刚编辑的内容感到抱歉
  • 简短的答案是,是的,长答案更复杂。一种新手方法是将组件的引用传递给子类,这通常是一个坏主意,因为这些类可以开始做他们不应该也不负责的事情。更好的解决方案是遵循模型-视图-控制器概念,其中子类执行某种工作,UI 侦听或对这些更改采取行动并相应地更新 UI,这样您就可以解耦代码跨度>
  • @MadProgrammer 假设我选择新手方法,那么如果我有一个使用大约一百个标签的函数,我是否必须将所有一百个标签的引用传递给子类?
  • @IbrahimFarooq 如果您想这样设计解决方案 - 那么,无论子类需要什么,您都需要将其传递给子类

标签: java swing model-view-controller


【解决方案1】:

MadProgrammer 评论的基本解决方案或“新手方法”是将diceLabel 的引用指向Dice 构造函数或setDiceIMG 方法,如以下mre 所示:

import java.awt.BorderLayout;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;

public class Game extends JFrame
{
    protected JLabel diceLabel;
    private final Dice diceObj; //should not be static
    protected int diceNumber;

    Game() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);

        diceLabel = new JLabel();
        diceLabel.setHorizontalAlignment(SwingConstants.CENTER);
        JButton changeIcon = new JButton("Change Icon");
        changeIcon.addActionListener(e -> changeLabelIcon());

        diceObj = new Dice(diceLabel); //path a reference of the lable to Dice
        changeLabelIcon();

        add(diceLabel, BorderLayout.NORTH);
        add(changeIcon, BorderLayout.SOUTH);

        pack();
    }

    private void changeLabelIcon() {
        diceNumber = new Random().nextInt(3);
        diceObj.setDiceIMG(diceNumber);
    }

    public static void main(String args[]){
        java.awt.EventQueue.invokeLater(() -> new Game().setVisible(true));
    }
}

class Dice //   no need to extend Game
{
    //use web resources to make code mre
    private static final String[] CIRCLES_64 = {
            "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Green.png",
            "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Red.png",
            "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Yellow.png",
    };
    private final JLabel diceLabel;

    Dice(JLabel diceLabel) {
        this.diceLabel = diceLabel;
    }

    public void setDiceIMG(int dice)
    {
        try {
            if(dice == 1)
            {
                diceLabel.setIcon(new ImageIcon(new URL(CIRCLES_64[0])));
            }
            else if(dice == 2)
            {
                diceLabel.setIcon(new ImageIcon(new URL(CIRCLES_64[1])));
            }
            else
            {
                diceLabel.setIcon(new ImageIcon(new URL(CIRCLES_64[2])));
            }
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        }
    }
}

可以通过引入模型来实现更好的设计,在视图和Dice之间共享:

public class Game extends JFrame
{
    protected JLabel diceLabel;
    private final Dice diceObj; //should not be static
    private final DiceModel model;

    Game() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);

        diceLabel = new JLabel();
        diceLabel.setHorizontalAlignment(SwingConstants.CENTER);
        JButton changeIcon = new JButton("Change Icon");
        changeIcon.addActionListener(e -> changeLabelIcon());

        model = new DiceModel(4);
        diceObj = new Dice(model);
        changeLabelIcon();

        add(diceLabel, BorderLayout.NORTH);
        add(changeIcon, BorderLayout.SOUTH);

        pack();
    }

    private void changeLabelIcon() {
        model.rollDice();
        diceLabel.setIcon(new ImageIcon(diceObj.imageURL()));
    }

    public static void main(String args[]){
        java.awt.EventQueue.invokeLater(() -> new Game().setVisible(true));
    }
}

class DiceModel {

    private int diceNumber;
    private final int max;
    private final Random rnd;

    DiceModel(int max) {
        this.max = max;
        rnd = new Random();
        diceNumber = 0;
    }

    void rollDice() {
        diceNumber = rnd.nextInt(max+1);
    }

    int getDiceNumber(){
        return diceNumber;
    }
}

class Dice
{
    //use web resources to make code mre
    private static final String[] CIRCLES_64 = {
            "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Green.png",
            "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Red.png",
            "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Yellow.png",
    };

    private final DiceModel model;

    Dice(DiceModel model) {
        this.model = model;
    }

     URL imageURL()
    {
        try {
            if(model.getDiceNumber() <= CIRCLES_64.length && model.getDiceNumber() > 0)
                return new URL(CIRCLES_64[model.getDiceNumber()-1]);
            else
                return new URL(CIRCLES_64[0]);

        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        }

        return null;
    }
}

下一个改进可能是重构代码以添加控制器以遵循MVC 设计模式:

import java.awt.BorderLayout;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeListener;

public class Game //acts as game controller
{
    Game() {

        DiceModel model = new DiceModel();
        View view = new View(model);
        view.update();//initialize icon 
        model.setListener(e->view.update());//respond to model changes 
        view.setVisible(true);
    }

    public static void main(String args[]){
        java.awt.EventQueue.invokeLater(() -> new Game());
    }
}

class View{

    private final JLabel diceLabel;
    private final DiceModel model;
    private final JFrame frame;

    View(DiceModel model) {
        this.model = model;
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);

        diceLabel = new JLabel();
        diceLabel.setHorizontalAlignment(SwingConstants.CENTER);
        JButton changeIcon = new JButton("Roll Dice");
        changeIcon.addActionListener(e -> model.rollDice()); //change model 

        frame.add(diceLabel, BorderLayout.NORTH);
        frame.add(changeIcon, BorderLayout.SOUTH);
    }

    void setVisible(boolean visible){
        frame.pack();
        frame.setVisible(visible);
    }

    void update() { 
        diceLabel.setIcon(new ImageIcon(model.imageURL()));
    }
}

class DiceModel {

    //use web resources to make code mre
    private static final String[] CIRCLES_64 = {
            "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Green.png",
            "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Red.png",
            "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Yellow.png",
            "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Blue.png",
            "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Orange.png",
            "https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Grey.png"
    };

    private int diceNumber;
    private final Random rnd;
    private ChangeListener listener;

    DiceModel() {
        rnd = new Random();
        diceNumber = 0;
    }

    void rollDice() {
        diceNumber = rnd.nextInt(CIRCLES_64.length);
        notifyListener();
    }

    int getDiceNumber(){
        return diceNumber;
    }

    void notifyListener(){
        if(listener != null){
            listener.stateChanged(null);
        }
    }

    void setListener(ChangeListener listener) {
        this.listener = listener;
    }

    URL imageURL()
    {
        try {
            return new URL(CIRCLES_64[diceNumber]);
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        }
        return null;
    }
}

运行它on line

【讨论】:

  • 是的,我终于明白了。非常感谢!
【解决方案2】:

您绝对可以使用(创建、引用)来自多个类的 Swing 组件。一般来说,除了“Hello world”之外的任何东西都只包含一个类是非常不寻常的。

正确的方法是创建自己的专用组件(可能从 JPanel 派生)来创建和管理他们拥有的所有组件(例如带有所有按钮和文本的对话框)。将它们专门用于呈现数据也非常好,而任何逻辑、计算和 IO 都应该在单独的类中完成。阅读有关模型视图控制器的信息。一些复杂的 Swing 类如 JTable 已经以这种方式实现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-01
    • 1970-01-01
    • 2015-05-10
    • 2020-07-13
    相关资源
    最近更新 更多