【问题标题】:Repaint is not functioning properly as required重绘未按要求正常运行
【发布时间】:2014-03-15 00:59:18
【问题描述】:

我已经制作了一个俄罗斯方块游戏。现在,我使用 JPanel 来显示内容和块(使用 paintComponents() 方法)。

问题是每当我尝试从另一个 JFrame 调用俄罗斯方块程序时,它根本不会绘制。

我的俄罗斯方块主菜单的代码是:

import javax.swing.*;
import sun.audio.AudioPlayer;
import sun.audio.AudioStream;
import java.awt.*;
import java.awt.event.*;
import java.io.FileInputStream;
import java.io.InputStream;

@SuppressWarnings("serial")
public class Tetris_MainMenu extends JFrame implements ActionListener {

    @SuppressWarnings("unused")
    public static void main(String[] args) {

        Tetris_MainMenu tmm = new Tetris_MainMenu();
        playAudio("doak.wav");

    }

    JPanel logo = new JPanel();
    JPanel buttonPanel = new JPanel(new GridLayout(4, 1));

    JButton start = new JButton("START NEW GAME");
    JButton help = new JButton("INSTRUCTIONS");
    JButton about = new JButton("ABOUT THIS GAME");
    JButton exit = new JButton("EXIT");

    Tetris_MainMenu(){

        setTitle("JAG's TETRIS");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocation(300, 100);
        setSize(200, 400);
        setEnabled(true);
        setFocusable(true);
        setVisible(true);

        //adding a logo to the logo panel

        //adding buttons to the buttonPanel
        buttonPanel.add(start);
        buttonPanel.add(help);
        buttonPanel.add(about);
        buttonPanel.add(exit);

        //add panels to window
        setLayout(new GridLayout(2, 1));
        add(logo);
        add(buttonPanel);

        //make buttons listen to actions
        start.addActionListener(this);
        help.addActionListener(this);
        about.addActionListener(this);
        exit.addActionListener(this);

    }

    @SuppressWarnings("restriction")
    public static void playAudio(String filename)
    {   
        InputStream in = null;
        AudioStream as = null;
        try{
            in = new FileInputStream(filename);
            as = new AudioStream(in);
        }
        catch(Exception e){
            System.out.println("Error!!!");
        }
        AudioPlayer.player.start(as);
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        if(e.getSource() == start){
            this.dispose();
            MatrixBoard b = new MatrixBoard();
            b.setRequestFocusEnabled(true);
        }
        else if(e.getSource() == help){
            JOptionPane.showMessageDialog(null, "Controls:\n"
                    + "LEFT and RIGHT ARROWS: For moving blocks left and right\n"
                    + "DOWN ARROW: For dropping block immediately\n"
                    + "SPACEBAR: For rotating block\n"
                    + "e: To exit to main menu");
        }
        else if(e.getSource() == about){
            JOptionPane.showMessageDialog(null, "Designed by: JAG."
                    + "\nIf you want you can use it for your own purposes."
                    + "\nBut give credit where it is due.");
        }
        else if(e.getSource() == exit){
            int opt = JOptionPane.showConfirmDialog(null, "Are you sure?", "Confirm Exit", JOptionPane.YES_NO_OPTION);
            if(opt == JOptionPane.YES_OPTION){
                System.exit(0);
            }
        }
    }


}

当 MatrixBoard 的构造函数被调用时,俄罗斯方块游戏在一个新窗口中开始。但是,这些块在屏幕上不可见。 MatrixBoard 的代码是:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import sun.audio.*;

@SuppressWarnings("serial")
public class MatrixBoard extends JPanel implements ActionListener{

    int boardHeight = 20;
    int boardWidth  = 10;
    int score = 0;
    int curX = 0, curY = 0;
    int squareWidth;
    int squareHeight;
    Timer timer;
    int sleepTime = 300;
    Shape curPiece;
    Shape.Tetromino[][] board;
    boolean isFallingFinished = false;
    boolean isStarted = false;
    boolean isPaused = false;
    JFrame f;

