奇怪的 JFrame 行为

2022-01-24 00:00:00 java swing actionlistener jframe

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

I have the following program which has some very strange and unwanted behavior when it runs. Its supposed to have two buttons, "Start" and "Stop, but when I click "Start" another button shows up right below "Start". Here's a print screen of what I'm talking about:

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

What am I doing wrong and how do I fix this ugly problem?

代码如下:

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
}

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

Also I'm trying to get the Start button to do two things. One is to of course start the animation but when the Stop button is pressed and I press Start again, I want it to clean the screen so to speak and start the animation again a new. Any tips on that?

推荐答案

你没有在重写的 paintComponent(..) 方法中调用 super.paintComponent(Graphics g)为了尊重 paint chain 以及其他组件的绘制,您应该这样做.

You do not call super.paintComponent(Graphics g) in overriden paintComponent(..) method which you should in order to honor the paint chain and thus the painting of other components.

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

This call should also be the first call within the method:

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

    //do painting here
}  

可能会出现绘图不持久的问题.您必须有一种方法来存储图纸并每次重绘.最常见的是一个 ArrayList ,它将保存要绘制的对象(因此您可以添加到列表中删除等),您将遍历列表并重绘 paintComponent.查看我的回答这里例子.

A probem might arise that drawings are not persistent. You must than have a way to store drawings and redraw every time. The most common is an ArrayList which will hold objects to be drawn (thus you cann add to the list remove etc), you would than iterate over the list and redraw each object in paintComponent. See my answer here for an example.

  • 另外请记得在 上创建和操作 Swing 组件事件调度线程 :

SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {
         //create UI and components here
    }
});

  • 不要在 JFrame 上调用 setSize(..) 而是覆盖 JPanelgetPreferredSize()并返回适合所有组件的适当高度,而不是在设置 JFrame 可见(但在添加所有组件之后)之前调用 JFrame#pack().

  • Dont call setSize(..) on JFrame rather override getPreferredSize() of JPanel and return an appropriate height which fits all components, than call JFrame#pack() before setting JFrame visible (but after adding all components).

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

    No need for getContentPane().add(..) as of Java 6+ add(..) defaults to contentPane

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

    Do not re declare Random i.e Random r=new Random() each time paintComponent is called as this will make the distributions of the values less random rather initiate it once when class is created and call methods on the instance

    这是固定代码(已实现上述修复):

    Here is the fixed code (with above fixes implemented):

    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;
        }
    }
    
  • 相关文章