JTable splitting cells. How to - java

I'm new to Swing and I want to create a table having this form:
So How to split a cell like the table shows?
Have you any useful links or tutorials or an idea?

This looks like a bowling score card to me. Based on that assumption, the number of columns is fixed, and the number of entries tends to be 6 or less. Since you likely won't need scrolling, I would recommend a fixed grid of components instead of a JTable.
This could easily be acheived using GridBagLayout. For the name, I'd use a JTextArea. For the 2 scoring cells for each frame, I'd use JTextFields. For the bottom 2-column-span component that holds the frame's score I'd probably use a JLabel.
Put all of this on a JPanel, and recreate the panel for each bowler.
EDIT:
Here's a quick mock-up just to show the concept. Not necessarily visually pretty, but I'll leave that as an exercise for the reader:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BowlingScoreCard implements Runnable
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new BowlingScoreCard());
}
public void run()
{
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(createScorecard(4), BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
private JPanel createScorecard(int numPlayers)
{
JPanel p = new JPanel(new GridBagLayout());
p.add(new JLabel("Player"), gbc(0, 0, 1, 1));
for (int x = 1; x <= 10; x++)
{
p.add(new JLabel(Integer.toString(x)), gbc(x, 0, 1, 1));
}
for (int y = 1; y <= numPlayers; y++)
{
JTextArea textArea = new JTextArea(2, 10);
p.add(textArea, gbc(0, y, 1, 1));
for (int i = 1; i <= 9; i++)
{
p.add(createFrame(2), gbc(i, y, 1, 1));
}
p.add(createFrame(3), gbc(10, y, 1, 1));
}
return p;
}
private JPanel createFrame(int entries)
{
JLabel label = new JLabel(" ");
label.setBackground(Color.GRAY);
JPanel p = new JPanel(new GridBagLayout());
p.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
for (int i = 0; i < entries; i++)
{
p.add(new JTextField(3), gbc(i, 0, 1, 1));
}
p.add(label, gbc(0, 1, 2, 1));
return p;
}
private GridBagConstraints gbc(int x, int y, int colspan, int rowspan)
{
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x;
gbc.gridy = y;
gbc.gridwidth = colspan;
gbc.gridheight = rowspan;
gbc.weightx = 0;
gbc.weighty = 0;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.NONE;
return gbc;
}
}

Related

What is the best way to add small numbers to the corner of each cell in a table or grid?

I'm trying to make a simple game involving a 10x10 grid, where each cell in the grid will need to contain a large letter in the center, and a small number in one of the corners. Like this:
Currently, the best idea I've had involves making 100 different JLabel components that correspond to each cell from jLabel00 to jLabel99, and using null layout to manually layer them over the bottom-right corner of each cell of a Table. However, this is going to cause a lot of repetitiveness in my code and UI even if I add the labels to an ArrayList (assuming I can even do that).
Does anyone know of a more elegant solution?
The kind of layout seen above can easily be made using a GridBagLayout for the 'cells' and a GridLayout to arrange those cells in rows. The code could use a factory method to produce the cells. It might take a number and a char or single character String for the letter. This code 'cheats' by assuming the number is a Unicode code point.
This is the method that makes a cell panel:
private JPanel getCellPanel(int i) {
JPanel p = new JPanel(new GridBagLayout());
p.setBorder(new LineBorder(Color.BLACK));
GridBagConstraints gbc = new GridBagConstraints();
JLabel l1 = new JLabel(new String(Character.toChars(i)));
l1.setFont(l1.getFont().deriveFont(30f));
gbc.gridx = 0;
gbc.gridy = 0;
p.add(l1, gbc);
JLabel l2 = new JLabel();
gbc.gridx = 0;
gbc.gridy = 1;
p.add(l2, gbc);
JLabel l3 = new JLabel();
gbc.gridx = 1;
gbc.gridy = 0;
p.add(l3, gbc);
JLabel l4 = new JLabel(new String("" + i));
l4.setFont(l4.getFont().deriveFont(12f));
gbc.gridx = 1;
gbc.gridy = 1;
p.add(l4, gbc);
return p;
}
The complete source code:
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class BigLetterLittleNumber {
private JComponent ui = null;
BigLetterLittleNumber() {
initUI();
}
public void initUI() {
if (ui != null) {
return;
}
ui = new JPanel(new GridLayout(0, 13, 2, 2));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
for (int ii=65; ii<91; ii++) {
ui.add(getCellPanel(ii));
}
}
private JPanel getCellPanel(int i) {
JPanel p = new JPanel(new GridBagLayout());
p.setBorder(new LineBorder(Color.BLACK));
GridBagConstraints gbc = new GridBagConstraints();
JLabel l1 = new JLabel(new String(Character.toChars(i)));
l1.setFont(l1.getFont().deriveFont(30f));
gbc.gridx = 0;
gbc.gridy = 0;
p.add(l1, gbc);
JLabel l2 = new JLabel();
gbc.gridx = 0;
gbc.gridy = 1;
p.add(l2, gbc);
JLabel l3 = new JLabel();
gbc.gridx = 1;
gbc.gridy = 0;
p.add(l3, gbc);
JLabel l4 = new JLabel(new String("" + i));
l4.setFont(l4.getFont().deriveFont(12f));
gbc.gridx = 1;
gbc.gridy = 1;
p.add(l4, gbc);
return p;
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
BigLetterLittleNumber o = new BigLetterLittleNumber();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}

Is there a way to place JButtons with different spaces in the begining at the GridBagLayout?

I'm trying to do something like this:
But I can't leave spaces before the buttons. I have tried to add invisible buttons, but nothing has changed.
for (int i = 0; i < gameSize; i++ ){
c.gridwidth = (i+1)*2;
c.gridx = 0;
c.gridy = 4*i;
JRadioButton temp = new JRadioButton();
temp.setVisible(false);
board.add(temp,c);
for(int j = 0; j < gameSize; j++){
c.gridwidth = 4;
c.gridx = 2+(4*j);
c.gridy = 2+(4*i);
cells[i][j] = new JButton();
cells[i][j].setBackground(Color.white);
board.add(cells[i][j],c);
}
}
It's look like [this:
when I made them visible. I didn't get why width of them is still 4 even though I'm assigning it to (i+1)*2.
I'm new to Java and very very new to Java GUI. So, maybe I didn't figure out the most basic thing. Thanks for advices!
I created the following GUI.
In order to do this, I had to use a combination of Swing layouts.
The buttons on each row are created in a JPanel with a GridLayout. The row is created with a row JPanel with a FlowLayout, using a dummy JLabel and the button row JPanel.
The main JPanel uses a GridLayout of (0, 1) to create the staggered effect. The dummy JLabel in each row gets bigger by half the size of the JButtons.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GridBagLayoutStaggeredGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new GridBagLayoutStaggeredGUI());
}
#Override
public void run() {
JFrame frame = new JFrame("Staggered Layout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createMainPanel() {
int gameSize = 8;
int buttonSize = 50;
int inset = 2;
JButton[][] cells = new JButton[gameSize][gameSize];
JPanel panel = new JPanel(new GridLayout(0, 1));
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
for (int i = 0; i < gameSize; i++) {
JPanel innerPanel = new JPanel(
new FlowLayout(FlowLayout.LEADING, 0, 0));
JLabel label = new JLabel();
label.setPreferredSize(new Dimension(inset, buttonSize));
innerPanel.add(label);
innerPanel.add(createRowPanel(cells[i], buttonSize));
panel.add(innerPanel);
inset += buttonSize / 2;
}
return panel;
}
private JPanel createRowPanel(JButton[] cells, int buttonSize) {
JPanel panel = new JPanel(new GridLayout(0, cells.length, 0, 0));
for (int i = 0; i < cells.length; i++) {
cells[i] = new JButton();
cells[i].setBackground(Color.white);
cells[i].setPreferredSize(new Dimension(buttonSize, buttonSize));
panel.add(cells[i]);
}
return panel;
}
}
The GridBagLayout can only calculate the width of a column if there is a component added to the column with a "gridwidth" of 1.
In your example you have 6 button each with a "gridwidth" of 2, implying you really want 12 columns. But what should the width of each column be?
The example below shows how to allocate a minimum width for each column. Now each of the 12 columns will have a minimum width based on the value specified.
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
public class GridBagLayoutMRE extends JPanel
{
public GridBagLayoutMRE()
{
int gameSize = 6;
int columnsNeeded = (gameSize * 3);
int cellSize = 30;
Dimension buttonSize = new Dimension(cellSize * 2, cellSize);
GridBagLayout gbl = new GridBagLayout();
setLayout( gbl );
int[] columnWidths = new int[columnsNeeded];
Arrays.fill(columnWidths, cellSize);
gbl.columnWidths = columnWidths;
GridBagConstraints gbc = new GridBagConstraints();
for (int i = 0; i < gameSize; i++)
{
gbc.gridx = 0;
gbc.gridy = i;
gbc.gridwidth = 1;
JRadioButton rb = new JRadioButton();
rb.setPreferredSize( new Dimension(60, cellSize) );
add(rb, gbc);
gbc.gridx = i + 1;
gbc.gridwidth = 2;
for (int j = 0; j < gameSize; j++)
{
JButton button = new JButton();
button.setPreferredSize( buttonSize );
add(button, gbc);
gbc.gridx = gbc.gridx + gbc.gridwidth;
}
}
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("GridBagLayoutMRE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout( new GridBagLayout() );
frame.add(new GridBagLayoutMRE());
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
}
}

Swing display table structure with Gridbaglayout

Currently I want to display a table structure using JPanel with gridbaglayout, but I have some trouble with designing it.
My datas are stored in a node structure (parsed from a xml document). In my code I create a new panel every time if a node has a group of other nodes (recursive method showGroupField). I also have a method that create a gridbagconstraint which gives me a table structure. The problem is that when a new panel is added to the mainpanel, it sticks with the first column of the gridbaglayout and not using the whole width of the mainpanel. Does anyone has a idea how to solve this? Thanks a lot!
This is what I have now:
And this is what I want:
Code:
package view;
import domain.NodeInfo;
public class InputScreen {
...
public InputScreen(Node parentNode){
this.parentNode = parentNode;
nodeInfo = new NodeInfo();
}
public void initiateInputScreen() {
mainFrame = new JFrame("Front-End Generator");
mainFrame.setSize(1000,1000);
contentPanel = new JPanel();
inputPanel = new JPanel();
makeContentPanel();
mainFrame.setContentPane(contentPanel);
mainFrame.pack();
mainFrame.setLocationRelativeTo(null);
mainFrame.setVisible(true);
mainFrame.setResizable(false);
}
private void makeContentPanel() {
contentPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
contentPanel.setLayout(new BorderLayout(5,5));
makeInputPanel();
contentPanel.add(inputPanel, BorderLayout.CENTER);
}
private void makeInputPanel() {
inputPanel.setBorder(BorderFactory.createTitledBorder("Inputs"));
inputPanel.setLayout(new GridBagLayout());
inputPanel.add(new JLabel(nodeInfo.getNodeInformation(parentNode)), createGbc(0, 0));
inputPanel.add(showGroupField(parentNode.getFirstChild()), createGbc(0,1));
}
private JPanel showGroupField(Node node) {
int count = 0;
JPanel tmpPanel = new JPanel(new GridBagLayout());
tmpPanel.setBorder(BorderFactory.createTitledBorder("Test"));
NodeList listNode = node.getChildNodes();
for(int i=0; i<listNode.getLength(); i++){
if(nodeInfo.isGroupField(listNode.item(i))){
tmpPanel.add(showGroupField(listNode.item(i)), createGbc(0, count));
count++;
}else{
tmpPanel.add(new JLabel("Test"), createGbc(0, count));
tmpPanel.add(new JTextField(20), createGbc(1, count));
count++;
}
}
return tmpPanel;
}
private GridBagConstraints createGbc(int x, int y){
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x;
gbc.gridy = y;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.anchor = (x==0) ? GridBagConstraints.WEST : GridBagConstraints.EAST;
gbc.fill = (x==0) ? GridBagConstraints.BOTH : GridBagConstraints.HORIZONTAL;
gbc.insets = (x==0) ? new Insets(5,0,5,5) : new Insets(5,5,5,0);
gbc.weightx = (x==0) ? 0.1 : 1.0;
gbc.weighty = 1.0;
return gbc;
}
}
Try to add inputPanel to the WEST position.
contentPanel.add(inputPanel, BorderLayout.WEST);

Adding a JLabel in relative position to buttons

OK, this is the relative code I have so far.
for (int i = gameLines - 1; i > 0; i--)
{
for (int j = 0; j < i; j++)
{
JButton jButton = new JButton();
jButton.setSize(buttonWidth, buttonHeight);
jButton.setText("" + line[i].charAt(j));
int x = ((buttonWidth / 2 * gameLines + 1) - (buttonWidth / 2 * i) + (buttonWidth * j));
int y = (gameLines - (i + 1)) * buttonHeight;
jButton.setLocation(x, y);
panel.add(jButton);
button[i][j] = jButton;
button[i][j].setActionCommand(Integer.toString(i) + "." + Integer.toString(j));
button[i][j].addActionListener(this);
}
}
The code creates and places all my buttons where I want them to be. This took me a while to figure out. I'm originally an AppleSoft BASIC programmer, I'm sorry about the i & j variable names.
Now for the fun. To the right of the bottom 3 rows of buttons, I want to place a jLabel. The right edge of the jLabel is to align with the right edge of the rightmost button in the top row. The text in each will be right justified, ending with a : and an up-to 4 digit number.
I'm thinking I can simply calculate the position like I did the button positions. The text, I was going to add based on a switch/case.
I'm having an trouble understanding JLabels, so I'd appreciate ANY help I can get.
My current thoughts are: to be inserted after the j loop
if (i < 4)
{
JLabel jLable = new JLabel();
JLabel.setSize(???, buttonHeight);
Calculate value of X;
int y = (gameLines - (i +1)) * buttonHeight;
jLabel.setLocation(x,y);
switch (i)
{
case 3:
jLabel.setText("Buttons Clicked: " + buttonsClicked);
break;
case 2:
etc.
}
panel.add(jLabel);
}
HELP please
For a bag of reasons, I would avoid absolute layouts. The moment you run it on some other PC, the whole thing begins to fall apart, instead, you should rely on the layout manager API available in Swing.
The following example uses a compound layout approach. Each row is it's own container (JPanel) and each row is then added to the main panel.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ButtonPyramid {
public static void main(String[] args) {
new ButtonPyramid();
}
public ButtonPyramid() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private String[] lines;
public TestPane() {
lines = new String[]{
"AAFITQPNXBE",
"?AXOPKMSUR",
"TGKFREUDI",
"DFEAAEOY",
"ZDE?VIF",
"G#RMLC",
"YUJGO",
"NSCP",
"KO#",
"MI",
"Y",
"B",
};
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
gbc.gridy = 1;
gbc.gridx = 0;
for (String line : lines) {
JPanel panel = new JPanel(new GridBagLayout());
for (char ch : line.toCharArray()) {
JButton btn = new JButton(Character.toString(ch));
btn.setMargin(new Insets(10, 10, 10, 10));
panel.add(btn);
}
add(panel, gbc);
gbc.gridy++;
}
JLabel label = new JLabel(":1234");
gbc.gridy -= 3;
gbc.gridx++;
gbc.anchor = GridBagConstraints.NORTH;
add(label, gbc);
}
}
}
If you would prefer the text not to take up another column, there is a little trick you can try, change the label layout constraints to look like the following instead...
JLabel label = new JLabel(":1234");
gbc.gridy -= 3;
gbc.anchor = GridBagConstraints.NORTHEAST;
add(label, gbc);
Check out Laying out Components within a Container for ideas and details
lines = new char[][]{...};
for (int outter = 0; outter < lines.length; outter++) {
JPanel panel = new JPanel(new GridBagLayout());
for (int inner = 0; inner < lines[outter].length) {
char ch = lines[outter][inner];
JButton btn = new JButton(Character.toString(ch));
btn.setMargin(new Insets(10, 10, 10, 10));
panel.add(btn);
}
add(panel, gbc);
gbc.gridy++;
}

