Search code examples
javaswingjbutton

Extended JButton fails to appear until Mouse Over


The situation is that I'm making a panel filled with 8*8 buttons, like a matrix, and I'm going to have to change specific button texts depending on the position of the clicked button. It seemed like a good idea to make my own JButton where I can assign x and y indexes to the button, so I can check easily which button was clicked by indexes.

Here is the code for that:

import javax.swing.JButton;

public class MatrixButton extends JButton {
    private int x;
    private int y;
    
    public MatrixButton(int x, int y, String text){
        super(text);
        this.x = x;
        this.y = y;
    }
    
    public int getX() {
        return x;
    }
    public int getY() {
        return y;
    }
}

And it does solve the task, but these Matrix buttons doesn't want to appear until I mouse over them. What could be the problem?

here is the code for the panel which contains these buttons and handles their actions:

public class PlayField extends JPanel implements ActionListener {
    private MatrixButton[][] button = new MatrixButton[8][8];
    
    public PlayField(){
        Random rand = new Random();
        
        setLayout(new GridLayout(8, 8));
        
        for (int i = 0; i < 8; ++i){
            for (int j = 0; j < 8; ++j){
                button[i][j] = new MatrixButton(j, i, "" + rand.nextInt(80));
                button[i][j].addActionListener(this);
                
                add(button[i][j]);
            }
        }
    }
    
    private String incrementText(MatrixButton mb){
        return "" + (Integer.parseInt(mb.getText()) + 1);
    }
    
    @Override
    public void actionPerformed(ActionEvent e){
        MatrixButton mb = (MatrixButton)e.getSource();
        
        for (int i = mb.getY(); i >= 0; --i){
            button[i][mb.getX()].setText(incrementText(button[i][mb.getX()]));
        }
        for (int i = 0; i < 8; ++i){
            if (i != mb.getX())
            button[mb.getY()][i].setText(incrementText(button[mb.getY()][i]));
        }
    }
}

PS: if I fill with ordinary JButtons they appear as they should be. That's why I'm confused, because I didn't change much on the JButton extension just added 2 more variables to it.


Solution

  • This is dangerous code:

    public int getX() {
        return x;
    }
    public int getY() {
        return y;
    }
    

    You do realize that this overrides two of JButton's critical methods (actually, the methods are from JButton's parent, JComponent) that help set the placement of the button on the GUI (add @Override above the methods to see that this is so). I strongly suggest that you either change the signature of these methods, perhaps getGridX() and getGridY(), or even better, use composition and not inheritance, since you run into risk of these types of problems -- unwittingly overriding a key method -- when you extend complex classes such as Swing component classes. For this reason I try to avoid extending these classes unless absolutely necessary.

    For example:

    import java.awt.GridLayout;
    import java.awt.event.*;
    import java.util.Random;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class PlayField {
        private static final int ROWS = 8;
        private static final int COLS = ROWS;
        private static final int MAX_RAND = 80;
        private JPanel mainPanel = new JPanel();
        private MatrixButton[][] button = new MatrixButton[ROWS][COLS];
    
        public PlayField(){
            Random rand = new Random();
            mainPanel.setLayout(new GridLayout(ROWS, COLS));
            for (int i = 0; i < ROWS; ++i){
                for (int j = 0; j < COLS; ++j){
                    button[i][j] = new MatrixButton(j, i, rand.nextInt(MAX_RAND));
                    button[i][j].addActionListener(new MyMatrixListener(i, j));
                    mainPanel.add(button[i][j].getButton());
                }
            }
        }
    
        private class MyMatrixListener implements ActionListener {
            private int i;
            private int j;
    
            public MyMatrixListener(int i, int j) {
                this.i = i;
                this.j = j;
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                for (int i2 = 0; i2 < button.length; i2++) {
                    if (i2 != i) {
                        int value = button[i2][j].getValue();
                        value++;
                        button[i2][j].setValue(value);
                    }
                }
                for (int j2 = 0; j2 < button[i].length; j2++) {
                    if (j2 != j) {
                        int value = button[i][j2].getValue();
                        value++;
                        button[i][j2].setValue(value);
                    }
                }
            }
        }
    
        public JPanel getMainPanel() {
            return mainPanel;
        }
    
        private static void createAndShowGui() {
            JFrame
            frame = new JFrame("PlayField");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(new PlayField().getMainPanel());
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> createAndShowGui());
        }    
    }
    

    class MatrixButton {
        private int column;
        private int row;
        private JButton button = new JButton();
        private int value;
    
        public MatrixButton(int column, int row, int value) {
            this.column = column;
            this.row = row;
            setValue(value);
        }
    
        public void addActionListener(ActionListener listener) {
            button.addActionListener(listener);
        }
    
        public int getColumn() {
            return column;
        }
    
        public int getRow() {
            return row;
        }
    
        public JButton getButton() {
            return button;
        }
    
        public int getValue() {
            return value;
        }
    
        public final void setValue(int value) {
            this.value = value;
            button.setText("" + value);
        }
    }
    

    Better to just use ints and not Strings