java - Construyendo una GUI para un Sudoku Solver(Completo con Ejemplo ASCII)
user-interface components (6)
.
Resumen, muestra
Hola a todos,
He creado un solucionador de Sudoku básico que puede resolver la mayoría de los problemas bastante rápidamente. Todavía tengo mucho trabajo por delante para que resuelva incluso los problemas más difíciles, pero me gustaría intentar implementar primero una GUI básica de JFrame.
He trabajado con applets de internet en el pasado, pero nunca antes con JFrames.
Quiero crear algo similar a la imagen de abajo (para empezar):
-------------------------------------------------------------------------------------------------
! 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 | !
! | | !
! ''-------------------------------------------------------------------------------------------'' !
! !
-------------------------------------------------------------------------------------------------
.
DETALLES ESPECÍFICOS
: Puzzle izquierdo
- Las secciones 9x9 deben estar claramente definidas (líneas intermedias; cuadros separados)
- Los cuadros de texto solo deben aceptar números / solo permitir que se ingrese un número (si es posible)
: Puzzle derecho
- Las secciones 9x9 deben estar claramente definidas (líneas intermedias; cuadros separados)
- No importa si los cuadros pueden o no pueden editarse siempre que puedan mostrar el resultado.
: Botón en el centro
- Debe ejecutar [SudokuPuzzle] .solve ();
: Cuadro de texto inferior
- No debe ser editable
.
LO QUE ESTOY BUSCANDO
Sé por experiencias pasadas que todo esto se puede hacer en un JFrame, pero como nunca lo creé, no estoy seguro de qué components (elementos de contenido, paneles, configuraciones, etc.) necesito usar para cumplir con mis especificaciones . Todavía tengo que encontrar una manera de limitar mis cuadros de texto a los números y evitar que el usuario inserte más de un valor a la vez. ¿Son los cuadros de texto realmente la mejor opción, o me falta algo que pueda satisfacer mis necesidades más específicamente?
No solo necesito saber qué clases necesito, sino también cómo organizarlas para que el botón permanezca cómodamente entre los dos rompecabezas y el cuadro de texto quede debajo. Por lo que he read , MigLayout parece una opción para simplificar este proceso.
.
NOTAS FINALES
Muchas, muchas gracias a cualquiera que ayude. Si alguna parte de esta pregunta parece un poco grosera o brusca, me disculpo. Tiendo a publicar la mayoría de mis preguntas por la noche, así que la comunidad tiene algunas horas para reflexionar antes de intentar todas las respuestas (eso y el hecho de que estoy haciendo cosas la mayoría de los días).
Estaré despierto durante 1-2 horas más para responder cualquier pregunta.
Gracias otra véz,
Justian
El GUI de Sudoku
Ok, no pude evitarlo ... Aquí está mi intento. Todo está en un paquete:
- GUI con todos los elementos de conformidad con la especificación (pregunta)
- diseño flexible
- sin dependencias externas - diseños de Swing estándar utilizados
- Validación de entrada (solo dígitos 0-9)
- Arquitectura del controlador de vista de modelo
- corredor de tareas en segundo plano (su GUI nunca se congela)
- algunos métodos de depuración incorporados (salida de Sudoku como texto)
- Implementación ficticia: simula un cálculo de larga ejecución que muestra la capacidad de respuesta de la GUI
Hice mi mejor esfuerzo para hacer que el código sea lo más legible posible. Puede haber partes bastante poco claras. Probablemente la parte de enhebrado no esté lúcida, pero si alguien encuentra esto de alguna utilidad, me encantaría describirlo mejor.
Así que mi objetivo era el uso más simple posible. Si observas las interfaces, es realmente difícil romper esto (congelar la interfaz de usuario, obtener Null Pointer Exc, etc.) como un ejercicio para escribir API públicas. Puede que esta no sea la mejor implementación, pero es una de las mejores que escribí. :)
Espero eso ayude.
Así es como se ve: Ejemplo de ejecución http://a.imageshack.us/img256/754/sudokusolvergui.png
(nota: los valores son aleatorios)
Uso
Todo lo que tienes que hacer es implementar la interfaz:
public interface SudokuImplementation {
void goButtonPressed(Integer[][] leftSudokuValues, SudokuController resultAcceptor);
}
Simplemente haga todos los cálculos en este método y almacene los resultados con resultAcceptor.setSudokuResult()
Aquí es cómo mostrar realmente GUI:
SudokuImplementation sudokuImplementation =
new YourSuperSudoku(); // <- your implementation
SudokuView sudokuView = new SudokuView();
sudokuView.setSudokuImplementation(sudokuImplementation);
sudokuView.setVisible(true);
¡Y eso es todo!
Código
Todas las clases están en el paquete por defecto - refactor como desee. Aquí está la lista de ellos:
- SudokuView - GUI principal
- SudokuRun - corredor de ejemplo
- SudokuController - permite controlar la vista de una manera segura
- SudokuImplementation - interfaz para la implementación de sudoku
- Implementación DummySudoku - ejemplo de implementación
1.SudokuView:
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. SudokuImplementación:
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;
}
}
Explicación
No pretendo que lo que hice sea lo mejor. Me encantaría ver otra respuesta con, digamos, todas las vistas realizadas con MigLayout. Sería muy instructivo. Estaba aprendiendo Swing GUI cuando la implementación de Sun era solo una, por lo que prevaleció en mi estilo. Dicho esto, recomiendo consultar el curso corto de Sun''s Swing GUI . También incluye un caso de estudio simple. Después de leerlo, casi toda la parte de SudokuView debería quedar clara.
Separé el código para hacerlo más legible. Es por eso que el controlador es otra clase, no parte de la vista. La vista es solo para la construcción de widgets y diseño, pero para hacerlo simple (no para crear algunas clases más) también inicializo el controlador en ella.
El verdadero trabajo está en el controlador. Contiene los detalles más delicados ... El enhebrado también va allí, así que no es tan obvio lo que realmente hace. Implementé una clase Thread desde cero. Hay alternativa: usar SwingWorker . Puede que sea un cliché, pero que quede claro: uso subprocesos para hacer que la GUI responda en cualquier momento. Sin el subprocesamiento adecuado, la GUI completa se congelaría cuando se realizara el cálculo. Decidí hacerlo lo más fácil posible desde el punto de vista de la implementación de Sudoku, como las actualizaciones incrementales sin bloqueo.
En cuanto a los hilos, es crucial saber qué código se ejecuta en qué hilo. Cada acción activada por el componente GUI se ejecuta en EDT (subproceso de distribución de eventos). Si realiza alguna tarea de larga duración en ella, la GUI no responderá. Así que solo hago otro hilo (ver implementación de goButtonPressed()
) y lo comienzo. Después de eso, EDT puede procesar cualquier otro evento sin bloquear.
Así que su Sudoku se ejecuta en un hilo especial, fondo. Puede hacer lo que quiera, a menos que tenga que actualizar la GUI. Es casi seguro que lo hará, ya que ahí es donde van las actualizaciones parciales. Aquí hay un problema: si llama a cualquier componente de la GUI directamente (establezca algunos valores), la GUI se congelará. Esta es una condición llamada violación de envío EDT. Toda la interacción con Swing debe realizarse en EDT para evitar cualquier congelación. ¿Cómo hacerlo? El EDT tiene una cola de eventos especiales solo para eso. Usted publica un evento de actualización en la cola. On EDT code está constantemente observando los eventos entrantes y actualiza la GUI en consecuencia. Básicamente, es una comunicación entre el hilo de fondo y EDT. Para publicar un evento en la cola Puede usar un método de utilidad especial diseñado solo para esto: EventQueue.invokeLater(new Runnable() { /* here goes your GUI interaction */ });
. Echa un vistazo a los métodos de SudokuController
:
- setSudokuResult ()
- público vacío setSudokuTime ()
- setSudokuCompleted ()
Eso es donde se publican los eventos de actualización de GUI.
Así que necesitas
- Limita los cuadros de texto a #s 1-9
- Evite la inserción de más de 1 valor a la vez.
(1) Para limitar sus cuadros de texto a solo números, creo que podría usar un JComboBox que se rellena con ints (de 1 a 9) envuelto en cuadros de números enteros.
(2) De esta manera, el usuario debe seleccionar un punto de cuadrícula JComboBox @ (x, y) en el tablero de sudoku. Luego debe seleccionar el número deseado de la lista desplegable, evitando la entrada múltiple al mismo tiempo.
Espero que esto ayude,
¡Buena suerte!
EDITAR: claridad + 1
Netbeans IDE tiene una excelente GUI para generar GUIs. Sin embargo, ayuda (mucho) tener una comprensión básica de las GUI de Java antes de intentar generar una con la herramienta. Especialmente importante es jugar con cada uno de los gerentes de diseño y tener una idea de lo que uno podría ayudar en una situación dada. También tenga en cuenta que puede anidar paneles con diferentes administradores de diseño para obtener más control sobre el diseño.
No puedo entender cómo es posible que desee abandonar esa impresionante impresión ASCII.
Debería echar un vistazo a los tutoriales que se encuentran en @ http://download.oracle.com/javase/tutorial/uiswing/ y observar cómo funcionan los administradores de diseño.
Para los cuadros de texto recomendaría usar JTextField. Aquí hay un código que puede usar para que solo acepten un dígito en ese momento:
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()));
}
}
Para que esto sea realmente útil, tendrás que personalizar muchas cosas.
Te sugiero que uses botones en lugar de campos de texto. Cuando alguien hace clic en el botón, se selecciona, cuando alguien escribe un número, va al campo de texto seleccionado (reemplaza el número allí, si corresponde). Esto le da un poco más de control sobre cómo se ingresan las teclas.
El diseño puede ser difícil. Sería bueno aprovechar la capacidad de Swing para escalar tamaños con el tamaño de la pantalla, pero asegúrese de escalar también las fuentes.
Casi cada GUI de swing hecho a mano debe comenzar con un BorderLayout. Esto le da "Lados", incluso si solo usa el norte o el sur (su ejemplo solo se usa al sur), BorderLayout es excelente para asignar todo el espacio no utilizado al centro.
En el centro es probable que desee colocar otro contenedor, posiblemente una cuadrícula.
Hay uno, creo que es "Box" que tiene un espaciado uniforme de una sola fila. Entonces, si configura una fila de 3 horizontalmente, luego agrega 3 verticalmente a cada caja, puede DENTRO de cada una de ellas crear una caja (para poder diferenciar cada grupo de 9) y luego dentro de esa caja agregar otros 3 horizontes, Cada uno relleno con 3 verticales.
Los diseños de swing generalmente se reducen a algunos trucos estándar (como siempre mirar con BorderLayout) seguidos de algunas adivinanzas, experimentos y cierto nivel de maldición.
Esto debería darle suficiente para empezar. Solo agregue la lógica de obtención para extraer los valores que ingresaron en los campos de texto.
Principal:
/*
* 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();
}
}
}
}
Tablero:
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]);
}
}
}