2017-07-03 7 views
0

Donc, j'ai plusieurs objets de la classe Square, qui est la sous-classe de JButton. J'ai une instance de la classe Board, qui contient quelques instances de Square. Ce que je veux faire, c'est quand j'appuie sur l'un des boutons (carrés), dessinez une forme (un cercle) sur le dessus. Pour ce faire, j'ai une variable booléenne dans la classe Square, à savoir isClicked, qui décide en gros de ce qui doit être dessiné dans la méthode paintComponent.Dessin au-dessus de plusieurs JButtons

Le problème est que les boutons commencent à se comporter bizarrement quand j'en ai quelques-uns. Étonnamment, s'il n'y en a qu'un seul, il n'y a aucun problème. Au début, j'avais pensé que le problème pouvait être lié aux threads, cependant, j'ai mis le code principal dans la méthode invokeLater et cela n'a pas aidé du tout.

J'ai vu une solution en utilisant BufferedImage, mais je voudrais voir s'il y a une possibilité de résoudre le problème à ma façon.

Désolé pour l'anglais peut-être pas parfait.

classe Square:

public class Square extends JButton implements ActionListener { 

private int number; 
private boolean isClicked; 

public Square(int x) { 
    number = x; 
    isClicked = false; 
} 

@Override 
protected void paintComponent(Graphics g) { 
    Graphics2D g2d = (Graphics2D) g; 
    if (!isClicked) { 
     super.paintComponent(g); 
    } else { 
     System.out.println("EXECUTED for: " + number); 
     g2d.drawOval(this.getX(), this.getY(), 100, 100); 
    } 
} 

@Override 
public void actionPerformed(ActionEvent e) { 
    isClicked = !isClicked; 
    System.out.println(isClicked + " " + number); 
    repaint(); 
} 

} 

classe Conseil:

public class Board extends JPanel { 

private static final int BOARD_WIDTH = (int) (TicTacToe.WIDTH * 0.7); 
private static final int VERTICAL_LINE_LENGTH = (int) (TicTacToe.WIDTH * 0.5); 
private static final int HORIZONTAL_LINE_LENGTH = (int) (TicTacToe.HEIGHT * 0.8); 
private static final int STROKE_WIDTH = 5; 

private Square[] squares; 

public Board() { 

} 

public void addButtons() { 
    squares = new Square[9]; 

    for (int i = 0; i < 3; i++) { 
      Square square = new Square(i); 
      square.setPreferredSize(new Dimension(30, 30)); 
      square.addActionListener(square); 
      this.add(square); 
      squares[i] = square; 
      ((GridLayout)this.getLayout()).setHgap(30); 
      ((GridLayout)this.getLayout()).setVgap(30); 
    } 
} 

public Square[] getButtons() { 
    return squares; 
} 

@Override 
protected void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    Graphics2D g2d = (Graphics2D) g; 
    g2d.setStroke(new BasicStroke(STROKE_WIDTH)); 
    // Horiztontal lines 
    g2d.drawLine(0, TicTacToe.HEIGHT/3, 
      BOARD_WIDTH, TicTacToe.HEIGHT/3); 
    g2d.drawLine(0, 2 * TicTacToe.HEIGHT/3, 
      BOARD_WIDTH, 2 * TicTacToe.HEIGHT/3); 
    // Vertical lines 
    g2d.drawLine(BOARD_WIDTH/3, 0, BOARD_WIDTH/3, 
      TicTacToe.HEIGHT); 
    g2d.drawLine(2 * BOARD_WIDTH/3, 0, 2 * BOARD_WIDTH/3, 
      TicTacToe.HEIGHT); 

} 
} 

méthode principale:

SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      Board board = new Board(); 
    board.setPreferredSize(new Dimension((int) (WIDTH * 0.7), HEIGHT)); 
    board.setLayout(new GridLayout(3, 3)); 
    board.addButtons(); 

    GameOptions opt = new GameOptions(); 
    opt.setPreferredSize(new Dimension((int) (WIDTH * 0.3), HEIGHT)); 


    JFrame frame = new JFrame("Tic Tac Toe"); 
    frame.setLayout(new FlowLayout()); 
    frame.add(board); 
    frame.add(opt); 

    frame.pack(); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    frame.setVisible(true); 
     } 
    }); 

Répondre

1

Votre utilisation de getX() et getY() sur le code de dessin du bouton est complètement faux et ne n'appartient pas. Ces méthodes renvoient l'emplacement du bouton par rapport à son conteneur, et alors que cela peut fonctionner pour un bouton situé en haut à gauche, il échouera pour autre chose puisque vous finirez par dessiner quelque part loin du bouton lui-même, et tant de vos dessins ne seront jamais montrés.

Il vaudrait mieux ne pas étendre JButton mais simplement échanger des ImageIcons qui affichent ce que vous voulez dessiner sur le JButton. C'est beaucoup plus simple et beaucoup plus idiot. Vous définissez l'icône du bouton en appelant le .setIcon(myImageIcon), en passant dans l'icône de votre choix. Mais si vous vouliez absolument dessiner sur le bouton, vous le feriez sans utiliser getX() ou getY(). Vous voudrez probablement utiliser un JToggleButton en tant que classe parent, puisque vous êtes en train de changer d'état. Par exemple, mon MCVE:

