2017-07-03 91 views
0

所以,我有类Square的多个对象,它是JButton的子类。我有一个班级委员会的实例,其中包含Square的几个实例。我想要做的是当我按下其中一个按钮(正方形)时,在其上绘制一个形状(一个圆形)。为此,我在Square类中有一个布尔变量,即isClicked,它基本上决定了paintComponent方法中必须绘制的内容。在多个JButton上绘图

问题是,当我有几个按钮时,按钮开始表现得很怪异。令人惊讶的是,如果只有其中一个,那完全没有问题。起初,我认为这个问题可能与线程有关,但是,我把主代码放到了invokeLater方法中,并且根本没有任何帮助。

我看到了一个使用BufferedImage的解决方案,但我想看看是否有任何可能性来解决这个问题。

对不起,可能不完美的英语。

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(); 
} 

} 

板类:

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); 

} 
} 

主要方法:

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); 
     } 
    }); 

回答

1

您对按钮的绘制代码使用getX()getY()是完全错误的,不不属于。这些方法返回按钮相对于其容器的位置,所以虽然这可能适用于位于左上方的按钮,但它会失败,因为您最终会在远离按钮本身的地方绘制,并且很多你的图纸永远不会显示。

你最好不要扩展JButton,而只需交换ImageIcons来显示你想要在JButton上绘制的内容。这是更简单,更笨的证明。您可以通过调用.setIcon(myImageIcon)来设置按钮的图标,并传入所选的图标。

但是,如果你绝对想要在按钮上画画,你会这样做,而不使用getX()getY()。你也可能想使用JToggleButton作为父类,因为你正在切换状态。例如,我的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 
    } 
} 

编辑以显示如何使用JToggleButton和图标执行此操作。

+0

所以你基本上只是说我应该创建ImageIcon并将它传递给一个按钮?这听起来太简单了......:D – Mantas

+0

@Mantas:我编辑了代码来向你展示我的意思。 –