    @SuppressWarnings("unused")
    public static void main(String [] args){
        MatrixBoard b = new MatrixBoard();      
    }

    public void update(Graphics g) {

        Graphics offgc;
        Image offscreen = null;
        Dimension d = getSize();
        // create the offscreen buffer and associated Graphics
        offscreen = createImage(d.width, d.height);
        offgc = offscreen.getGraphics();
        // clear the exposed area
        offgc.setColor(getBackground());
        offgc.fillRect(0, 0, d.width, d.height);
        offgc.setColor(getForeground());
        // do normal redraw
        paint(offgc);
        // transfer offscreen to window
        g.drawImage(offscreen, 0, 0, this);
    }

    @SuppressWarnings("restriction")
    public static void playAudio(String filename)
    {   
        InputStream in = null;
        AudioStream as = null;
        try{
            in = new FileInputStream(filename);
            as = new AudioStream(in);
        }
        catch(Exception e){
            System.out.println("Error!!!");
        }
        AudioPlayer.player.start(as);
    }

    MatrixBoard(){
        f = new JFrame("JAG's TETRIS");
        f.setVisible(true);
        f.setSize(205, 400);
        f.setFocusable(true);
        f.setBackground(Color.GREEN);
        f.add(this);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(200, 400);
        setVisible(true);
        setFocusable(true);
        requestFocusInWindow();
        setBackground(Color.BLACK);
        timer = new Timer(400, this);
        timer.setInitialDelay(10);
        squareWidth = (getWidth())/ boardWidth;
        squareHeight = (getHeight()) / boardHeight;
        curPiece = new Shape();
        board = new Shape.Tetromino[boardWidth][boardHeight];
        addKeyListener(new KeyHandler());
        clearBoard();
        timer.start();
        JOptionPane.showMessageDialog(null, "Press Enter to Start!!!");
        f.setEnabled(true);
        f.setResizable(false);
        //setEnabled(true);
        start();
        System.out.println("MatrixBoard() Success!");
    }

    public void clearBoard(){
        for(int i = 0; i < boardWidth; ++i)
            for(int j = 0; j < boardHeight; ++j)
                board[i][j] = Shape.Tetromino.NoShape;
    }

    public void start()
    {
        if (isPaused)
            return;
        clearBoard();
        timer.start();
        timer = new Timer(400, this);
        timer.setInitialDelay(100);
        isStarted = true;
        isFallingFinished = false;
        score = 0;
        repaint();
        newPiece();
        System.out.println("START SUCCESS!");
    }

    private void newPiece(){
        if(!isStarted) return;
        curPiece.generateShape();
        curX = boardWidth / 2;
        curY = 1;

        if(!tryMove(curPiece, curX, curY)){
            curPiece.selectPiece(Shape.Tetromino.NoShape);
            isStarted = false;
            JOptionPane.showMessageDialog(null, "Game Over! Score : " + score);
            isStarted = false;
            int opt = JOptionPane.showConfirmDialog(null, "Try Again?", "Again?", JOptionPane.YES_NO_OPTION);
            if(opt == JOptionPane.YES_OPTION){
                start();
            }
            else if (opt == JOptionPane.NO_OPTION){
                System.exit(0);
            }
            //dispose();
            System.exit(0);
            //new Tetris();
            return;
        }
        dropDown();
        System.out.println("NEW PIECE SUCCESS!");
    }

    private boolean tryMove(Shape newPiece, int newX, int newY){
        for(int i = 0; i < 4; i++){
            int x = newX + newPiece.coords[i][0]; 
            int y = newY + newPiece.coords[i][1]; 
            if(x < 0 || x >= boardWidth || y < 0 || y >= boardHeight){
                System.out.println("FALSE1");
                return false;
            }
            if(board[x][y] != Shape.Tetromino.NoShape){
                System.out.println("FALSE2");
                return false;
            }
        }
        curPiece = newPiece;
        curX = newX;
        curY = newY;
        System.out.println("curX = " + curX + " curY = " + curY);
        System.out.println("TRY MOVE SUCCESS!");
        return true;
    }

