2

我有以下程序,它在运行时有一些非常奇怪和不需要的行为。它应该有两个按钮,“开始”和“停止”,但是当我单击“开始”时,另一个按钮会显示在“开始”下方。这是我正在谈论的打印屏幕:

在此处输入图像描述

我做错了什么,我该如何解决这个丑陋的问题?

这是代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;

public class TwoButtonsTest {

    JFrame frame;
    Timer timer;
    boolean isClicked;

    public static void main(String[] args) {
    TwoButtonsTest test = new TwoButtonsTest();
    test.go();
    }

    public void go() {
    frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(500, 500);

    JButton startButton = new JButton("Start");
    startButton.addActionListener(new StartListener());
    JButton stopButton = new JButton("Stop");
        stopButton.addActionListener(new StopListener());

    final DrawPanel myDraw = new DrawPanel();

    frame.getContentPane().add(BorderLayout.CENTER, myDraw);
    frame.getContentPane().add(BorderLayout.NORTH, startButton);
    frame.getContentPane().add(BorderLayout.SOUTH, stopButton);


    frame.setVisible(true);

    timer = new Timer(50, new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            myDraw.repaint();
        }
        });
    }

    class StartListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        //needs to be implemented
        if(!isClicked) {
        }
        isClicked = true;
        timer.start();
    }
    }

    class StopListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        //needs to be implemented
        timer.stop();
        isClicked = false;
    }
    }

    class DrawPanel extends JPanel {
    public void paintComponent(Graphics g) {

        int red = (int)(Math.random()*256);
        int blue = (int)(Math.random()*256);
        int green = (int)(Math.random()*256);

        g.setColor(new Color(red, blue, green));

        Random rand = new Random();
        // following 4 lines make sure the rect stays within the frame
        int ht = rand.nextInt(getHeight());
        int wd = rand.nextInt(getWidth());

        int x = rand.nextInt(getWidth()-wd);
        int y = rand.nextInt(getHeight()-ht);

        g.fillRect(x,y,wd,ht);
    }
    } // close inner class
}

此外,我试图让“开始”按钮做两件事。一个当然是开始动画,但是当按下停止按钮并再次按下开始时,我希望它可以清理屏幕并重新开始动画。有什么建议吗?

4

3 回答 3

4

您不会调用您应该调用super.paintComponent(Graphics g)的重写paintComponent(..)方法,以尊重油漆链,从而尊重其他组件的油漆。

此调用也应该是方法中的第一个调用:

@Override
public void paintComponent(Graphics g) {
     super.paintComponent(g);

    //do painting here
}  

可能会出现图纸不持久的问题。您必须有一种方法来存储图纸并每次重绘。最常见的是ArrayList保存要绘制的对象(因此您不能添加到列表中删除等),您将遍历列表并重绘paintComponent. 例如,请参阅我的答案here

  • 另外请记住在Event Dispatch Thread上创建和操作 Swing 组件:

    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
             //create UI and components here
        }
    });
    
  • 不要调用setSize(..)JFrame不是覆盖并返回适合所有组件getPreferredSize()JPanel适当高度,而不是JFrame#pack()在设置JFrame可见之前调用(但在添加所有组件之后)。

  • getContentPane().add(..)从 Java 6+开始不需要add(..)默认为contentPane

  • 不要重新声明,RandomRandom r=new Random()每次paintComponent被调用,因为这会使值的分布不那么随机,而是在创建类时启动一次并调用实例上的方法

这是固定代码(实施了上述修复):

在此处输入图像描述

import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;

public class TwoButtonsTest {

    JFrame frame;
    Timer timer;
    boolean isClicked;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                TwoButtonsTest test = new TwoButtonsTest();
                test.go();
            }
        });
    }
    final DrawPanel myDraw = new DrawPanel();

    public void go() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton startButton = new JButton("Start");
        startButton.addActionListener(new StartListener());
        JButton stopButton = new JButton("Stop");
        stopButton.addActionListener(new StopListener());


        frame.add(myDraw, BorderLayout.CENTER);
        frame.add(startButton, BorderLayout.NORTH);
        frame.add(stopButton, BorderLayout.SOUTH);


        frame.pack();
        frame.setVisible(true);

        timer = new Timer(50, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                myDraw.repaint();
            }
        });
    }

    class StartListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            //needs to be implemented
            if (!isClicked) {
            }

            myDraw.clearRects();

            isClicked = true;
            timer.start();
        }
    }

    class StopListener implements ActionListener {

        public void actionPerformed(ActionEvent e) {
            //needs to be implemented
            timer.stop();
            isClicked = false;
        }
    }

    class DrawPanel extends JPanel {

        private ArrayList<MyRectangle> rects = new ArrayList<>();
        private Random rand = new Random();

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);

            addRect();
            for (MyRectangle r : rects) {
                g.setColor(r.getColor());
                g.fillRect(r.x, r.y, r.width, r.height);
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(500, 500);
        }

        public void clearRects() {
            rects.clear();
        }

        public void addRect() {
            // following 4 lines make sure the rect stays within the frame
            int ht = rand.nextInt(getHeight());
            int wd = rand.nextInt(getWidth());

            int x = rand.nextInt(getWidth() - wd);
            int y = rand.nextInt(getHeight() - ht);

            int red = (int) (Math.random() * 256);
            int blue = (int) (Math.random() * 256);
            int green = (int) (Math.random() * 256);

            rects.add(new MyRectangle(x, y, wd, ht, new Color(red, blue, green)));
        }
    } // close inner class
}

