java - JPanel 创建两次

标签 java swing applet

我正在尝试为战舰游戏制作 GUI。一个类用于创建 GUI 本身,第二个类用于管理游戏中的棋盘。我的问题是,一旦发生鼠标单击,JPanel 就会创建两次(鼠标单击应该是在游戏中开火的地方,然后将其标记为命中/未命中)。我不确定为什么它会创建两次。是因为一个面板过去了吗?下面的代码和代码生成的照片。

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;


public class BattleshipApplet extends JApplet implements MouseListener {
    private final JButton playButton = new JButton("Play");
    private final JLabel msgBar = new JLabel("Click Play to start game");
    private BoardPanel panel;



    public BattleshipApplet(){
        playButton.addActionListener(this::playButtonClicked);  
        addMouseListener(this);
    }

    public void init(){
        configureGui();
    }

    private void configureGui(){
        setLayout(new BorderLayout());
        JPanel buttons = new JPanel(new FlowLayout(FlowLayout.LEFT));
        buttons.setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
        buttons.add(playButton);
        add(buttons, BorderLayout.NORTH);
        msgBar.setBorder(BorderFactory.createEmptyBorder(10,10,5,5));
        add(createBoardPanel(), BorderLayout.CENTER);
        add(msgBar, BorderLayout.SOUTH);
    }

    private BoardPanel createBoardPanel(){
        panel = new BoardPanel();
        return panel;
    }

    private void displayMessage(String msg){
        msgBar.setText(msg);
    }

    private void playButtonClicked(ActionEvent event){
        displayMessage("Play button clicked!");
    }

    public void mouseClicked(MouseEvent e) {
        panel.mouseClickedAt(e.getX(), e.getY());
        e.consume();
    }


    public void mouseEntered(MouseEvent e) {
    }


    public void mouseExited(MouseEvent e) {
    }  
    public void mousePressed(MouseEvent e) {
    }
    public void mouseReleased(MouseEvent e) {
    }   
}

使用JPanel的板类

[![import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;


public class BoardPanel extends JPanel  {
    int mx, my;
    boolean rect1Clicked;
    //gamePlay a;
    public void init(){

        rect1Clicked = false;

    }

    /***Your applet shall show the status of the board before and after
     each shot, including the number of shots made and the status of
     each place (no shot or hit/miss shot). ***/
    public void paint(Graphics g){
        boolean miss = false;


        for (int i=0; i<11; i++){ 
            g.setColor(Color.blue);
            g.drawLine(20,20+i*28, 300, 20+i*28);   
        }
        for (int i=0; i<11; i++)   
            g.drawLine(20+i*28,20,20+i*28,300);

        //if inside board 
        if(rect1Clicked == true){
            g.setColor(Color.green);
            //aligns to square to check in computer board for hit/miss
            int bx =(my-20)/28;
            int by =(mx-20)/28;



            //check hit on board
            //if shot was a miss
            if(miss == true ){
                //update to white
                g.setColor(Color.white);
            }
            //if shot was a hit
            if(miss == false){
                //update to red
                g.setColor(Color.red);
            }
            //compare to line for fill
            int fillx = mx/2;
            int filly = my/2 ;
            if(mx<=47){
                fillx = 20;
            }
            if(mx>47 && mx<=75){
                fillx = 48;
            }
            if(mx>75 && mx<=103){
                fillx = 76;
            }
            if(mx>103 && mx <=131){
                fillx = 104;
            }
            if(mx>131 && mx<=159){
                fillx = 132;
            }
            if(mx>159 && mx<=187){
                fillx = 160;
            }
            if(mx>187 && mx <=215){
                fillx = 188;
            }
            if(mx>215 && mx <=243){
                fillx = 216;
            }
            if(mx>243 && mx <=271){
                fillx = 244;
            }
            if(mx>271 && mx<=299){
                fillx = 272;
            }
            if(mx>299){
                fillx = 300;
            }
            //y comparisons
            if(my<=47){
                filly = 20;
            }
            if(my>47 && my<=75){
                filly = 48;
            }
            if(my>75 && my<=103){
                filly = 76;
            }
            if(my>103 && my <=131){
                filly = 104;
            }
            if(my>131 && my<=159){
                filly = 132;
            }
            if(my>159 && my<=187){
                filly = 160;
            }
            if(my>187 && my <=215){
                filly = 188;
            }
            if(my>215 && my <=243){
                filly = 216;
            }
            if(my>243 && my <=271){
                filly = 244;
            }
            if(my>271 && my<=299){
                filly = 272;
            }
            if(my>299){
                filly = 300;
            }


            g.drawString("("+mx+","+my+")",mx,my);
            //25 describes size of square 
            g.fillOval(fillx, filly, 25, 25);

        }


    }

    public void game(BoardPanel p){
        //while game plays
    }


    public void mouseClickedAt(int x, int y){

        mx = x;
        my = y;

        //user clicked inside of board space
        if(mx>20 && mx<300 && my>20 && my<300){       

            //send to board in MainBattleship
            rect1Clicked = true;

        }
        //updates board
        repaint();
    }


}][1]][1]

我很迷茫,谢谢你的帮助!

最佳答案