Sudoku Board Using JPanels in Java

I know there have been several posts already about Sudoku-related questions, but I am not sure any of them have exactly what I am looking for...
I am trying to build an empty Sudoku board in Java using JPanels and JTextfields. I also need to create a menu on the right hand side with another JPanel.
The board itself is a 9 x 9 square, divided into 9 3x3 squares. Note that each smaller square is set off by a heavier border than the regular intersquare borders. Each square is a text field. Write the program so that nothing is in a text field. Users can type in the text field if they want, and if they do, numbers will show up. On the side there are four buttons that allow you to solve, get a new puzzle, get a hint, or reset puzzle.
Any ideas would be great. I am having trouble understanding how to nest the for loops to create the board. Here is my code...
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
public class ArrayTest extends JFrame {
public ArrayTest() {
JPanel board = new JPanel(new GridLayout(9, 9));
add(board);
JPanel[][] squares = new JPanel[9][9];
Border border = BorderFactory.createLineBorder(Color.BLACK);
for (int row = 1; row < 9; row++) {
for (int col = 1; col < 9; col++) {
squares[row][col] = new JPanel();
board.add(squares[row][col]);
}
}
JPanel menu = new JPanel();
menu.add(new JButton("Reset"));
menu.add(new JButton("Hint"));
menu.add(new JButton("Solve"));
menu.add(new JButton("New Puzzle"));
add(menu);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
/** Create a frame and set its properties*/
JFrame frame = new ArrayTest();
frame.setTitle("Sudoku");
frame.setSize(600, 600);
frame.setLocationRelativeTo(null); //Center the frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
A couple of things I see:
I don't think you want 9x9 JPanels as you have now, but 9x9 JTextFields. You may want 3x3 JPanels so you can make the borders of each section bolder. It might be easier to just lay these out explicitly instead of trying to do it in a loop.
Your loop counters (and array indices) should start at 0, not 1. The way you have it now, the loops will only execute 8 times.
You are going to want to keep track of values in each row, column, and in each 3x3 sub-group. Rows and columns are easy the way you have it in a 2D array. You might consider another array of arrays that hold the values in each 3x3 area. This makes it easier to scan through these values when you need to, and might be useful for placing values in the smaller 3x3 JPanels, if you go that route.
Firstly, I would use some kind of model to control the values within a "virtual" board, this will separate the logic from the UI and allow either to change without adversely effecting the other.
I would provide appropriate event with the model to allow the UI to be updated when the model changes as well as providing a means for each field to update the model as required.
I would then reduce the problem to it's smallest conceptional component, which would be the sub board, and generate the UI to represent it in the most abstract manner I could. This allows for re-use and helps with debugging, as if one board has an issue, then you can fix it in one place for all.
public class Sudoku {
public static void main(String[] args) {
new Sudoku();
}
public Sudoku() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new SudokuBoard());
frame.add(new MenuPane(), BorderLayout.AFTER_LINE_ENDS);
frame.pack();
frame.setVisible(true);
}
});
}
public class MenuPane extends JPanel {
public MenuPane() {
setBorder(new EmptyBorder(4, 4, 4, 4));
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(new JButton("Solve"), gbc);
gbc.gridy++;
add(new JButton("New"), gbc);
gbc.gridy++;
add(new JButton("Hint"), gbc);
gbc.gridy++;
add(new JButton("Reset"), gbc);
}
}
public class SudokuBoard extends JPanel {
public static final int ROWS = 3;
public static final int COLUMNS = 3;
private SubBoard[] subBoards;
public SudokuBoard() {
setBorder(new EmptyBorder(4, 4, 4, 4));
subBoards = new SubBoard[ROWS * COLUMNS];
setLayout(new GridLayout(ROWS, COLUMNS, 2, 2));
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLUMNS; col++) {
int index = (row * ROWS) + col;
SubBoard board = new SubBoard();
board.setBorder(new CompoundBorder(new LineBorder(Color.GRAY, 3), new EmptyBorder(4, 4, 4, 4)));
subBoards[index] = board;
add(board);
}
}
}
}
public class SubBoard extends JPanel {
public static final int ROWS = 9;
public static final int COLUMNS = 9;
private JTextField[] fields;
public SubBoard() {
setLayout(new GridLayout(ROWS, COLUMNS, 2, 2));
fields = new JTextField[ROWS * COLUMNS];
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLUMNS; col++) {
int index = (row * COLUMNS) + col;
JTextField field = new JTextField(4);
fields[index] = field;
// field.setText(Integer.toString(index));
add(field);
}
}
}
}
}
UPDATED
To limit the text fields to only allow numeric values to be entered, you can take a look at JTextField limiting character amount input and accepting numeric only for some ideas
UPDATED (with 2D arrays)
Here's a implementation that uses 2D arrays, it also sub groups the sub boards so that each grid of 3x3 fields has it's own board...
public class Sudoku {
public static void main(String[] args) {
new Sudoku();
}
public Sudoku() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new SudokuBoard());
frame.add(new MenuPane(), BorderLayout.AFTER_LINE_ENDS);
frame.pack();
frame.setVisible(true);
}
});
}
public class MenuPane extends JPanel {
public MenuPane() {
setBorder(new EmptyBorder(4, 4, 4, 4));
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(new JButton("Solve"), gbc);
gbc.gridy++;
add(new JButton("New"), gbc);
gbc.gridy++;
add(new JButton("Hint"), gbc);
gbc.gridy++;
add(new JButton("Reset"), gbc);
}
}
public class SudokuBoard extends JPanel {
public static final int ROWS = 3;
public static final int COLUMNS = 3;
private SubBoard[][] subBoards;
public SudokuBoard() {
setBorder(new EmptyBorder(4, 4, 4, 4));
subBoards = new SubBoard[ROWS][COLUMNS];
setLayout(new GridLayout(ROWS, COLUMNS, 2, 2));
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLUMNS; col++) {
int index = (row * ROWS) + col;
SubBoard board = new SubBoard();
board.setBorder(new CompoundBorder(new LineBorder(Color.GRAY, 3), new EmptyBorder(4, 4, 4, 4)));
subBoards[row][col] = board;
add(board);
}
}
}
}
public class SubBoard extends JPanel {
public SubBoard() {
setLayout(new GridLayout(3, 3, 2, 2));
for (int index = 0; index < 3*3; index++) {
add(new ChildBoard(3, 3));
}
}
}
public class ChildBoard extends JPanel {
private JTextField[][] fields;
public ChildBoard(int rows, int cols) {
setBorder(new LineBorder(Color.LIGHT_GRAY));
setLayout(new GridLayout(rows, cols, 2, 2));
fields = new JTextField[rows][cols];
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
JTextField field = new JTextField(4);
fields[row][col] = field;
add(field);
}
}
}
}
}
or, if you want to try and keep all the fields in a single top level reference you could do something like...
public class SubBoard extends JPanel {
private JTextField[][] fields;
public SubBoard() {
setLayout(new GridLayout(3, 3, 2, 2));
fields = new JTextField[9][9];
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
fields[row][col] = new JTextField(4);
}
}
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
int startRow = row * 3;
int startCol = col * 3;
add(new ChildBoard(3, 3, fields, startRow, startCol));
}
}
}
}
public class ChildBoard extends JPanel {
public ChildBoard(int rows, int cols, JTextField[][] fields, int startRow, int startCol) {
setBorder(new LineBorder(Color.LIGHT_GRAY));
setLayout(new GridLayout(rows, cols, 2, 2));
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
JTextField field = fields[startRow + row][startCol + col];
fields[row][col] = field;
add(field);
}
}
}
}
UPDATED with single class
Okay, so rather then sub-classing, simple use a couple of methods to create each individual section of the board, from which you can make repeated calls to...
Remember, reduce and reuse.
public class Sudoku {
public static void main(String[] args) {
new Sudoku();
}
public Sudoku() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new SudokuBoard());
frame.add(new MenuPane(), BorderLayout.AFTER_LINE_ENDS);
frame.pack();
frame.setVisible(true);
}
});
}
public class MenuPane extends JPanel {
public MenuPane() {
setBorder(new EmptyBorder(4, 4, 4, 4));
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(new JButton("Solve"), gbc);
gbc.gridy++;
add(new JButton("New"), gbc);
gbc.gridy++;
add(new JButton("Hint"), gbc);
gbc.gridy++;
add(new JButton("Reset"), gbc);
}
}
public class SudokuBoard extends JPanel {
public static final int GRID_ROWS = 3;
public static final int GRID_COLUMNS = 3;
public static final int BOARD_ROWS = 9;
public static final int BOARD_COLUMNS = 9;
private JTextField fields[][];
public SudokuBoard() {
setBorder(new EmptyBorder(4, 4, 4, 4));
fields = new JTextField[GRID_ROWS * BOARD_ROWS][GRID_COLUMNS * BOARD_COLUMNS];
setLayout(new GridLayout(GRID_ROWS, GRID_COLUMNS, 2, 2));
for (int row = 0; row < GRID_ROWS; row++) {
for (int col = 0; col < GRID_COLUMNS; col++) {
int startRow = row * GRID_ROWS;
int startCol = col * GRID_COLUMNS;
add(createBoard(fields, startRow, startCol));
}
}
}
protected JPanel createBoard(JTextField fiels[][], int startRow, int startCol) {
JPanel panel = new JPanel(new GridLayout(3, 3, 2, 2));
panel.setBorder(new CompoundBorder(new LineBorder(Color.DARK_GRAY, 2), new EmptyBorder(2, 2, 2, 2)));
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
int rowIndex = (startRow + row) * 3;
int colIndex = (startCol + col) * 3;
panel.add(createSubBoard(fields, rowIndex, colIndex));
}
}
return panel;
}
protected JPanel createSubBoard(JTextField[][] fields, int startRow, int startCol) {
JPanel panel = new JPanel(new GridLayout(3, 3, 2, 2));
panel.setBorder(new CompoundBorder(new LineBorder(Color.GRAY, 2), new EmptyBorder(2, 2, 2, 2)));
populateFields(fields, startRow, startCol);
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
panel.add(fields[row + startRow][col + startCol]);
}
}
return panel;
}
protected void populateFields(JTextField[][] fields, int startRow, int startCol) {
for (int row = startRow; row < startRow + 3; row++) {
for (int col = startCol; col < startCol + 3; col++) {
fields[row][col] = new JTextField(4);
}
}
}
}
}

Categories