问题
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.
回答1:
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:
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:
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.
来源:https://stackoverflow.com/questions/61582013/updating-gui-from-another-class