2017-07-03 59 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:我編輯了代碼來向你展示我的意思。 –