建议:

  1. 不要重写 JPanel 的 paint 方法,而是重写它的 paintComponent 方法,因为这样更安全,稍后当您想要制作动画时,动画效果会更流畅。
  2. 最重要的是,您几乎总是需要在自己的绘画方法中调用 super 的绘画方法,否则 JPanel 不会删除需要清理的先前图像工件。因此,如果您继续覆盖 paint(尽管我不建议这样做),覆盖的第一行应该是 super.paint(g);,或者如果您覆盖 paintComponent那么第一行应该是 super.paintComponent(g);,当然假设您的方法使用名为 g 的 Graphics 参数。
  3. 此外,将 MouseListener 添加到 JPanel,而不是 到小程序,因为鼠标在面板上的点击位置对您很重要。
  4. 此外,使用组件网格或一些数学来极大地简化您的代码——丑陋的 if block 列表应该被更简单的 for 循环所取代,一个使用基本数学的循环。
  5. 考虑将上述逻辑从您的绘画方法中提取出来,并提取到某种模型中,也许是二维 boolean 数组。
  6. 您在代码中使用了很多“神奇”数字,这些数字应该更改为常量和数学派生数字的组合。
  7. 请注意如果您单击您的 GUI,然后调整它的大小,或者如果您将它最小化然后恢复它会发生什么——除了最后一个按下的红色圆圈之外,您会丢失所有红色圆圈。这是使用 boolean 网格或其他模型来保存游戏状态,然后在绘制 GUI 时使用此模型的另一个原因。
  8. 进一步考虑,您可能需要枚举的二维数组或整数数组,因为网格单元格状态可能会超过 2 个值(真或假),而是三个 值(value)观 -- 未经测试、命中和未命中,如果命中,您可能希望用红色填充椭圆形,如果未命中,则可能需要用白色填充椭圆形。

例如:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.*;
import javax.swing.*;

public class GridExample {
    private static void createAndShowGui() {
        final GridPanel gridPanel = new GridPanel();

        JButton resetBtn = new JButton(new AbstractAction("Reset") {

            @Override
            public void actionPerformed(ActionEvent e) {
                gridPanel.reset();
            }
        });
        JPanel btnPanel = new JPanel();
        btnPanel.add(resetBtn);

        JFrame frame = new JFrame("GridExample");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(gridPanel);
        frame.getContentPane().add(btnPanel, BorderLayout.PAGE_END);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGui();
            }
        });
    }
}

@SuppressWarnings("serial")
class GridPanel extends JPanel {
    private static final int ROWS = 10;
    private static final int CELL_WIDTH = 28;
    private static final int PAD = 20;
    private static final int PREF_W = ROWS * CELL_WIDTH + 2 * PAD;
    private static final int PREF_H = PREF_W;
    private static final Color GRID_COLOR = Color.blue;
    private static final Color CIRCLE_COLOR = Color.red;
    private static final int SML_GAP = 2;
    private boolean[][] grid = new boolean[ROWS][ROWS];

    public GridPanel() {
        addMouseListener(new MyMouse());
    }

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

    public void reset() {
        grid = new boolean[ROWS][ROWS]; // fills grid with false
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // draw grid:
        g2.setColor(GRID_COLOR);
        for (int i = 0; i <= ROWS; i++) {
            int x1 = PAD + i * CELL_WIDTH;
            int y1 = PAD;
            int x2 = x1;
            int y2 = PAD + CELL_WIDTH * ROWS;
            g2.drawLine(x1, y1, x2, y2);
            g2.drawLine(y1, x1, y2, x2);
        }

        // iterate through the grid boolean array
        // draw red circles if the grid value is true.
        g2.setColor(CIRCLE_COLOR);
        int w = CELL_WIDTH - 2 * SML_GAP; // width of the circle to draw
        int h = w;
        // nested for loop to go through the grid array
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[r].length; c++) {
                if (grid[r][c]) {
                    int x = PAD + c * CELL_WIDTH + SML_GAP;
                    int y = PAD + r * CELL_WIDTH + SML_GAP;
                    g2.fillOval(x, y, w, h);
                }
            }
        }        
    }

    private class MyMouse extends MouseAdapter {
        public void mousePressed(MouseEvent e) {
            int x = e.getPoint().x;
            int y = e.getPoint().y;

            if (x < PAD || y < PAD) {
                // clicked above or to right of grid
                return;
            }

            int r = (y - PAD) / CELL_WIDTH;
            int c = (x - PAD) / CELL_WIDTH;

            // if clicked to right or below grid.
            // the < 0 part is likely unnecessary, but why not be extra safe?
            if (r >= ROWS || c >= ROWS || r < 0 || c < 0) {
                return;
            }
            grid[r][c] = true;
            repaint();
        }
    }
}

关于java - JPanel 创建两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32748767/

相关文章:

java - 如何编写java程序来生成 "cloud"的随机点?

java - 如何计算两个纬度和经度之间的距离

java - 如何销毁Java Process的子进程

java - 基于预定义代码构建的 Netbeans GUI

Java 小程序使用额外的随机字母呈现 JLabel(和其他组件)

java - 为什么我们使用 init() 而不是构造函数

java - 带条件的任务设计模式

Java VM 堆可以变小吗?

java - 使用字符串设置 JLabel 的颜色

java - 如何删除之前创建的 JLabel?