更新来自另一个类的GUI

huangapple go评论77阅读模式
英文:

Updating GUI from another class

问题

我正在使用NetBeans作为IDE在Java中使用Swing构建了一个贪吃蛇与梯子游戏问题是当我在另一个类中而不是主类外部编写一个处理GUI某些方面的函数例如设置标签的图标应用程序虽然运行但使用该函数中的代码应该发生的更改却不会应用

```java
    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"));
                  }
            } 

    }

如果我运行这段代码,图像将不会设置在标签上,但是如果我将setDiceIMG函数放在Game类内部,它可以完美地工作。关于此事的任何信息将会有所帮助。


<details>
<summary>英文:</summary>
I am building a Snake and Ladders game in Java with Swing using NetBeans as the IDE. The problem is when i write a function which handles some aspects of the GUI (e.g setting icon of a label) OUTSIDE of the main class in another class, the application runs but the changes that have to happen using that code in that function doesn&#39;t apply. 
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(&quot;D:\\dice\\dice11.jpg&quot;));
}
else if(dice == 2)
{
diceLabel.setIcon(new javax.swing.ImageIcon(&quot;D:\\dice\\dice22.jpg&quot;));
}
else
{
diceLabel.setIcon(new javax.swing.ImageIcon(&quot;D:\\dice\\dice66.jpg&quot;));
}
} 
}
If i run this code, the image won&#39;t be set on the label but if my make the setDiceIMG function inside the Game class, it works perfectly. Any information regarding this would be helpful.
</details>
# 答案1
**得分**: 1
以下是翻译好的内容:
**初始解决方案**,由MadProgrammer评论的“新手方法”是将`diceLabel`的引用传递给`Dice`构造函数或`setDiceIMG`方法,如以下[mre](https://stackoverflow.com/help/minimal-reproducible-example)中所示:
```java
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
{
// ... (省略其他代码)
class Dice // 不需要扩展Game
{
// 使用网络资源使代码更简洁
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
{
    // ... (省略其他代码)

    class DiceModel {
        // ... (省略其他代码)
    }

    class Dice {
        // ... (省略其他代码)
    }

    // ... (省略其他代码)
}

接下来的改进可以对代码进行重构,以添加一个控制器来遵循MVC设计模式:

public class Game {
    // ... (省略其他代码)

    class View {
        // ... (省略其他代码)
    }

    class DiceModel {
        // ... (省略其他代码)
    }

    // ... (省略其他代码)
}

在这个版本中,Game类充当了游戏控制器,View类用于视图,DiceModel类用于模型。控制器、视图和模型之间的分离遵循MVC设计模式。

英文:

The basic solution, or "novice approach" as commented by MadProgrammer is to path a reference of diceLabel to Dice constructor or setDiceIMG method as demonstrated in the following mre:<br/>

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(&quot;Change Icon&quot;);
changeIcon.addActionListener(e -&gt; 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(() -&gt; new Game().setVisible(true));
}
}
class Dice //	no need to extend Game
{
//use web resources to make code mre
private static final String[] CIRCLES_64 = {
&quot;https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Green.png&quot;,
&quot;https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Red.png&quot;,
&quot;https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Yellow.png&quot;,
};
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();
}
}
}

A better design can be achieved by introducing a model, shared between the view and 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(&quot;Change Icon&quot;);
changeIcon.addActionListener(e -&gt; 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(() -&gt; 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 = {
&quot;https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Green.png&quot;,
&quot;https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Red.png&quot;,
&quot;https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Yellow.png&quot;,
};
private final DiceModel model;
Dice(DiceModel model) {
this.model = model;
}
URL imageURL()
{
try {
if(model.getDiceNumber() &lt;= CIRCLES_64.length &amp;&amp; model.getDiceNumber() &gt; 0)
return new URL(CIRCLES_64[model.getDiceNumber()-1]);
else
return new URL(CIRCLES_64[0]);
} catch (MalformedURLException ex) {
ex.printStackTrace();
}
return null;
}
}

Next improvement could be refactoring the code to add a controller to follow the MVC design pattern:

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-&gt;view.update());//respond to model changes 
view.setVisible(true);
}
public static void main(String args[]){
java.awt.EventQueue.invokeLater(() -&gt; 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(&quot;Roll Dice&quot;);
changeIcon.addActionListener(e -&gt; 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 = {
&quot;https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Green.png&quot;,
&quot;https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Red.png&quot;,
&quot;https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Yellow.png&quot;,
&quot;https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Blue.png&quot;,
&quot;https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Orange.png&quot;,
&quot;https://cdn3.iconfinder.com/data/icons/softwaredemo/PNG/64x64/Circle_Grey.png&quot;
};
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;
}
}

Run it on line.

答案2

得分: 0

当然可以从多个类中使用(创建、引用)Swing组件。通常情况下,仅仅从一个类中创建一个超出"Hello world"的内容是非常不寻常的。

正确的方法是创建专门的自定义组件(可能是从JPanel派生的),这些组件创建和管理它们拥有的所有组件(比如拥有所有按钮和文本的对话框)。同时,最好将它们专门用于呈现数据,而将任何逻辑、计算和IO操作都放在单独的类中进行。了解一下模型-视图-控制器(Model-View-Controller,MVC)的概念。一些复杂的Swing类,如JTable,已经是按照这种方式实现的。

英文:

Absolutely you can use (create, reference) Swing components from multiple classes. In general it is highly unusual to have anything more than "Hello world" consisting from just one class.

The right approach is to create own specialized components (likely derived from JPanel) that create and manage all components they own (say dialog box with all its buttons and text). It is also very good to dedicate them for just presenting the data, while any logic, calculations and IO should be done in separate classes. Read about the model-view-controller. Some complex Swing classes like JTable are already implemented this way.

huangapple
  • 本文由 发表于 2020年5月4日 05:42:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/61582013.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定