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);
}
}
}
}
}
Related
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() );
}
}
I've been having a problem with the GridBagLayout for a few hours but I don't get to solve it. I have two JPanels (which contain JTextFields) in a JFrame. The first (upper) JPanel has a grid of 3 rows and 2 columns. The second (lower) JPanel has a grid of 3 rows and 3 columns. I need them to be aligned as in the next figure (white column in the upper JPanel means space; the numbers indicate the relative width) and the proportions to be consistent as the size of the window changes.
The code that I have is the next:
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
public class FrameWithTwoPanels {
private GridBagConstraints constraints;
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
FrameWithTwoPanels window = new FrameWithTwoPanels();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public FrameWithTwoPanels() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 800, 800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Populate the frame with two Panels
frame.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.weightx = 1;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridy = 0;
// PANEL 1: 3 rows and 2 columns
frame.add(this.createPanel(3, 2), c);
// PANEL 2: 3 rows and 3 columns
c.gridy = 1;
frame.add(this.createPanel(3, 3), c);
frame.pack();
frame.setLocationRelativeTo(null);
}
private JPanel createPanel(int numrows, int numcols){
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
constraints = new GridBagConstraints();
//constraints.fill = GridBagConstraints.BOTH;
for(int row=0; row<numrows; row++){
for(int col =0; col<numcols; col++){
JTextField component = new JTextField(row + "-" + col);
if(col == 0){
constraints.weightx = 0.5;
}else if(col == 1){
constraints.weightx = 0.4;
}else if(col == 2){
constraints.weightx = 0.1;
}
constraints.gridy = row;
constraints.gridx = col;
panel.add(component, constraints);
if(numcols == 2 && col == 1){
// Add auxiliar panel
constraints.weightx = 0.1;
constraints.gridx = col + 1;
panel.add(new JLabel());
}
}
}
panel.setBorder(new TitledBorder("Number of columns: " + numcols));
return panel;
}
}
The code gives the form shown in the screenshot:
This is a circumstance where you're actually better off using plain GridLayout. I think the following code does what you want:
private JPanel createPanel(int numrows, int numcols) {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(numrows, 3));
for (int row = 0; row < numrows; row++) {
for (int col = 0; col < numcols; col++) {
JTextField component = new JTextField(row + "-" + col);
panel.add(component);
if (numcols == 2 && col == 1) {
JPanel spacer = new JPanel();
panel.add(spacer);
}
}
}
panel.setBorder(new TitledBorder("Number of columns: " + numcols));
return panel;
}
This GUI is made from javax.swing. I'm wondering why it throws Array Index Out of Bounds exception but I'm sure that it's declared properly. I also tried running tests inside the for loops of the instantiation of the cells and the resetting of the cells but it still won't work. When I tried printing the i and j of the loop in the resetFields actionEvent it doesn't really go out of bounds but it says it goes out of bounds.
I found out that col adds one to itself when it goes to the actionPerformed function which is kinda weird.
NOTE: I get the row and col from the main class in another Java file.
Here's the full code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ExperimentalFrame extends JFrame implements ActionListener
{
private int row, col;
private JTextField cells[][];
private JPanel matrix;
private JButton compute1;
private JButton reset;
private JButton showSolution1;
private JButton resetFields;
private JPanel matrixPanel;
private JPanel mainPanel;
private JPanel buttonPanel;
private JLabel theory;
private JPanel theoryPanel;
private JLabel header[];
public ExperimentalFrame(int row, int col)
{
this.row = row; this.col = col;
theoryPanel = new JPanel();
theory = new JLabel("Theory here");
mainPanel = new JPanel();
matrix = new JPanel();
matrixPanel = new JPanel();
buttonPanel = new JPanel();
cells = new JTextField[row][col];
header = new JLabel[col];
compute1 = new JButton("Compute");
reset = new JButton("Reset");
showSolution1 = new JButton("Show Solution");
resetFields = new JButton("Reset Fields");
GridBagConstraints gbc = new GridBagConstraints();
GridBagConstraints gbc2 = new GridBagConstraints();
GridBagConstraints gbc1 = new GridBagConstraints();
matrixPanel.setLayout(new GridBagLayout());
matrix.setLayout(new GridBagLayout());
mainPanel.setLayout(new GridBagLayout());
theoryPanel.setLayout(new GridBagLayout());
buttonPanel.setLayout(new GridBagLayout());
compute1.addActionListener(this);
reset.addActionListener(this);
showSolution1.addActionListener(this);
resetFields.addActionListener(this);
gbc1.gridx = 1;
gbc1.gridy = 1;
gbc1.insets = new Insets(10,10,10,10);
theoryPanel.add(theory);
mainPanel.add(theoryPanel, gbc1);
for (int j = 0; j < col; j++)
{
gbc.gridy = 1;
gbc.gridx = j + 2;
if (j != (col - 1))
header[j] = new JLabel("I" + (j + 1));
else
header[j] = new JLabel("x");
matrix.add(header[j], gbc);
}
gbc.insets = new Insets(5, 5, 5, 5);
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
gbc.gridx = j + 2;
gbc.gridy = i + 3;
cells[i][j] = new JTextField(3);
System.out.println(i + " " + j);
matrix.add(cells[i][j], gbc);
}
}
for (int i = 0; i < row; i++)
{
gbc.gridx = 1;
gbc.gridy = i + 3;
matrix.add(new JLabel("Equation " + (i + 1) + ": "), gbc);
}
gbc2.insets = new Insets(10, 10, 10, 10);
gbc1.gridx = 1;
gbc1.gridy = 1;
gbc1.insets = new Insets(10,10,10,10);
matrixPanel.add(matrix, gbc1);
gbc1.gridy = 2;
gbc1.insets = new Insets(0, 10, 10, 10);
matrixPanel.add(compute1, gbc1);
matrixPanel.setBorder(BorderFactory.createLineBorder(Color.gray));
gbc1.gridx = 1;
gbc1.gridy = 2;
mainPanel.add(matrixPanel, gbc1);
//gbc1.gridy = 3;
gbc1.anchor = GridBagConstraints.LINE_END;
gbc1.gridx = 1; gbc1.gridy = 1;
gbc1.insets = new Insets(0, 5, 0, 0);
buttonPanel.add(reset, gbc1);
gbc1.gridx = 2; gbc1.gridy = 1;
buttonPanel.add(resetFields, gbc1);
gbc1.gridx = 1; gbc1.gridy = 3;
gbc1.insets = new Insets(0, 10, 10, 10);
mainPanel.add(buttonPanel, gbc1);
add(mainPanel);
pack();
setTitle("Experimental");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
#Override
public void actionPerformed(ActionEvent ae)
{
Object o = ae.getSource();
JButton jb = (JButton) o;
if (jb == reset)
{
this.setVisible(false);
this.dispose();
new Experiment10();
}
else if (jb == resetFields)
{
System.out.println(row + " " + col);
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
{
System.out.println(i + " " + j);
cells[i][j].setText(""); //here
}
}
}
}
Here's the error:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException:6
at ExperimentalFrame.actionPerformed(ExperimentalFrame.java:140)
It's because, when you declare cells = new JTextField[row][col]; col is not equal to this.col (this.col = col + 1)
So when you go for for(int j = 0; j < col; j++) in actionPerformed j is out of bound for this.cells
For example, when you instantiate your class with col = 5, the cells will be created with a 5 width, however, this.col will be equal to 6, and j will go the 5 value in you for loop.
To correct you issue, do that
public ExperimentalFrame(int row, int col){
this.row = row; this.col = col;
or, if you really need col to be increased by one, which I personnaly think is odd,
public ExperimentalFrame(int row, int col){
col++;
this.row = row; this.col = col;
Ditch the class variables (row, col) and iterate based on the array size. There is no need to maintain row and column variables once the array is created.
for (int i = 0; i < cells.length; i++)
for (int j = 0; j < cells[i].length; j++) {
System.out.println(i + " " + j);
cells[i][j].setText("");
}
Works as expected.
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;
}
}
The code below produces nine individual JPanels in which are 9 buttons. The nine JPanels are arranged onto a base JPanel using GridLayout. This base JPanel is then placed onto the ContentPane using Border Layout. I'm using borders for the JButtons and each individual JPanel to clearly define their separation. The JButtons inside each JPanel look fine but there is a gap between the JPanels which is causing the appearance of a double line which is bugging the hell out of me. I've tried adding each individual JPanel onto the ContentPane directly but this does nothing. Totally stumped and wondering if anyone has any suggestions?
P.S. I know the mass of duplicate code needs re-factoring in a bad way and it will, this is just for mock-up purposes.
Thanks,
import java.awt.*;
import javax.swing.*;
import javax.swing.UIManager.*;
public class View1 extends JFrame
{
private JPanel basePanel;
private JPanel childPanel1;
private JPanel childPanel2;
private JPanel childPanel3;
private JPanel childPanel4;
private JPanel childPanel5;
private JPanel childPanel6;
private JPanel childPanel7;
private JPanel childPanel8;
private JPanel childPanel9;
private int row = 3;
private int column = 3;
private JButton[][] squares1;
private JButton[][] squares2;
private JButton[][] squares3;
private JButton[][] squares4;
private JButton[][] squares5;
private JButton[][] squares6;
private JButton[][] squares7;
private JButton[][] squares8;
private JButton[][] squares9;
private Font numberFont;
public View1()
{
setSize(400,400);
setTitle("2013");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
basePanel = new JPanel();
basePanel.setLayout(new GridLayout(row, column, 0, 0));
numberFont = new Font("sansserif", Font.BOLD, 32);
childPanel1Setup();
childPanel2Setup();
childPanel3Setup();
childPanel4Setup();
childPanel5Setup();
childPanel6Setup();
childPanel7Setup();
childPanel8Setup();
childPanel9Setup();
basePanel.add(childPanel1);
basePanel.add(childPanel2);
basePanel.add(childPanel3);
basePanel.add(childPanel4);
basePanel.add(childPanel5);
basePanel.add(childPanel6);
basePanel.add(childPanel7);
basePanel.add(childPanel8);
basePanel.add(childPanel9);
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
cp.add(basePanel, BorderLayout.CENTER);
}
public void childPanel1Setup()
{
childPanel1 = new JPanel();
childPanel1.setLayout(new GridLayout(row, column, 0, 0));
childPanel1.setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares1 = new JButton[row][column];
for(int rows = 0; rows < this.row; rows++)
{
for(int cols = 0; cols < this.column; cols++)
{
squares1[rows][cols] = new JButton();
squares1[rows][cols].setSize(32, 32);
squares1[rows][cols].setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares1[rows][cols].setBackground(Color.WHITE);
childPanel1.add(squares1[rows][cols]);
}
}
}
public void childPanel2Setup()
{
childPanel2 = new JPanel();
childPanel2.setLayout(new GridLayout(row, column, 0, 0));
childPanel2.setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares2 = new JButton[row][column];
for(int rows = 0; rows < this.row; rows++)
{
for(int cols = 0; cols < this.column; cols++)
{
squares2[rows][cols] = new JButton();
squares2[rows][cols].setSize(32, 32);
squares2[rows][cols].setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares2[rows][cols].setBackground(Color.WHITE);
childPanel2.add(squares2[rows][cols]);
}
}
}
public void childPanel3Setup()
{
childPanel3 = new JPanel();
childPanel3.setLayout(new GridLayout(row, column, 0, 0));
childPanel3.setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares3 = new JButton[row][column];
for(int rows = 0; rows < this.row; rows++)
{
for(int cols = 0; cols < this.column; cols++)
{
squares3[rows][cols] = new JButton();
squares3[rows][cols].setSize(32, 32);
squares3[rows][cols].setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares3[rows][cols].setBackground(Color.WHITE);
childPanel3.add(squares3[rows][cols]);
}
}
}
public void childPanel4Setup()
{
childPanel4 = new JPanel();
childPanel4.setLayout(new GridLayout(row, column, 0, 0));
childPanel4.setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares4 = new JButton[row][column];
for(int rows = 0; rows < this.row; rows++)
{
for(int cols = 0; cols < this.column; cols++)
{
squares4[rows][cols] = new JButton();
squares4[rows][cols].setSize(32, 32);
squares4[rows][cols].setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares4[rows][cols].setBackground(Color.WHITE);
childPanel4.add(squares4[rows][cols]);
}
}
}
public void childPanel5Setup()
{
childPanel5 = new JPanel();
childPanel5.setLayout(new GridLayout(row, column, 0, 0));
childPanel5.setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares5 = new JButton[row][column];
for(int rows = 0; rows < this.row; rows++)
{
for(int cols = 0; cols < this.column; cols++)
{
squares5[rows][cols] = new JButton();
squares5[rows][cols].setSize(32, 32);
squares5[rows][cols].setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares5[rows][cols].setBackground(Color.WHITE);
childPanel5.add(squares5[rows][cols]);
}
}
}
public void childPanel6Setup()
{
childPanel6 = new JPanel();
childPanel6.setLayout(new GridLayout(row, column, 0, 0));
childPanel6.setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares6 = new JButton[row][column];
for(int rows = 0; rows < this.row; rows++)
{
for(int cols = 0; cols < this.column; cols++)
{
squares6[rows][cols] = new JButton();
squares6[rows][cols].setSize(32, 32);
squares6[rows][cols].setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares6[rows][cols].setBackground(Color.WHITE);
childPanel6.add(squares6[rows][cols]);
}
}
}
public void childPanel7Setup()
{
childPanel7 = new JPanel();
childPanel7.setLayout(new GridLayout(row, column, 0, 0));
childPanel7.setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares7 = new JButton[row][column];
for(int rows = 0; rows < this.row; rows++)
{
for(int cols = 0; cols < this.column; cols++)
{
squares7[rows][cols] = new JButton();
squares7[rows][cols].setSize(32, 32);
squares7[rows][cols].setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares7[rows][cols].setBackground(Color.WHITE);
childPanel7.add(squares7[rows][cols]);
}
}
}
public void childPanel8Setup()
{
childPanel8 = new JPanel();
childPanel8.setLayout(new GridLayout(row, column, 0, 0));
childPanel8.setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares8 = new JButton[row][column];
for(int rows = 0; rows < this.row; rows++)
{
for(int cols = 0; cols < this.column; cols++)
{
squares8[rows][cols] = new JButton();
squares8[rows][cols].setSize(32, 32);
squares8[rows][cols].setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares8[rows][cols].setBackground(Color.WHITE);
childPanel8.add(squares8[rows][cols]);
}
}
}
public void childPanel9Setup()
{
childPanel9 = new JPanel();
childPanel9.setLayout(new GridLayout(row, column, 0, 0));
childPanel9.setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares9 = new JButton[row][column];
for(int rows = 0; rows < this.row; rows++)
{
for(int cols = 0; cols < this.column; cols++)
{
squares9[rows][cols] = new JButton();
squares9[rows][cols].setSize(32, 32);
squares9[rows][cols].setBorder(BorderFactory.createLineBorder(Color.BLACK));
squares9[rows][cols].setBackground(Color.WHITE);
childPanel9.add(squares9[rows][cols]);
}
}
}
}
I believe it's because the Layout Manager for JPanels is FlowLayout which includes a margin. When you create the panels, set their layout to GridLayout which will give you 0 margin.
Also have you done setVgap(0); and setHgap(0); for the GridLayout?
GridLayout makes all component to be of the same size. If it needs to layout matrix of 3x3 components inside JPanel that it 100x100 pixels, each component will be 33x33 pixels and there will be on extra pixel at the right and at the bottom because 100 is not factor of 3. If JPanel is 101x101 pixels, there will be extra pixels at all four edges.
Consider using GridBagLayout or Box or some other layout that is smarter than GridLayout.