英文:
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'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("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"));
}
}
}
If i run this code, the image won'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("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();
}
}
}
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("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;
}
}
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->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;
}
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论