0

There is a JPanel with GridLayout which is randomly populated by JButtons created using an ArrayList. Each element of the array (a Tile) contains a character. It's supposed to be a tile removing game. However, I realised that the code I wrote didn't work, because once a tile is removed, the others' indexes change.

A KeyListener is applied to the JPanel. This method is in my Model class, where the ArrayList is first created. getChar, in the Tile class, simply returns its character.

public void removeChar(char typedChar) {
    for(Tile t: _al){
            if(t.getChar() == typedChar) {
                System.out.println(typedChar + " is in the game, at index " + _al.indexOf(t) + " of the array.");
                _p.remove(_al.indexOf(t));
            }

With my code, I basically just want to remove the corresponding tile when its character key is typed. Can anyone help me see a better (well, working) way to do this? My code seems to become much more complicated because the ArrayList is full of objects, but I will need the objects later.

  • 1
    Do not remove the value, just replace it with something you can check and exclude while filling your JPanel `_p.set(_al.indexOf(t), null)` <- Here I did it with `null` for example. – Yassin Hajaj Nov 19 '15 at 16:13
  • 1
    *" It's supposed to be a tile removing game."* I think a better strategy here would be to completely fill the grid with buttons at start-up, then set the button icons according to a model of the game. It would be possibly to use a completely transparent icon for a tile that is hidden or disable a button that for whatever reason cannot be selected. – Andrew Thompson Nov 19 '15 at 16:16
  • Yassin, I'm confused by your comment. Do you mean I should set the value of each JButton to be `_p.set(_al.indexOf(t), null)`? And Andrew, I've never really coded before so I'm not sure exactly what you mean. My game is supposed to be a clone of KeyBricks, basically. –  Nov 19 '15 at 16:26
  • Tip: Add @YassinHajaj (or whoever, the `@` is important) to notify the person of a new comment. – Andrew Thompson Nov 19 '15 at 16:30
  • @MaddoxJKingsley No. Basically, instead of using `remove`, use `set` and it will not change your List's length. Then, when filling your JPanel with buttons, check if for example the index is null and do not treat it as the other indexes. – Yassin Hajaj Nov 19 '15 at 16:33
  • *"My game is supposed to be a clone of KeyBricks, basically"* Sorry, not familiar with it. Anything like [Chess](http://stackoverflow.com/q/21142686/418556) or Battleship or Tetris? I think any of those games could be created as I described. The important thing that would be constant for any game is the model (or game state) that determines what should appear in the view (the components we are changing to display the game progress). – Andrew Thompson Nov 19 '15 at 16:40
  • @YassinHajaj Ahh, I understand what you mean now. That will actually be very useful for a later step in my project. Though, just to clarify, do you mean I should use `set` on the ArrayList, `_al`? `_p` is the JPanel where the JButtons are, so no such thing as `set` exists for it. –  Nov 19 '15 at 16:43
  • @MaddoxJKingsley OK sorry I did not understood that _p stood for JPanel. – Yassin Hajaj Nov 19 '15 at 16:45
  • @AndrewThompson In KeyBricks, when a key is typed, the block with that letter and any surrounding it of the same colour will be removed. It's a simple browser game, so if you google it, you can see a quick example if you wish. As to the model, it's something which I am attempting to do. As the project is becoming more complicated and multiple steps are added, it's become necessary. I think I stated it badly, but the GUI and data model are separated, basically. –  Nov 19 '15 at 16:47

1 Answers1

2

Don't use a KeyListener. Swing was designed to be used with Key Bindings.

Add a Key Binding to each button. Then when the letter is typed the button will be the source of the event so you can just remove the button from the panel.

For an example of how key bindings work see below:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class CalculatorPanel extends JPanel
{
    private JTextField display;

    public CalculatorPanel()
    {
        Action numberAction = new AbstractAction()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
//              display.setCaretPosition( display.getDocument().getLength() );
                display.replaceSelection(e.getActionCommand());
            }
        };

        setLayout( new BorderLayout() );

        display = new JTextField();
        display.setEditable( false );
        display.setHorizontalAlignment(JTextField.RIGHT);
        add(display, BorderLayout.NORTH);

        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout( new GridLayout(0, 5) );
        add(buttonPanel, BorderLayout.CENTER);

        for (int i = 0; i < 10; i++)
        {
            String text = String.valueOf(i);
            JButton button = new JButton( text );
            button.addActionListener( numberAction );
            button.setBorder( new LineBorder(Color.BLACK) );
            button.setPreferredSize( new Dimension(30, 30) );
            buttonPanel.add( button );

            InputMap inputMap = button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
            inputMap.put(KeyStroke.getKeyStroke(text), text);
            inputMap.put(KeyStroke.getKeyStroke("NUMPAD" + text), text);
            button.getActionMap().put(text, numberAction);
        }
    }

    private static void createAndShowUI()
    {
        JFrame frame = new JFrame("Calculator Panel");
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.add( new CalculatorPanel() );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible(true);
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}

So in your case the code in the actionPerformed() method of the Action would be something like:

JButton button = (JButton)e.getSource();
JPanel parent = (JPanel)button.getParent();
parent.remove(button);
parent.revalidate();
parent.repaint();

Edit:

When using a KeyListener you might use HashMap to bind the character to the related button:

HashMap<Character, JButton> buttons = new HashMap<Character, JButton>();
...
buttons.put('a', aButton);
buttons.put('b', bButton);

Then the keyTyped() code in the KeyListener code would be something like:

JButton button = buttons.get( e.getKeyChar() );
panel.remove( button );
panel.revalidate();
panel.repaint();
camickr
  • 321,443
  • 19
  • 166
  • 288
  • My professor always says "KeyListener", so I think I'm expected to use it in my project. Everyone always says that Key Bindings are supposed to be used for Swing components on here, but not once have they been mentioned in my class. If I use Key Bindings instead, will that get rid of the index problem I'm having? –  Nov 19 '15 at 16:33
  • @MaddoxJKingsley - `If I use Key Bindings instead, will that get rid of the index problem I'm having?` - did you try my suggestion with the code I gave you? You don't learn by asking. You learn by trying (and sometimes making mistakes). See edit for alternative suggestion. – camickr Nov 19 '15 at 16:53