Tykher
Tykher

Reputation: 13

Writing a small app for a DnD puzzle and have trouble with a JButton array

After adding an object of class Puzzle to Main everything displays mostly as it should. when I click any of the buttons some indexes of state should swap to the opposite so from true to false or from false to true.

unfortunately button clicking doesn't want to register for any of the buttons from the array yet it does register for a single button that was initialized by itself. How can I fix the problem?

my code:


import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;

public class Puzzle extends JFrame implements ActionListener
{
    int doors = 8;
    boolean [] state = new boolean[doors];
    JButton [] levers = new JButton[doors];
    JButton weird = new JButton("weird lever");
    JLabel display = new JLabel();


    Puzzle()
    {
        reset();

        this.setSize(new Dimension(1920, 1080));
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
        this.setVisible(true);
        this.setResizable(false);
        this.setLayout(null);
        this.add(display);
        this.add(weird);

        int num = levers.length;
        int start = 50;
        int size = (1920-(num+1)*start)/num;
        char label = 'A';
        display.setBounds(size*2, 150, 2000, 300);
        display.setFont(new Font("Arial Black", Font.PLAIN, 200));
        Display();
        for(JButton i : levers)
        {
            i = new JButton(String.valueOf(label));
            label++;
            i.setBounds(start, 500, size, size);
            start+=(size+50);
            i.addActionListener(this);
            i.setFont(new Font("Arial black", Font.PLAIN, size/2));
            i.setFocusable(false);
            this.add(i);
        }
        weird.setFocusable(false);
        weird.setBounds(550, 800, 800, 200);
        weird.setFont(new Font("Arial Black", Font.PLAIN, size/2));
        weird.addActionListener(this);





    }

    @Override
    public void actionPerformed(ActionEvent e)
    {

        /*if(e.getSource() == levers[0])
        {
            state[2] = Swap(state[2]);
            Display();

        }
         if(e.getSource() == levers[1])
        {
            state[4] = Swap(state[4]);
            state[6] = Swap(state[6]);
            Display();
        }
         if(e.getSource() == levers[2])
        {
            state[2] = Swap(state[2]);
            state[3] = Swap(state[3]);
            state[6] = Swap(state[6]);
            state[7] = Swap(state[7]);
            Display();
        }
         if(e.getSource() == levers[3])
        {
            state[0] = Swap(state[0]);
            state[2] = Swap(state[2]);
            state[7] = Swap(state[7]);
            Display();
        }
        if(e.getSource() == levers[4])
        {
            state[1] = Swap(state[1]);
            state[3] = Swap(state[3]);
            state[4] = Swap(state[4]);
            state[5] = Swap(state[5]);
            Display();
        }
         if(e.getSource() == levers[5])
        {
            state[0] = Swap(state[0]);
            state[2] = Swap(state[2]);
            state[6] = Swap(state[6]);
            Display();
        }
         if(e.getSource() == levers[6])
        {
            state[1] = Swap(state[1]);
            state[5] = Swap(state[5]);
            Display();
        }
         if(e.getSource() == levers[7])
        {
            state[1] = Swap(state[1]);
            state[2] = Swap(state[2]);
            state[4] = Swap(state[4]);
            state[5] = Swap(state[5]);
            Display();
        }
        */

        if(e.getSource() == levers[0])
        {
            display.setText("A works");
        }
         if(e.getSource() == weird)
        {
            display.setText("test");
        }

    }

      boolean Swap(boolean n)
      {
          return !n;
      }

    void Display()
    {
        StringBuilder toDisplay = new StringBuilder();
        for (boolean j : state)
        {
            if (j)
            {
                toDisplay.append("| ");
            } else
                toDisplay.append("_ ");
        }
        display.setText(toDisplay.toString());
    }