class MyRectangle extends Rectangle {

    Color color;

    public MyRectangle(int x, int y, int w, int h, Color c) {
        super(x, y, w, h);
        this.color = c;
    }

    public Color getColor() {
        return color;
    }
}
于 2012-12-11T15:28:39.450 回答
3

我希望我能提供一个解决方案,但到目前为止我还没有找到一个。我可以告诉你这里“问题”的根源在于你绘制 BorderLayout 的中心部分的方式。您正在覆盖该程序的整个 paintComponent() 函数,并将它创建的任何内容放入 BoarderLayout 的中心。在这种情况下,每次单击按钮时,程序都会调用重绘来绘制单击按钮的图像,但由于您还向中心面板添加了任何绘制的对象,因此它也被绘制在那里。由于此特定重绘未指定位置,因此它位于左上角。

于 2012-12-11T16:14:06.647 回答
2

我通过调用 SwingUtilities 修复了我的 Windows XP 计算机上的按钮问题。

我格式化了你的 Java 代码。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class TwoButtonsTest implements Runnable {

    JFrame frame;
    Timer timer;
    boolean isClicked;

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

    @Override
    public void run() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500, 500);

        JButton startButton = new JButton("Start");
        startButton.addActionListener(new StartListener());
        JButton stopButton = new JButton("Stop");
        stopButton.addActionListener(new StopListener());

        final DrawPanel myDraw = new DrawPanel();

        frame.getContentPane().add(BorderLayout.CENTER, myDraw);
        frame.getContentPane().add(BorderLayout.NORTH, startButton);
        frame.getContentPane().add(BorderLayout.SOUTH, stopButton);

        frame.setVisible(true);

        timer = new Timer(50, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                myDraw.repaint();
            }
        });
    }

    class StartListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            // needs to be implemented
            if (!isClicked) {
            }
            isClicked = true;
            timer.start();
        }
    }

    class StopListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            // needs to be implemented
            timer.stop();
            isClicked = false;
        }
    }

    class DrawPanel extends JPanel {
        @Override
        public void paintComponent(Graphics g) {
            int red = (int) (Math.random() * 256);
            int blue = (int) (Math.random() * 256);
            int green = (int) (Math.random() * 256);

            g.setColor(new Color(red, blue, green));

            Random rand = new Random();
            // following 4 lines make sure the rect stays within the frame
            int ht = rand.nextInt(getHeight());
            int wd = rand.nextInt(getWidth());

            int x = rand.nextInt(getWidth() - wd);
            int y = rand.nextInt(getHeight() - ht);

            g.fillRect(x, y, wd, ht);
        }
    } // close inner class
}

要在按下 Start 按钮时清理屏幕,您必须向 DrawPanel 类添加一些方法。

这是一种方法。

class DrawPanel extends JPanel {
        protected boolean eraseCanvas;

        public void setEraseCanvas(boolean eraseCanvas) {
            this.eraseCanvas = eraseCanvas;
        }

        @Override
        public void paintComponent(Graphics g) {
            if (eraseCanvas) {
                g.setColor(Color.WHITE);
                g.fillRect(0,  0, getWidth(), getHeight());
            } else {
                int red = (int) (Math.random() * 256);
                int blue = (int) (Math.random() * 256);
                int green = (int) (Math.random() * 256);

                g.setColor(new Color(red, blue, green));

                Random rand = new Random();
                // following 4 lines make sure the rect stays within the frame
                int ht = rand.nextInt(getHeight());
                int wd = rand.nextInt(getWidth());

                int x = rand.nextInt(getWidth() - wd);
                int y = rand.nextInt(getHeight() - ht);

                g.fillRect(x, y, wd, ht);
            }
        }
    } // close inner class
于 2012-12-11T14:58:39.507 回答