在多个JButton之上绘图

时间:2017-07-03 20:48:37

标签: java jbutton paintcomponent

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

问题是,当我有几个按钮时,按钮开始以奇怪的方式运行。令人惊讶的是,如果只有其中一个,则完全没有问题。起初,我原以为问题可能与线程有关,但是,我把主代码放到invokeLater方法中,这根本没用。

我看到了一个使用BufferedImage的解决方案,但我想看看是否有可能以我的方式解决问题。

抱歉可能不是完美的英语。

Square class:

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 个答案:

答案 0 :(得分:1)

您在按钮的绘图代码中使用getX()getY()是完全错误的,不属于。这些方法返回按钮相对于其容器的位置,因此虽然这可能适用于位于左上角的按钮,但它会因其他任何原因而失败,因为您最终会在远离按钮本身的位置绘制,并且你的很多图画都不会出现。

你最好不要扩展JButton,而是简单地交换显示你想要在JButton上绘制的内容的ImageIcons。这更加简单,更加白痴。您可以通过调用按钮的.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和图标执行此操作。