    void reset ()
    {
        Arrays.fill(state, true);
    }

}```

Upvotes: 1

Views: 62

Answers (2)

Gilbert Le Blanc
Gilbert Le Blanc

Reputation: 51567

Introduction

Your code was too complex for me to understand. I like simple code. Short methods and simple classes.

Here's the GUI I came up with.

Weird Lever

Here's the GUI after I clicked a couple of letter JButtons

Weird Lever

Explanation

Oracle has a nifty tutorial, Creating a GUI With JFC/Swing, which will show you how to create a Swing GUI. Skip the Netbeans section.

Your code was missing a main method, so I added one. I started the Swing application with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.

The first thing I did was create a PuzzleModel class to hold the boolean array. It's a good idea to separate your model from your view and controller classes. This pattern is the model / view / controller (MVC) pattern.

A Swing JFrame can contain many JPanels. I created a segment JPanel to hold a JLabel and a JButton aligned vertically. I used the GridBagLayout to align the JLabel and JButton. Swing layout managers help you to avoid absolute positioning and the problems that come with absolute positioning.

I created a main JPanel to hold 8 segment JPanels. These JPanels are aligned with a FlowLayout.

As you can see, my JFrame is smaller than yours. You create as small a JFrame as possible. If the user wants to make it bigger, that's what the rectangle in the upper right corner is for.

Swing is meant to be designed from the inside out. You don't specify a JFrame size and try to make the components fit. You create the components and let Swing determine the size of the JFrame. If you want the JFrame I created to be bigger, increase the font sizes. Hint: A fraction or multiple of 72 points looks better on most displays.

I created two ActionListener classes, one for the alphabet JButtons and one for the lever JButton. This makes it easier to focus on the alphabet JButtons. All you have to do in the ActionListener is swap the appropriate isVertical boolean values when each JButton is left-clicked. I just flipped the corresponding boolean as a demonstration.

Code

Here's the complete runnable code.

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;

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 PuzzleGUI implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new PuzzleGUI());
    }
    
    private JLabel[] leverLabel;
    
    private final PuzzleModel model;

    public PuzzleGUI() {
        this.model = new PuzzleModel();
    }
    
    @Override
    public void run() {
        JFrame frame = new JFrame("Weird Lever");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        frame.add(createMainPanel(), BorderLayout.CENTER);
        frame.add(createButtonPanel(), BorderLayout.AFTER_LAST_LINE);
        
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
        
        System.out.println(frame.getSize());
    }
    
    private JPanel createMainPanel() {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        
        char c = 'A';
        boolean[] isVertical = model.getIsVertical();
        leverLabel = new JLabel[isVertical.length];
        for (int i = 0; i < isVertical.length; i++) {
            String labelText = (isVertical[i]) ? "|" : "-";
            panel.add(createLeverButtonPanel(labelText, Character.toString(c), i));
            c = (char) (((int) c) + 1);
        }
        
        return panel;
    }
    
    public void updateMainPanel() {
        boolean[] isVertical = model.getIsVertical();
        for (int i = 0; i < isVertical.length; i++) {
            String labelText = (isVertical[i]) ? "|" : "-";
            leverLabel[i].setText(labelText);
        }
    }
    
    private JPanel createLeverButtonPanel(String labelText, String buttonText, int index) {
        JPanel panel = new JPanel(new GridBagLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        
        Font font1 = new Font("Arial Black", Font.PLAIN, 144);
        Font font2 = new Font("Arial Black", Font.PLAIN, 72);
        
        GridBagConstraints gbc = new GridBagConstraints();
        
        gbc.gridx = 0;
        gbc.gridy = 0;
        leverLabel[index] = new JLabel(labelText);
        leverLabel[index].setFont(font1);
        panel.add(leverLabel[index], gbc);
        
        gbc.gridy++;
        JButton button = new JButton(buttonText);
        button.addActionListener(new AlphabetButtonListener());
        button.setFont(font2);
        panel.add(button, gbc);
        
        return panel;
    }
    
    private JPanel createButtonPanel() {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        
        Font font2 = new Font("Arial Black", Font.PLAIN, 48);
        
        JButton button = new JButton("Weird Lever");
        button.addActionListener(new LeverButtonListener());
        button.setFont(font2);
        panel.add(button);
        
        return panel;
    }

    public class AlphabetButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent event) {
            JButton button = (JButton) event.getSource();
            String text = button.getText();
            char c = text.charAt(0);
            int index = ((int) c - 'A');
            model.swap(index);
            updateMainPanel();
        }
        
    }
    
    public class LeverButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent event) {
            // TODO Auto-generated method stub
            
        }
        
    }

    public class PuzzleModel {
        
        private boolean[] isVertical;
        
        public PuzzleModel() {
            int doors = 8;
            this.isVertical = new boolean[doors];
            reset();
        }
        
        private void reset() {
            Arrays.fill(isVertical, true);
        }
        
        public void swap(int index) {
            isVertical[index] = !isVertical[index];
        }

        public boolean[] getIsVertical() {
            return isVertical;
        }
        
    }
    
}

Upvotes: 2

camickr
camickr

Reputation: 324167

button clicking doesn't want to register for any of the buttons from the array yet it does register for a single button

System.out.println( levers[0] );

if(e.getSource() == levers[0])
{
    display.setText("A works");
}

Add some debug code to your ActionListener and you will see that the value of levers[0] is "null".

    for(JButton i : levers)
    {
        i = new JButton(String.valueOf(label));
        label++;
        i.setBounds(start, 500, size, size);
        start+=(size+50);
        i.addActionListener(this);
        i.setFont(new Font("Arial black", Font.PLAIN, size/2));
        i.setFocusable(false);
        this.add(i);
    }

You create the button, but you never add the instance of each button to the Array.

for(JButton i : levers)

Why would you use "i" as the variable name. Typically "i" is used as an index. Use a proper variable name, like "button". However, in this case you don't want to use a "for each" loop.

Instead you want a normal for loop so you can index your Array to add each button as you create it:

//for(JButton i : levers)
for (int i = 0; i < doors; i++)
{
    JButton button = new JButton(String.valueOf(label));
    levers[i] = button;
    ...

Other issues:

  1. method names should NOT start with an upper case character.
  2. components should be added to the frame BEFORE the frame is made visible.
  3. components should be created on the Event Dispatch Thread (EDT).
  4. Don't use a null layout and setBounds(). Swing was designed to be used with layout managers.
  5. Don't hardcode screen sizes. Instead you can use frame.setExtendedState(JFrame.MAXIMIZED_BOTH);, so it will work for all screen sizes.

Upvotes: 2

Related Questions