【问题标题】:JAVA - GUI Place HoldersJAVA - GUI 占位符
【发布时间】:2015-03-01 14:23:07
【问题描述】:

我希望为 JAVA 开发一个猜词游戏,但我在开发 GUI 时遇到了一些困难。我不是想开发一个只使用一个文本字段的简单 GUI,我想开发一个更像移动设备的 GUI。因此,我希望白框显示为单词中字符的占位符。如下图所示。

基本上,玩家将能够从下方将字符(我可能会让它们是按钮或其他东西)拖放到占位符框中,以便与该位置的单词字符进行比较。我试过制作一个文本字段的数组列表,但是失败了。有人对如何进行有任何建议吗?


根据 MadProgrammer 提供的答案更新

在使用了一些 MadProgrammer 的代码后,我为我的视图包提出了以下包结构。

> Views
>> GameBoard.java
>> HighScorePanel.java
>> MainPanel.java
>> ScorePanel.java
>> StatisticPanel.java
>> TimerPanel.java
>> ViewConfig.java
>> WordPanel.java

本质上只是一堆放在 GameBoard.java 类中的面板。我遇到的主要问题是 MainPanel.java 类,并保持纵横比。所以下面是到目前为止的视图图像,它看起来还不错,但是单词提示和类别正好与文本字段相对,并且文本字段不够宽。

如果我放大这个框架,组件之间的间距将保持完全相同,并且不会按比例放大。所以基本上我在寻找一种方法来动态设置组件之间的间距。我已将 GameBoard.java、MainPanel.java 和 StatisticPanel.java 的代码放在下面。 GameBoard 由 MainPanel 和 StatisticsPanel 组成。 StatisticsPanel 由 ScorePanel、TimerPanel 和 HighScorePanel 组成。最后 MainPanel 由 MadProgrammer 之前建议的位组成。

GameBoard.java

package views;

import java.awt.BorderLayout;

import javax.swing.JFrame;

public class GameBoard extends JFrame{

    private StatisticsPanel sp = new StatisticsPanel();
    private MainPanel mp = new MainPanel();

    public GameBoard() {
        // set the title for the game board
        setTitle(ViewConfig.DEFAULT_GAME_TITLE);

        // add panel components to board
        add(sp, BorderLayout.PAGE_START);
        add(mp, BorderLayout.CENTER);

        // set the board size
        setSize(ViewConfig.DEFAULT_FRAME_WIDTH, ViewConfig.DEFAULT_FRAME_HEIGHT);

        // set the default close operation
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        GameBoard gb = new GameBoard();

        // set visible
        gb.setVisible(true);
    }
}

StatisticsPanel.java

package views;

import java.awt.FlowLayout;

import javax.swing.JPanel;

public class StatisticsPanel extends JPanel {

    private ScorePanel sp = new ScorePanel();
    private TimerPanel tp = new TimerPanel();
    private HighScorePanel hsp = new HighScorePanel();

    protected StatisticsPanel() {
        // Create a layout for the panel, flow layout in this case as each component should line up
        // horizontally
        FlowLayout panelLayout = new FlowLayout(FlowLayout.CENTER, (ViewConfig.DEFAULT_FRAME_WIDTH/6), 4);

        // Set the panel's layout to the newly created layout
        setLayout(panelLayout);

        // add components
        add(sp);
        add(tp);
        add(hsp);
    }

}

MainPanel.java

package views;

import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class MainPanel extends JPanel {

    private JButton submitAns, clearAns;
    private JLabel wordHint, wordCategory;
    private WordPanel wp = new WordPanel(ViewConfig.DEFAULT_GUESS_WORD.length());

    protected MainPanel() {
        // create the buttons to submit and clear answer
        submitAns = new JButton(ViewConfig.DEFAULT_SUBMIT);
        clearAns = new JButton(ViewConfig.DEFAULT_CLEAR);

        // Create new default labels for the word hint and category
        wordHint = new JLabel(ViewConfig.DEFAULT_WORD_HINT);
        wordCategory = new JLabel(ViewConfig.DEFAULT_WORD_CATEGORY);

        // Set layout manger to GridBagLayout
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.insets = new Insets(4, 4, 4, 4);

        // add all components to panel with following format:
        // Word Hint > Input Fields > Word Category > Buttons
        add(wordHint, gbc);
        add(wp, gbc);
        add(wordCategory, gbc);

        // Create secondary JPanel to group buttons together, give it flow layout and add button panel to parent
        JPanel buttonPanel = new JPanel();
        FlowLayout buttonPanelLayout = new FlowLayout(FlowLayout.CENTER, 10, 4);
        buttonPanel.add(submitAns, buttonPanelLayout);
        buttonPanel.add(clearAns, buttonPanelLayout);

        // add button panel to parent
        add(buttonPanel);
    }

    protected JLabel getWordHint() {
        return wordHint;
    }

    protected void setWordHint(String wordHint) {
        this.wordHint.setText(wordHint);
    }

    protected JLabel getWordCategory() {
        return wordCategory;
    }

    protected void setWordCategory(String wordCategory) {
        this.wordCategory.setText(wordCategory);
    }
}

【问题讨论】:

  • 您可以使用 GridLayout 将 JLabels 放置在 JPanel 上...
  • 不是一个选项。太丑了。目前的问题是您无法将 JTextFields 的数组列表添加到 JPanel

标签: java


【解决方案1】:

