Strange JFrame Behavior

蹲街弑〆低调 提交于 2019-11-27 08:21:21

问题


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?

Here's the code:

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?


回答1:


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
}  

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.

  • Also please remember to create and manipulate Swing components on Event Dispatch Thread :

    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
             //create UI and components here
        }
    });
    
  • 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).

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

  • 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;
    }
}



回答2:


I wish I could offer a solution, but as of yet I haven't found one. I can tell you the root of the "problem" here lies in the way you are drawing the Center section of your BorderLayout. You are overriding the whole paintComponent() function for this program and having whatever it creates put into the Center of your BoarderLayout. In this case, each time you click a button, the program calls the repaint to draw the image of a clicked button, but since you have also added ANY of the drawn objects to the Center panel, it also is drawn there. Since this specific repaint doesn't specify a location, it goes in the upper left corner.




回答3:


I fixed your button problem on my Windows XP computer by invoking SwingUtilities.

I formatted your Java code.

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
}

To clean the screen when you press the Start button, you're going to have to add some methods to your DrawPanel class.

Here's one way to do it.

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


来源:https://stackoverflow.com/questions/13822019/strange-jframe-behavior

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!