0

I have a take home assignment where I need to make a sudoku board that displays integers from a file on a board and allows someone to click a JButton and input a missing value.

I have gotten the board to show up using JPanel and printed the text file to the individual buttons but I can't figure out how to get the addActionListener to pick up any of the buttons that are missing values. It only works for the last button that is blank. (Blank buttons are given a value of 0).

my question is why is the last blank button only being targeted. There are 6 in total but only the last one brings up the dialogue box after being clicked?

public class MyCustomeFrame extends JFrame {
    private int[][] numbers;
    private String[] nums;
    JButton b1;
    JButton b2;
    JButton b3;
    JButton b4;

    private JPanel p2;

    public MyCustomeFrame() {
        // Create the border layout
        setLayout(new BorderLayout(5, 5));

        // Create a new panel for the buttons to be placed on
        JPanel p1 = new JPanel();

        // Create 3 buttons

        b1 = new JButton("Load");
        b2 = new JButton("Save");
        b3 = new JButton("Check");

        // Adds the 3 buttons to the panel
        p1.add(b1);
        p1.add(b2);
        p1.add(b3);

        // Create the event handlers for when the button is pressed
        b1.addActionListener(new MyButtonHandler());
        b2.addActionListener(new MyButtonHandler());
        b3.addActionListener(new MyButtonHandler());


        // Place the panel south in the window
        add(p1, BorderLayout.SOUTH);

        p2 = new JPanel();

        // Define the grid parameters
        p2.setLayout(new GridLayout(9, 9, 5, 5));

        // Show the grid
        add(p2, BorderLayout.CENTER);
        int[][] numbers = new int[9][9];
        int rowIdx = 0;



        //This is where i read the input file located on my computer and place the numbers on the Sudoku board
        try {
            BufferedReader bReader = new BufferedReader(new FileReader(
                "C:\\Users\\Derek\\Desktop\\input.txt"));

            String line = bReader.readLine();

            while (line != null) {
                nums = line.split(",");

                for (int i = 0; i < numbers[0].length; i++) {
                    numbers[rowIdx][i] = Integer.parseInt(nums[i]);

                    // This creates the individual buttons that are then placed on the board
                    if (numbers[rowIdx][i] >= 1) {
                        p2.add(new JButton(nums[i]));
                    } else {

                        //this is where I'm having the issue
                        b4 = new JButton(" ");
                        p2.add(b4);
                        b4.addActionListener(new MyButtonHandler());
                    }
                }

                rowIdx++;
                line = bReader.readLine();
            }
            bReader.close();
        } catch (FileNotFoundException g) {
            System.out.println("File Not Found!");
        } catch (IOException g) {
            System.out.println("Something went wrong...Try Again");
            g.printStackTrace();
        }
    }

    class MyButtonHandler implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == b1) {
                System.out.println("Loading File...");

            } else if (e.getSource() == b2) {
                System.out.println("Saving File...");

                try {
                    BufferedWriter bWriter = new BufferedWriter(new FileWriter(
                        new File("C:\\SudokuSave.txt"), true));

                    bWriter.close();

                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            } else if (e.getSource() == b3) {
                System.out.println("Checking Solution...");

            } else if (e.getSource() == b4) {
                System.out.println("clicked");
                JOptionPane.showInputDialog("Input a number between 1 - 9");
            }
        }
    }
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
D-Rock
  • 3
  • 1

2 Answers2

2

The reason for the problem was already pointed out by Vyacheslav in https://stackoverflow.com/a/21803753

Some hints:

You should use proper variable names. Calling a JButton b2 is horrible. When it is a "Load" button, then call it loadButton. When it is a "Save" button, call it saveButton. Code is written (at most) once, but read possibly hundreds of times. And Java code should read like prose, in the best case.

Reading a file with a hard-coded name in the constructor, in order to build the GUI components, is a very bad practice. You should consider creating some "Data model" that contains the information that you can create your GUI from, and split the process of

  1. reading the file and store the data in the data model, and
  2. creating the GUI from a data model.

This will also allow you to handle Exceptions better than by printing

System.out.println("Something went wrong...Try Again");

In order to resolve your problem, you might consider using anonymous listeners. Creating a single ActionListener that is responsible for all buttons is not very flexible. Usually, you only want to associate a click on a button with a single call to a (private) method. So for example, you could write

JButton saveButton = new JButton("Save");
saveButton.addActionListener(new ActionListener() {
{
    @Override
    public void actionPerformed(ActionEvent) {
        saveButtonWasPressed();
    }
});

Particularly for the case that you have several buttons wit similar functionality, this approach offers an advantage: You can create anonymous listeners for each button, each of them containing the required information about which button was clicked - roughly applied to your code:

if (numbers[rowIdx][i] == 0) {
    JButton b = new JButton(" ");
    panel.add(b);
    b.addActionListener(createActionListener(rowIdx, i));
}

...

private ActionListener createActionListener(
    final int row, final int column) {
    ActionListener actionListener = new ActionListener() {
    {
        @Override
        public void actionPerformed(ActionEvent) {
            System.out.println("Pressed button in row "+row+", column "+column);
        }
    };
    return actionListener;
}
Community
  • 1
  • 1
Marco13
  • 53,703
  • 9
  • 80
  • 159
  • 1
    Also consider [`Action`](http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html) "to separate functionality and state from a component." – trashgod Feb 15 '14 at 22:30
  • @trashgod Definitely! For a grid of buttons of a sudoku board, this may not be sooo beneficial, but for the standard actions (Load, Save..) Actions are definitely the way to go, particularly when the same functionality should be used in a Button and a MenuItem, or when something like I18N comes into play... – Marco13 Feb 16 '14 at 15:28
  • [`JDigit`](http://stackoverflow.com/a/4151403/230513) is an example that uses `AbstractAction`. – trashgod Feb 16 '14 at 20:24
1

Your mistake is pretty simple - every iteration of for you are assigning a new JButton object reference to b4 variable so finally b4 refers to last JButton you had created.

Viacheslav
  • 5,443
  • 1
  • 29
  • 36