如何添加一个触发其他地方定义的函数的actionEvent?

时间:2018-03-18 17:25:49

标签: java swing

我正在研究Java添加一个按钮,在单击时触发功能。这是从主类执行以创建游戏窗口的GUI文件。我找到的唯一方法是:

package Kingdomino;

import java.awt.BorderLayout;
...

public class createAndShowGUI extends JPanel {

    static public JComboBox createNumPlayersMenu(){
        String[] lista = {"2 players","3 players","4 players"};
        JComboBox numPlayersMenu = new JComboBox(lista);
        numPlayersMenu.setSelectedIndex(2);
        numPlayersMenu.setMaximumSize(new Dimension(200,30));
        return numPlayersMenu;
    }

    static public JComboBox createPlayerColorMenu(){
        String[] lista = {"blue","green","red","pink"};
        JComboBox playerColor = new JComboBox(lista);
        playerColor.setSelectedIndex(2);
        playerColor.setMaximumSize(new Dimension(200,30));
        return playerColor;
    }   

    static public JButton createPlayButton()  {
        JButton createPlayButton = new JButton();
        createPlayButton.setText("Play");
        return createPlayButton;
    }

    static public void main() throws IOException {
        JFrame frame = new JFrame("FrameDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setJMenuBar(createMenuBar());

        JPanel jpanel = new JPanel();

        frame.getContentPane().add(jpanel, BorderLayout.CENTER);

        Box verticalBox = Box.createVerticalBox();      
        jpanel.add(verticalBox, BorderLayout.CENTER);

        JLabel label = new JLabel();
        label.setText("Hi");
        verticalBox.add(label);

        Box horizontalBox = Box.createHorizontalBox();
        horizontalBox.setMaximumSize(new Dimension(700,50));

        JComboBox numPlayersMenu = createNumPlayersMenu();
        horizontalBox.add(numPlayersMenu);

        JComboBox playerColor = createPlayerColorMenu();
        horizontalBox.add(playerColor);

        verticalBox.add(horizontalBox);

        JButton btnNewButton = createPlayButton();
        btnNewButton.addActionListener( 
            new ActionListener() {          
            public void actionPerformed(ActionEvent e)  {
                String num1 =  numPlayersMenu.getSelectedItem().toString();
                String num2 = playerColor.getSelectedItem().toString();
                label.setText( "Selected number of players: "+num1 + " // selected color: " + num2 );
        }
        });
        verticalBox.add(btnNewButton);

        frame.pack();
        frame.setVisible(true);

    }
}

问题是,如果我想实现的函数比几行代码更复杂,我想用不同的方法实现它,而不是在 main()的代码中

我尝试过很多方法来尝试这样做,但都没有效果。这个方法似乎最简单,最干净,但我不喜欢在main()方法中定义一个函数,我宁愿把它放在外面并在动作监听器中引用它。

我确信必须有办法做到这一点,但我无法弄明白。有帮助吗?谢谢!

1 个答案:

答案 0 :(得分:0)

我的评论中的建议可能看起来似乎过于复杂,因为建议你重新思考你的代码,你努力使它更符合面向对象,并且在短期内这可能是是的,但从中期和长期来看,情况恰恰相反,因为现在你的游戏非常简单,你只是想设置两个属性,int和一个颜色,但是一旦你做出来这只是稍微复杂一点,将一切都留在静态领域所带来的复杂性可能会变得无法抗拒。

所以说到了,我的建议是什么?首先从视图(GUI代码)中提取模型(非GUI状态),这里模型非常简单,一个包含int的类,一个Color,一个包含构造函数,字段,getter / setter,以及其他任何需要的东西,如下:

import java.awt.Color;

// class that holds the non-GUI "state" of the game
// Also holds non-GUI behaviors as well via its methods
public class Game {
    // fields to hold state
    private int numberOfPlayers;
    private Color color;

    public Game() {
        this(0, Color.WHITE);
    }

    public Game(int numberOfPlayers, Color color) {
        this.numberOfPlayers = numberOfPlayers;
        this.color = color;
    }

    public int getNumberOfPlayers() {
        return numberOfPlayers;
    }

    public void setNumberOfPlayers(int numberOfPlayers) {
        this.numberOfPlayers = numberOfPlayers;
    }

    public Color getColor() {
        return color;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    // other behavior methods here
}

然后让我们创建一个JPanel来保存并显示两个JComboBox,比如称为UserSelectionPanel:

public class UserSelectionPanel extends JPanel {
    private static final String[] NUMBER_OF_PLAYERS_ITEMS = {
            "2 Players", "3 Players", "4 Players"};