    private void dropDown(){
        int newY = curY;
        sleepTime = 300;
        System.out.println("newY = " + newY);
        while(newY < boardHeight){
            if(!tryMove(curPiece, curX, newY+1)){ break;}
            ++newY;
            System.out.println("calling f.update()");
            repaint();
            System.out.println("called f.update()");
            try {
                Thread.sleep(sleepTime);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        pieceDropped();
        System.out.println("DROPDOWN SUCCESS!");
    }

    private void pieceDropped(){

        for(int i = 0; i < 4; i++){
            int x = curX + curPiece.coords[i][0];
            int y = curY + curPiece.coords[i][1];
            board[x][y] = curPiece.retShape();
            System.out.println("PIECE: at X = " + x + " Y = " + y + "is " + curPiece.retShape().ordinal());
        }
        removeFullLines();
        if(!isFallingFinished) newPiece();
        System.out.println("PIECE DROPPED SUCCESS!");

    }

    public void paintComponent(Graphics g){ 

        System.out.println("PAINTED\n\n\n\n\nPAINTED!!!!");

        super.paintComponent(g);

        Dimension size = getSize();
        int boardTop = (int) size.getHeight() - boardHeight * squareHeight;

        System.out.println("PAINTED\n\n\n\n\nPAINTED!!!!");

        for (int i = 0; i < boardWidth; ++i) {
            for (int j = 0; j < boardHeight; ++j) {
                Shape.Tetromino shape = board[i][j];
                if (shape != Shape.Tetromino.NoShape)
                    drawSquare(g, i * squareWidth,
                               boardTop + j * squareHeight, shape);
            }
        }

        if (curPiece.retShape() != Shape.Tetromino.NoShape) {
           for (int i = 0; i < 4; ++i) {
               int x = curX + curPiece.coords[i][0];
               int y = curY + curPiece.coords[i][1];
               drawSquare(g, x * squareWidth,
                          boardTop + (y - 1) * squareHeight,
                          curPiece.retShape());
           }
       }
   }

    private void drawSquare(Graphics g, int x, int y, Shape.Tetromino shape)
    {
        Color colors[] = { new Color(0, 0, 0), new Color(204, 102, 102), 
            new Color(102, 204, 102), new Color(102, 102, 204), 
            new Color(204, 204, 102), new Color(204, 102, 204), 
            new Color(102, 204, 204), new Color(218, 170, 0)
        };

        Color color = colors[shape.ordinal()];

        g.setColor(color);
        g.fillRect(x + 1, y + 1, squareWidth - 2, squareHeight - 2);

        g.setColor(color.brighter());
        g.drawLine(x, y + squareHeight - 1, x, y);
        g.drawLine(x, y, x + squareWidth - 1, y);

        g.setColor(color.darker());
        g.drawLine(x + 1, y + squareHeight - 1,
                         x + squareWidth - 1, y + squareHeight - 1);
        g.drawLine(x + squareWidth - 1, y + squareHeight - 1,
                         x + squareWidth - 1, y + 1);

    }

    private void removeFullLines(){

        int numLines = 0;

        for(int i = 0; i < boardHeight; i++){
            boolean isLineFull = true;
            for(int j = 0; j < boardWidth; j++){
                System.out.println("i = " + i + " j = " + j);
                if(board[j][i] == Shape.Tetromino.NoShape){
                    System.out.println("Found No Shape here!");
                    isLineFull = false;
                    break;
                }
            }

            System.out.println("IIIIIIIIS LINE : " + isLineFull);

            if(isLineFull){
                numLines++;
                for(int k = i; k > 0; k--){
                    for(int j = 0; j < boardWidth; ++j){
                        board[j][k] = board[j][k-1]; 
                    }
                }
            }
        }

        if(numLines > 0){
            score += numLines * numLines;
            repaint();
            newPiece();
        }

    }

    class KeyHandler extends KeyAdapter{
        public void keyPressed(KeyEvent e){
            if(!isStarted || curPiece.retShape() == Shape.Tetromino.NoShape){
                return;
            }
            int keyCode = e.getKeyCode();

            switch(keyCode){
            case KeyEvent.VK_LEFT:
                tryMove(curPiece, curX - 1, curY);
                break;
            case KeyEvent.VK_RIGHT:
                tryMove(curPiece, curX + 1, curY);
                break;
            case KeyEvent.VK_DOWN:
                sleepTime = 10;
                break;
            case KeyEvent.VK_E:
                int opt = JOptionPane.showConfirmDialog(null,"Are you sure?", "Exit", JOptionPane.YES_NO_OPTION);
                if(opt == JOptionPane.YES_OPTION){
                    System.exit(0);
                }
                break;
            case KeyEvent.VK_SPACE:
                tryMove(curPiece.rotateLeft(), curX, curY);
                break;
            default:
                break;
            }
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (isFallingFinished) {
            isFallingFinished = false;
            newPiece();
        } 
    }

}

请帮忙。我怀疑问题出在重绘机制上,但我不确定。

【问题讨论】:

    标签: java swing user-interface awt repaint


    【解决方案1】:

    您的问题似乎是 Swing 线程问题,因为您似乎在 Swing 事件线程上调用 Thread.sleep(...)。这样做会锁定整个线程,冻结你的应用程序。不要这样做,而是使用 Swing Timer。

    顺便说一句,您不希望覆盖 Swing GUI 的 update() 方法,因为它主要用于 AWT GUI,除非您在运行中更改应用程序的外观。


    编辑
    你问:

    我知道调用 Thread.sleep() 是不可取的,最好使用计时器。但问题是,当我单独运行 MatrixBoard 时,它运行得非常好。这里没有遇到线程问题。请尽可能解释。

    当您自己调用它时,主应用程序并没有在 Swing 线程上运行(这不是应该做的事情——所有 Swing 应用程序应该在 Swing 事件线程上运行),因此您的代码似乎可以工作。一旦你强制它在 Swing 事件线程上运行,它就会冻结。

    所有 Swing GUI 都应该使用 SwingUtilities.invokeLater(new Runnable() {...}); 排队到事件线程中


    编辑 2

    观察当你改变这个时会发生什么:

    public static void main(String [] args){
        MatrixBoard b = new MatrixBoard();      
    }
    

    到更合适的代码:

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

    我敢打赌你的 MatrixBoard 不会死机(虽然我还没有测试过)。

    【讨论】:

    • 我知道调用 Thread.sleep() 是不可取的,最好使用计时器。但问题是,当我单独运行 MatrixBoard 时,它运行得非常好。这里没有遇到线程问题。请尽可能解释。
    • 我现在明白了。如果您能指出一些参考或示例来帮助我解决问题,我将不胜感激,因为我对 Swing 还很陌生。另一个问题:如果我使用 AWT 会出现同样的问题吗?
    • 这不取决于你是使用 AWT 还是 Swing。如果您关心响应性,那么在主线程/UI 线程上休眠绝不是一个好主意,
    • @CrystalMeth:我在回答中给了你参考。请点击链接。是的,AWT 与我熟悉的大多数 GUI 库有类似的问题。线程总是一个问题。不要使用 AWT。
    • 最后一个问题:如何在我的程序的 dropDown() 方法中使用定时器而不是线程?我找到了有关 Timer 的文章和文档,但没有一篇能解决我的问题。
    猜你喜欢
    • 2020-03-17
    • 2019-08-14
    • 1970-01-01
    • 2012-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-12
    • 1970-01-01
    相关资源
    最近更新 更多