import java.awt.BasicStroke; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.GridLayout; 
import java.awt.RenderingHints; 
import java.awt.Stroke; 
import java.awt.event.ItemEvent; 
import java.awt.event.ItemListener; 
import java.awt.image.BufferedImage; 
import javax.swing.*; 

@SuppressWarnings("serial") 
public class DrawButtonPanel extends JPanel { 
    private static final int SIDE = 3; 
    private static final int GAP = 5; 
    private static final Color BG = Color.BLACK; 

    public DrawButtonPanel() { 
     setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP)); 
     setLayout(new GridLayout(SIDE, SIDE, GAP, GAP)); 
     setBackground(BG); 
     for (int i = 0; i < SIDE * SIDE; i++) { 
      // add(new DrawButton1()); 
      DrawButton2 drawButton2 = new DrawButton2(i); 
      AbstractButton button = drawButton2.getButton(); 
      add(button); 
     } 
    } 

    private static void createAndShowGui() { 
     JFrame frame = new JFrame("Test"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.getContentPane().add(new DrawButtonPanel()); 
     frame.pack(); 
     frame.setLocationRelativeTo(null); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(() -> createAndShowGui()); 
    } 
} 

class DrawButton2 { 
    private static final int PREF_W = 200; 
    private static final int PREF_H = PREF_W; 
    private static final int GAP = 20; 
    private static final float STROKE_WIDTH = 15f; 
    private static final Stroke BASIC_STROKE = new BasicStroke(STROKE_WIDTH); 
    private static final Color COLOR = Color.RED; 
    private static final Color BG = Color.LIGHT_GRAY; 
    private AbstractButton button = new JToggleButton(); 
    private int index; 

    public DrawButton2(int index) { 
     this.index = index; 
     button.setBorderPainted(false); 
     button.setBorder(null); 
     button.setIcon(createPlainIcon()); 
     button.setSelectedIcon(createSelectedIcon()); 

     button.addItemListener(new ItemListener() { 

      @Override 
      public void itemStateChanged(ItemEvent e) { 
       if (e.getStateChange() == ItemEvent.SELECTED) { 
        System.out.println("Index: " + index); 
       } 
      } 
     }); 
    } 

    public int getIndex() { 
     return index; 
    } 

    private Icon createPlainIcon() { 
     BufferedImage img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB); 
     Graphics2D g2d = img.createGraphics(); 
     g2d.setColor(new Color(0, 0, 0, 0)); 
     g2d.fillRect(0, 0, PREF_W, PREF_H); 
     g2d.dispose(); 
     return new ImageIcon(img); 
    } 

    private Icon createSelectedIcon() { 
     BufferedImage img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB); 
     Graphics2D g2d = img.createGraphics(); 
     g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
     g2d.setStroke(BASIC_STROKE); 
     g2d.setColor(BG); 
     g2d.fillRect(0, 0, PREF_W, PREF_H); 
     g2d.setColor(COLOR); 
     g2d.drawOval(GAP, GAP, PREF_W - 2 * GAP, PREF_H - 2 * GAP); 
     g2d.dispose(); 
     return new ImageIcon(img); 
    } 

    public AbstractButton getButton() { 
     return button; 
    } 

} 

@SuppressWarnings("serial") 
class DrawButton1 extends JToggleButton { 
    private static final int PREF_W = 200; 
    private static final int PREF_H = PREF_W; 
    private static final int GAP = 20; 
    private static final float STROKE_WIDTH = 15f; 
    private static final Stroke BASIC_STROKE = new BasicStroke(STROKE_WIDTH); 
    private static final Color COLOR = Color.RED; 
    private static final Color BG = Color.LIGHT_GRAY; 

    @Override 
    public Dimension getPreferredSize() { 
     if (isPreferredSizeSet()) { 
      return super.getPreferredSize(); 
     } 
     return new Dimension(PREF_W, PREF_H); 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     Graphics2D g2d = (Graphics2D) g.create(); 
     g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
     g2d.setStroke(BASIC_STROKE); 
     if (!isSelected()) { 
      super.paintComponent(g); 
     } else { 
      g2d.setColor(BG); 
      g2d.fillRect(0, 0, getWidth(), getHeight()); 
      g2d.setColor(COLOR); 
      g2d.drawOval(GAP, GAP, getWidth() - 2 * GAP, getHeight() - 2 * GAP); 
     } 
     g2d.dispose(); // since we created a new one 
    } 
} 

Edité pour montrer comment faire avec JToggleButton et icônes.

+0

Donc, vous dites simplement que je devrais créer ImageIcon et le passer à un bouton? Cela semble trop simple ...: D – Mantas

+0

@Mantas: J'ai modifié le code pour vous montrer ce que je voulais dire. –