    // using "parallel arrays" below, not a good thing long-term
    private static final String[] COLOR_ITEMS = {
            "Blue", "Green", "Red", "Pink"
    };
    private static final Color[] COLORS = {
            Color.BLUE, Color.GREEN, Color.RED, Color.PINK
    };
    private static final Color DEFAULT_COLOR = Color.WHITE;
    JComboBox<String> numberOfPlayersCombo = new JComboBox<>(NUMBER_OF_PLAYERS_ITEMS);
    JComboBox<String> colorsCombo = new JComboBox<>(COLOR_ITEMS);

我们也可以提供这种getter方法,以便我们可以提取用户选择的内容,例如播放器编号:

public int getNumberOfPlayers() {
    int selection = numberOfPlayersCombo.getSelectedIndex();
    if (selection < 0) {
        return 0;
    } else {
        return selection + 2;
    }
}

整个事情看起来像:

import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.*;

@SuppressWarnings("serial")
public class UserSelectionPanel extends JPanel {
    private static final String[] NUMBER_OF_PLAYERS_ITEMS = {
            "2 Players", "3 Players", "4 Players"};

    // using "parallel arrays" below, not a good thing long-term
    private static final String[] COLOR_ITEMS = {
            "Blue", "Green", "Red", "Pink"
    };
    private static final Color[] COLORS = {
            Color.BLUE, Color.GREEN, Color.RED, Color.PINK
    };
    private static final Color DEFAULT_COLOR = Color.WHITE;
    JComboBox<String> numberOfPlayersCombo = new JComboBox<>(NUMBER_OF_PLAYERS_ITEMS);
    JComboBox<String> colorsCombo = new JComboBox<>(COLOR_ITEMS);

    public UserSelectionPanel() {
        setLayout(new GridLayout(0, 2, 10, 10));
        add(new JLabel("Number of Players:"));
        add(numberOfPlayersCombo);
        add(new JLabel("Color:"));
        add(colorsCombo);
    }

    public Color getSelectedColor() {
        int selection = colorsCombo.getSelectedIndex();
        if (selection < 0) {
            return DEFAULT_COLOR;
        } else {
            return COLORS[selection];
        }
    }

    public int getNumberOfPlayers() {
        int selection = numberOfPlayersCombo.getSelectedIndex();
        if (selection < 0) {
            return 0;
        } else {
            return selection + 2;
        }
    }
}

然后我们可以轻松地在JOptionPane中显示这个JPanel,如果我们想要显示它,并从中获取信息

接下来是播放游戏的主JFrame中显示的主要JPanel。我们在这一点上非常简单,只需持有一个显示玩家数量的JLabel,一个在JOptionPane中显示UserSelectionPanel的JButton,主要的JPanel背景颜色将反映所选择的颜色。我们还可以给它一个Game对象,以便在进行用户选择时更新Game对象的状态。像这样:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;

@SuppressWarnings("serial")
public class MainGamePanel extends JPanel {
    private static final int PREF_W = 600;
    private static final int PREF_H = 450;
    private UserSelectionPanel userSelectionPanel = new UserSelectionPanel();
    private Game game;
    private JLabel numberOfPlayersLabel = new JLabel("0");

    public MainGamePanel(Game game) {
        JPanel topPanel = new JPanel();
        topPanel.setOpaque(false); // so color shows through

        // display the number of players here
        topPanel.add(new JLabel("Number of Players:"));
        topPanel.add(numberOfPlayersLabel);

        // to hold button that displays dialog
        JPanel middlePanel = new JPanel(new GridBagLayout());
        middlePanel.setOpaque(false); // same reason
        middlePanel.add(new JButton(new AbstractAction("Set-Up Game") {

            @Override
            public void actionPerformed(ActionEvent e) {
                setUpGameActionPerformed();
            }
        }));

        setPreferredSize(new Dimension(PREF_W, PREF_H));
        this.game = game;
        setBackground(game.getColor());
        setLayout(new BorderLayout());
        add(topPanel, BorderLayout.PAGE_START);
        add(middlePanel);
    }

    private void setUpGameActionPerformed() {
        // set up the JOptionPane
        Component parentComponent = MainGamePanel.this;
        UserSelectionPanel message = userSelectionPanel; // *** our all-imporant JPanel
        String title = "Set-up Game";
        int optionType = JOptionPane.OK_CANCEL_OPTION;
        int messageType = JOptionPane.PLAIN_MESSAGE;

        // display the JOptionPane
        int result = JOptionPane.showConfirmDialog(parentComponent, message, title,
                optionType, messageType);

        // if the user pressed "OK" then make the changes
        if (result == JOptionPane.OK_OPTION) {
            // update the state of the GUI
            int numberOfPlayers = userSelectionPanel.getNumberOfPlayers();
            Color color = userSelectionPanel.getSelectedColor();

            // and update our Game field, game:
            game.setNumberOfPlayers(numberOfPlayers);
            game.setColor(color);

            numberOfPlayersLabel.setText(String.valueOf(numberOfPlayers));
            setBackground(color);
        }
    }
}

我们可以像这样运行整个事情:

import javax.swing.*;

public class SimpleGameMain {
    private static void createAndShowGui() {
        Game game = new Game();
        MainGamePanel mainPanel = new MainGamePanel(game);

        JFrame frame = new JFrame("OOP Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

那么为什么要经历所有这些混乱?因为您当前的游戏设置非常简单,只要您尝试使其更加复杂和有趣,就会很难 进行调试和增强。如果你以这种方式分离出问题,如果你利用OOP,你就有更好的机会创造出可以增长和改进的东西。