JPanel创建了两次

时间:2015-09-23 20:21:16

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

答案 0 :(得分:5)

建议:

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

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