一个想法可能是使用一系列复合布局,您可以构建这些布局来满足您的要求。

核心是JPanel,它将呈现特制的JLabels,能够显示单词的字符,例如...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new GuessingGame());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class GuessingGame extends JPanel {

        public GuessingGame() {
            setLayout(new GridBagLayout());
            JLabel label = new JLabel("<html>A few sentences giving either a definition or a<br> few hints about what the word could be</html>");
            WordPane wordPane = new WordPane("stackoverflow");
            JLabel wordCategory = new JLabel("Word Category");

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.insets = new Insets(4, 4, 4, 4);
            add(label, gbc);
            add(wordPane, gbc);

            JPanel pnlWordCategory = new JPanel(new GridLayout(1, 6, 4, 4));
            pnlWordCategory.add(new LetterLabel("A"));
            pnlWordCategory.add(new LetterLabel("B"));
            pnlWordCategory.add(new LetterLabel("C"));
            pnlWordCategory.add(new LetterLabel("D"));
            pnlWordCategory.add(new LetterLabel("W"));
            pnlWordCategory.add(new LetterLabel("G"));
            add(wordCategory, gbc);
            add(pnlWordCategory, gbc);
        }

    }

    public class WordPane extends JPanel {

        private String word;
        private List<JLabel> labels;

        public WordPane(String text) {
            setLayout(new GridLayout(1, text.length(), 4, 4));
            labels = new ArrayList<>(text.length());
            for (int index = 0; index < text.length(); index++) {
                LetterLabel label = new LetterLabel();
                labels.add(label);
                add(label);
            }
        }

    }

        public class LetterLabel extends JLabel {

            public LetterLabel(String text) {
                this();
                setText(text);
            }

            public LetterLabel() {
                setBorder(new CompoundBorder(new LineBorder(Color.GRAY), new EmptyBorder(4, 4, 4, 4)));
            }

            @Override
            public Dimension getPreferredSize() {
                Insets insets = getInsets();
                Dimension size = new Dimension();
                FontMetrics fm = getFontMetrics(getFont());
                size.width = (insets.left + insets.right) + fm.stringWidth("M");
                size.height = (insets.top + insets.bottom) + fm.getHeight();
                return size;
            }

        }

}

现在,您可以使用相同的想法,但使用 JTextFields 而不是 JLabels

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new GuessingGame());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class GuessingGame extends JPanel {

        public GuessingGame() {
            setLayout(new GridBagLayout());
            JLabel label = new JLabel("<html>A few sentences giving either a definition or a<br> few hints about what the word could be</html>");
            WordPane wordPane = new WordPane("stackoverflow");
            JLabel wordCategory = new JLabel("Word Category");

            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.insets = new Insets(4, 4, 4, 4);
            add(label, gbc);
            add(wordPane, gbc);

            JPanel pnlWordCategory = new JPanel(new GridLayout(1, 6, 4, 4));
            pnlWordCategory.add(new LetterLabel("A"));
            pnlWordCategory.add(new LetterLabel("B"));
            pnlWordCategory.add(new LetterLabel("C"));
            pnlWordCategory.add(new LetterLabel("D"));
            pnlWordCategory.add(new LetterLabel("W"));
            pnlWordCategory.add(new LetterLabel("G"));
            add(wordCategory, gbc);
            add(pnlWordCategory, gbc);
        }

    }

    public class WordPane extends JPanel {

        private String word;
        private List<JTextField> fields;

        public WordPane(String text) {
            setLayout(new GridLayout(1, text.length(), 4, 4));
            fields = new ArrayList<>(text.length());
            for (int index = 0; index < text.length(); index++) {
                JTextField field = new JTextField(2);
                fields.add(field);
                add(field);
            }
        }

    }

        public class LetterLabel extends JLabel {

            public LetterLabel(String text) {
                this();
                setText(text);
            }

            public LetterLabel() {
                setBorder(new CompoundBorder(new LineBorder(Color.GRAY), new EmptyBorder(4, 4, 4, 4)));
            }

            @Override
            public Dimension getPreferredSize() {
                Insets insets = getInsets();
                Dimension size = new Dimension();
                FontMetrics fm = getFontMetrics(getFont());
                size.width = (insets.left + insets.right) + fm.stringWidth("M");
                size.height = (insets.top + insets.bottom) + fm.getHeight();
                return size;
            }

        }

}

现在,控制。为了管理该过程,您需要某种模型或控制器,它们能够从WordPane 获取输入并对其进行验证,然后通知WordPane 猜测是否有效,以便它可以更新UI,但这超出了问题的范围

更新

有很多方法可以实现将字符从一个部分拖到另一个部分,但都不是简单的。

可能最简单的方法是将控件的String 值包装在Transferable 中,并在将其拖放到该字段后更新该字段,例如this for example

【讨论】:

  • 非常感谢。是的,我已经对控制器类有了一个想法,对模型也有了一个想法。非常感谢!
  • 明白 D'n'D 不是最简单的东西,看更新
  • 是的,你是对的!但我想学点新东西。我只是不擅长图形部分,我肯定很难弄清楚,但这将是一次很好的学习体验
猜你喜欢
  • 2021-03-04
  • 1970-01-01
  • 2021-12-28
  • 2015-08-31
  • 2013-06-26
  • 1970-01-01
  • 2012-10-13
  • 2017-10-12
  • 2013-09-13
相关资源
最近更新 更多