。
概述,示例
大家好,
我已经创建了一个基本的数独解算器,可以相当快地解决大多数问题。我还有很多工作要做,要让它解决即使是最困难的问题,但我想首先尝试实现一个基本的JFrame图形用户界面。
我过去曾使用过互联网小程序,但以前从未使用过JFrames。
我想创建类似于下图的东西(对于初学者来说):
-------------------------------------------------------------------------------------------------
! Sudoku Solver 1.0 - [] X !
-------------------------------------------------------------------------------------------------
! _____________ _____________ _____________ _____________ _____________ _____________ !
! | _ _ _ | _ _ _ | _ _ _ | | _ _ _ | _ _ _ | _ _ _ | !
! | !5! !_! !_! | !_! !_! !_! | !6! !_! !1! | | !5! !7! !2! | !4! !9! !3! | !6! !8! !1! | !
! | _ _ _ | _ _ _ | _ _ _ | | _ _ _ | _ _ _ | _ _ _ | !
! | !6! !_! !_! | !_! !_! !2! | !4! !_! !_! | | !6! !1! !3! | !8! !5! !2! | !4! !7! !9! | !
! | _ _ _ | _ _ _ | _ _ _ | | _ _ _ | _ _ _ | _ _ _ | !
! | !_! !_! !_! | !7! !_! !1! | !_! !_! !2! | | !8! !4! !9! | !7! !6! !1! | !3! !5! !2! | !
! -_____________-_____________-_____________- -_____________-_____________-_____________- !
! | _ _ _ | _ _ _ | _ _ _ | | _ _ _ | _ _ _ | _ _ _ | !
! | !_! !_! !4! | !_! !2! !_! | !_! !3! !_! | | !1! !6! !4! | !9! !2! !7! | !5! !3! !8! | !
! | _ _ _ | _ _ _ | _ _ _ | .---. | _ _ _ | _ _ _ | _ _ _ | !
! | !_! !3! !_! | !_! !_! !_! | !_! !9! !_! | | > | | !2! !3! !8! | !5! !1! !6! | !7! !9! !4! | !
! | _ _ _ | _ _ _ | _ _ _ | '---' | _ _ _ | _ _ _ | _ _ _ | !
! | !_! !_! !_! | !_! !4! !_! | !_! !_! !_! | | !7! !9! !5! | !3! !4! !8! | !1! !2! !6! | !
! -_____________-_____________-_____________- -_____________-_____________-_____________- !
! | _ _ _ | _ _ _ | _ _ _ | | _ _ _ | _ _ _ | _ _ _ | !
! | !_! !2! !_! | !1! !_! !5! | !9! !_! !_! | | !4! !2! !7! | !1! !8! !5! | !9! !6! !3! | !
! | _ _ _ | _ _ _ | _ _ _ | | _ _ _ | _ _ _ | _ _ _ | !
! | !_! !_! !_! | !6! !_! !_! | !_! !_! !5! | | !3! !8! !1! | !6! !7! !9! | !2! !4! !5! | !
! | _ _ _ | _ _ _ | _ _ _ | | _ _ _ | _ _ _ | _ _ _ | !
! | !_! !_! !6! | !_! !3! !_! | !_! !_! !7! | | !9! !5! !6! | !2! !3! !4! | !8! !1! !7! | !
! -_____________-_____________-_____________- -_____________-_____________-_____________- !
! !
! .-------------------------------------------------------------------------------------------. !
! | | !
! | Solved Puzzle in 9.096ms | Completely Solved: True | !
! | | !
! '-------------------------------------------------------------------------------------------' !
! !
-------------------------------------------------------------------------------------------------
。
细节
:左侧拼图
应明确定义
:右拼图
应清楚定义
,框是否可以/不能进行编辑都很重要
:居中按钮
应运行SudokuPuzzle.solve();的
:底部文本框
。
我要找的是
我从过去的经验中知道,这些都可以在JFrame中完成,但因为我自己从来没有构建过,我不太确定我需要使用哪些components (内容项、面板、设置等)来满足我的规范。我还没有找到一种方法来将我的文本框限制为数字,并防止用户一次插入多个值。文本框真的是最好的选择吗,或者我是否遗漏了一些更能满足我的需求的东西?
我不仅需要知道我需要哪些类,而且还需要知道如何组织这些类,以便按钮位于两个拼图之间,文本框位于下面。从我所做的read来看,MigLayout似乎是简化这个过程的一个选择。
。
结束语
非常非常感谢任何帮助我们的人。如果这个问题的任何部分显得有点粗鲁或突然,我道歉。我倾向于在晚上发布我的大多数问题,所以在我尝试所有的回复之前,社区有几个小时的时间来仔细考虑(这是因为我大部分时间都在外面做事情)。
我会再清醒1-2个小时来回答任何问题。
再次感谢,
Justian
发布于 2010-08-05 08:16:43
数独GUI
好吧,我情不自禁...这是我的尝试。这一切都在一个包中:
所有元素均符合规范的
的长时间运行计算
我尽了最大的努力使代码尽可能地可读。可能会有相当不清楚的部分。可能线程部分不清晰,但如果有人发现它有任何用处,我很乐意更好地描述它。
所以我的目标是最简单的用法。如果你看一下界面,你很难打破这些东西(冻结UI,获取空指针Exc等)作为编写公共API的练习。这可能不是最好的实现,但它是我写的最好的实现之一。:)
希望能有所帮助。
它看起来是这样的:
(注意:值是随机的)
用法
你所要做的就是实现这个接口:
public interface SudokuImplementation {
void goButtonPressed(Integer[][] leftSudokuValues, SudokuController resultAcceptor);
}
只需在此方法中完成所有计算,并使用resultAcceptor.setSudokuResult()
存储结果
下面是如何实际显示GUI:
SudokuImplementation sudokuImplementation =
new YourSuperSudoku(); // <- your implementation
SudokuView sudokuView = new SudokuView();
sudokuView.setSudokuImplementation(sudokuImplementation);
sudokuView.setVisible(true);
仅此而已!
代码
所有的类都在默认的包中-如你所愿进行重构。以下是它们的列表:
sudoku GUI
接口中控制视图
1.数独视图:
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.border.*;
/**
* View which constructs every component and creates it's own controller.
*/
public class SudokuView extends JFrame {
SudokuController controller;
public void setSudokuImplementation(SudokuImplementation listener) {
controller.setListener(listener);
}
/** Creates new form NewJFrame */
public SudokuView() {
controller = new SudokuController();
setTitle("Sudoku Solver 1.0");
getContentPane().add(createCenterPanel(), BorderLayout.CENTER);
getContentPane().add(createBottomPanel(), BorderLayout.SOUTH);
setMinimumSize(new Dimension(600, 300));
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private JPanel createBottomPanel() {
JPanel bottomPanel = new JPanel(new GridBagLayout());
JLabel leftLabel = createLabel("left");
JLabel rightLabel = createLabel("right");
controller.bindLeftLabel(leftLabel);
controller.bindRightLabel(rightLabel);
bottomPanel.add(leftLabel, getWholeCellConstraints());
bottomPanel.add(new JSeparator(JSeparator.VERTICAL));
bottomPanel.add(rightLabel, getWholeCellConstraints());
bottomPanel.setBorder(new BevelBorder(BevelBorder.LOWERED));
return bottomPanel;
}
private JLabel createLabel(String text) {
JLabel label = new JLabel(text);
label.setHorizontalAlignment(JLabel.CENTER);
return label;
}
private JPanel createCenterPanel() {
JPanel centerPanel = new JPanel(new GridBagLayout());
centerPanel.add(createLeftPanel(), getWholeCellConstraints());
centerPanel.add(createCenterButton(), getPreferredSizeConstraint());
centerPanel.add(createRightPanel(), getWholeCellConstraints());
return centerPanel;
}
private GridBagConstraints getPreferredSizeConstraint() {
// default will do
return new GridBagConstraints();
}
private JButton createCenterButton() {
JButton goButton = new JButton(">");
controller.bindCenterButton(goButton);
return goButton;
}
private static final Insets sixPixelInset = new Insets(6, 6, 6, 6);
private JPanel createRightPanel() {
JPanel rightPanel = create3x3Panel(6);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
JPanel panel2 = create3x3Panel(2);
fillPanelWithNonEditable(panel2, i, j);
rightPanel.add(panel2);
}
}
rightPanel.setBorder(new EmptyBorder(sixPixelInset));
return rightPanel;
}
private JPanel createLeftPanel() {
JPanel leftPanel = create3x3Panel(6);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
JPanel panel2 = create3x3Panel(2);
fillPanelWithEditable(panel2, i, j);
leftPanel.add(panel2);
}
}
leftPanel.setBorder(new EmptyBorder(sixPixelInset));
return leftPanel;
}
private GridBagConstraints getWholeCellConstraints() {
GridBagConstraints wholePanelCnstr = getPreferredSizeConstraint();
wholePanelCnstr.fill = java.awt.GridBagConstraints.BOTH;
wholePanelCnstr.weightx = 1.0;
wholePanelCnstr.weighty = 1.0;
return wholePanelCnstr;
}
private void fillPanelWithEditable(JPanel panel, int majorRow, int majorColumn) {
for (int minorRow = 0; minorRow < 3; minorRow++) {
for (int minorColumn = 0; minorColumn < 3; minorColumn++) {
final JFormattedTextField editableField = createEditableField();
int column = majorColumn * 3 + minorColumn;
int row = majorRow * 3 + minorRow;
controller.bindLeftSudokuCell(row, column, editableField);
panel.add(editableField);
}
}
}
private void fillPanelWithNonEditable(JPanel panel, int majorRow, int majorColumn) {
for (int minorRow = 0; minorRow < 3; minorRow++) {
for (int minorColumn = 0; minorColumn < 3; minorColumn++) {
final JFormattedTextField editableField = createNonEditableField();
int column = majorColumn * 3 + minorColumn;
int row = majorRow * 3 + minorRow;
controller.bindRightSudokuCell(row, column, editableField);
panel.add(editableField);
}
}
}
private JPanel create3x3Panel(int gap) {
final GridLayout gridLayout = new GridLayout(3, 3, 1, 1);
gridLayout.setHgap(gap);
gridLayout.setVgap(gap);
JPanel panel = new JPanel(gridLayout);
return panel;
}
private JFormattedTextField createNonEditableField() {
JFormattedTextField field = createEditableField();
field.setEditable(false);
field.setBackground(Color.WHITE); // otherwise non-editable gets gray
return field;
}
private JFormattedTextField createEditableField() {
JFormattedTextField field = new JFormattedTextField();
// accept only one digit and nothing else
try {
field.setFormatterFactory(new DefaultFormatterFactory(new MaskFormatter("#")));
} catch (java.text.ParseException ex) {
}
field.setPreferredSize(new Dimension(16, 30));
field.setHorizontalAlignment(javax.swing.JTextField.CENTER);
field.setText(" ");
field.setBorder(null);
return field;
}
}
2. SudokuRun:
import java.awt.EventQueue;
import javax.swing.UIManager;
public class SudokuRun implements Runnable {
public void run() {
// ******************** here You can swap Your true implementation
SudokuImplementation sudokuImplementation = new DummySudokuImplementation();
// ***************************** *************** ********* **** ** *
SudokuView sudokuView = new SudokuView();
sudokuView.setSudokuImplementation(sudokuImplementation);
sudokuView.setVisible(true);
}
public static void main(String args[]) {
tryToSetSystemLookAndFeel();
EventQueue.invokeLater(new SudokuRun());
}
private static void tryToSetSystemLookAndFeel() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
System.out.println("Couldn't set LAF");
}
}
}
3. SudokuController:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
public class SudokuController {
JLabel leftLabel, rightLabel;
JFormattedTextField[][] leftSudoku, rightSudoku;
JButton goButton;
public SudokuController() {
leftSudoku = new JFormattedTextField[9][9]; // standard sudoku size
rightSudoku = new JFormattedTextField[9][9];
}
void bindLeftLabel(JLabel label) {
leftLabel = label;
}
void bindRightLabel(JLabel label) {
rightLabel = label;
}
void bindLeftSudokuCell(final int row, final int column, JFormattedTextField field) {
field.addPropertyChangeListener("value", new PropertyChangeListener() {
// if user edits field than You could do something about it here
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() != null) {
String newValue = (String) evt.getNewValue();
userEditedValueAt(row, column, Integer.valueOf(newValue));
}
}
});
leftSudoku[row][column] = field;
}
void userEditedValueAt(int row, int column, int value) {
System.out.println("Value changed at row:" + row + ", column:" + column + " to " + value);
}
void bindRightSudokuCell(int row, int column, JFormattedTextField field) {
rightSudoku[row][column] = field;
}
void spitOutSudokus() {
System.out.println("Left:");
System.out.println(getPrettyPrinted(leftSudoku));
System.out.println("Right:");
System.out.println(getPrettyPrinted(rightSudoku));
}
private String getPrettyPrinted(JFormattedTextField[][] sudoku) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 9; i++) {
sb.append("|");
for (int j = 0; j < 9; j++) {
if (sudoku[i][j] != null) {
sb.append(sudoku[i][j].getText());
} else {
sb.append("-");
}
sb.append(" ");
}
sb.append("|\n");
}
return sb.toString();
}
void bindCenterButton(JButton goButton) {
this.goButton = goButton;
goButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
goButtonPressed();
}
});
}
SudokuImplementation listener;
public void setListener(SudokuImplementation listener) {
this.listener = listener;
}
Thread backGroundThread;
private void goButtonPressed() {
if (listener != null) {
if (backGroundThread == null || (backGroundThread != null && !backGroundThread.isAlive())) {
backGroundThread = new Thread() {
@Override
public void run() {
listener.goButtonPressed(getLeftValues(), SudokuController.this);
}
};
backGroundThread.start();
}
}
}
private Integer[][] getLeftValues() {
Integer[][] values = new Integer[9][9];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (!leftSudoku[i][j].getText().equals(" ")) {
values[i][j] = Integer.valueOf(leftSudoku[i][j].getText());
}
}
}
return values;
}
public void setSudokuResult(final Integer[][] result) {
// Any GUI interaction must be done on EDT
// We don't want to block computation so we choose invokeLater
// as opposed to invokeAndWait.
EventQueue.invokeLater(new Runnable() {
public void run() {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
rightSudoku[i][j].setValue(String.valueOf(result[i][j]));
}
}
}
});
}
public void setSudokuTime(final String time) {
EventQueue.invokeLater(new Runnable() {
public void run() {
leftLabel.setText("<html>Running time: <b>" + time);
}
});
}
public void setSudokuCompleted(final boolean completed) {
EventQueue.invokeLater(new Runnable() {
public void run() {
rightLabel.setText("<html>Completely Solved: <b>" + completed);
if (completed) {
spitOutSudokus();
}
}
});
}
}
4. SudokuImplementation:
public interface SudokuImplementation {
void goButtonPressed(Integer[][] leftSudokuValues, SudokuController resultAcceptor);
}
5. DummySudokuImplementation:
import java.util.concurrent.TimeUnit;
/**
* Simulates Sudoku solver. Demonstrates how to update GUI. The whole
* implementation is constructed so GUI never freezes.
*/
class DummySudokuImplementation implements SudokuImplementation {
public DummySudokuImplementation() {
}
public void goButtonPressed(Integer[][] leftSudokuValues, SudokuController resultAcceptor) {
System.out.println("Long running computation simulation...");
for (int i = 0; i < 50; i++) {
resultAcceptor.setSudokuCompleted(false);
resultAcceptor.setSudokuTime(String.valueOf(i * 50) + "ms");
resultAcceptor.setSudokuResult(getRandomResult());
waitSomeTime();
}
resultAcceptor.setSudokuResult(leftSudokuValues);
resultAcceptor.setSudokuCompleted(true);
waitSomeTime();
System.out.println("Done!");
}
private void waitSomeTime() {
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException ex) {
}
}
private Integer[][] getRandomResult() {
Integer[][] randomResult = new Integer[9][9];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
randomResult[i][j] = (int) (Math.random() * 9);
}
}
return randomResult;
}
}
解释
我并不认为我做的方式是最好的。我很想看到其他的答案,比如说,所有视图都是用MigLayout完成的。这将是非常有启发性的。当Sun的实现只有一个时,我正在学习Swing GUI,所以它在我的风格中占了上风。也就是说,我推荐咨询Sun's Swing GUI short course。它还包括一个简单的研究案例。读完之后,SudokuView的几乎整个部分都应该是清晰的。
我确实分离了代码以使其更具可读性。这就是为什么控制器是另一个类,而不是视图的一部分。该视图仅用于小部件和布局的构造,但为了使其简单(而不是创建更多的类),我还在其中初始化了控制器。
真正的工作是在控制器中。它包含了最多的细节。线程也在那里,所以它的实际作用并不是很明显。我从头开始实现了一个Thread类。还有另一种选择:使用SwingWorker。这可能是陈词滥调,但要清楚:我使用线程来使GUI随时响应。如果没有适当的线程,当计算发生时,整个GUI都会冻结。我决定从Sudoku的实现角度尽可能简化它,就像非阻塞增量更新一样。
至于线程,知道哪些代码在哪个线程中运行是至关重要的。GUI组件触发的每个操作都运行在EDT (事件分派线程)上。如果您在其上执行任何长时间运行的任务,GUI将不会响应。所以我只需创建另一个线程(参见goButtonPressed()
的实现)并启动它。在此之后,EDT可以处理任何其他事件,而不会阻塞。
所以你的数独游戏运行在一个特殊的后台线程中。它可以做任何它想做的事情,除非它必须更新GUI。它几乎可以肯定它会,因为这是部分更新的地方。这里有一个问题:如果您直接调用任何GUI组件(设置一些值),那么GUI将冻结。这是一种称为EDT调度冲突的情况。所有与Swing的交互都应该在EDT上完成,以避免任何冻结。该怎么做呢?EDT有专门的事件队列。您在队列上发布了一个更新事件。在EDT上,代码不断地关注传入的事件,并相应地更新GUI。所以基本上,它是后台线程和EDT之间的通信。要在队列中发布事件,可以使用专门为此设计的特殊实用程序方法:EventQueue.invokeLater(new Runnable() { /* here goes your GUI interaction */ });
。看一看SudokuController
方法:
这就是发布GUI更新事件的原因。
发布于 2010-08-05 04:51:25
这应该会给你足够的开始。只需添加getter逻辑以提取他们在文本字段中输入的值。
Main:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package sudoku;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
*
* @author nicholasdunn
*/
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
JFrame frame = new JFrame("");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.add(new Board());
panel.add(new JButton(">"));
panel.add(new Board());
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
NineSquare:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package sudoku;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
/**
*
* @author nicholasdunn
*/
public class NineSquare extends JPanel {
// What direction in relation to the center square
private JTextField nw,n,ne,e,se,s,sw,w,c;
private JTextField[] fields = new JTextField[]{
nw,n,ne,e,se,s,sw,w,c
};
private static final int BORDER_WIDTH = 5;
public NineSquare(Color bgColor) {
setLayout(new GridLayout(3,3));
initGui();
setBackground(bgColor);
}
private void initGui() {
for (int i = 0; i < fields.length; i++) {
fields[i] = new JTextField(1);
fields[i].setDocument(new NumericalDocument());
add(fields[i]);
}
setBorder(BorderFactory.createMatteBorder(BORDER_WIDTH,BORDER_WIDTH,BORDER_WIDTH,BORDER_WIDTH, Color.BLACK));
}
public Dimension getPreferredDimension() {
return new Dimension(100,100);
}
public static class NumericalDocument extends PlainDocument {
String numbers = "0123456789";
@Override
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
if (getLength() == 0 && str.length() == 1 && numbers.contains(str)) {
super.insertString(offs, str, a);
}
else {
Toolkit.getDefaultToolkit().beep();
}
}
}
}
电路板:
package sudoku;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.JPanel;
/**
*
* @author nicholasdunn
*/
public class Board extends JPanel {
private NineSquare[] gridSquares = new NineSquare[9];
private Color[] bgs = {Color.blue.brighter(), Color.gray};
public Board() {
setLayout(new GridLayout(3,3));
for (int i = 0; i < gridSquares.length; i++) {
gridSquares[i] = new NineSquare(bgs[i%2]);
add(gridSquares[i]);
}
}
}
发布于 2010-08-05 04:34:24
我不明白你怎么可能想要放弃这个很棒的ASCII打印输出。
你真的应该看看@ http://download.oracle.com/javase/tutorial/uiswing/提供的教程,看看布局管理器是如何工作的。
对于文本框,我建议使用JTextField。下面是一些你可以用来让他们一次只接受一个数字的代码:
public class textBox extends JTextField implements KeyListener{
public textBox() {
addKeyListener(this);
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent ke) {
//consume the event otherwise the default implementation will add it to the text
ke.consume();
if (Character.isDigit(ke.getKeyChar()))
this.setText(Character.toString(ke.getKeyChar()));
}
}
https://stackoverflow.com/questions/3409099
复制相似问题