Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
177 views
in Technique[技术] by (71.8m points)

windows - Programming Rain in Java

I am a beginner programmer taking java in high school. I'm not very good but I love coding and I want to improve. In this code I'm simply trying to get rain particles to fall but they won't do so. Also I didn't add my imports

public class RainDrop
{

    private int x, y;
    private int width, height;
    private int vx;

    public RainDrop()
    {
        //x = (int) (Math.random()*640); 
        //y = (int) (Math.random() *500) - 500;
        x= (int) (Math.random()*640);
        y = 50;
        width = 3;
        height = 25;
        vx = 1;
    }
    public void draw(Graphics g)
    {
        g.setColor(Color.blue);
        g.fillRect(x, y, width, height);
    }
    public void fall()
    {
        y += vx;
        if(y >= 480)
        {
            y = (int) (Math.random() *500) - 500;
            vx = 1;

        }
    }

    public int getY()
    {
        return y;
    }
}



public class Panel extends JPanel implements Runnable

{

    public static final int drops = 1;
    public RainDrop[] d = new RainDrop[drops];
    public Panel()
    {
        for(int i = 0; i < drops; i++)
        {
            d[i] = new RainDrop();
        }
    }

    @Override
    public void run() 
    {
        update();
        repaint();
    }

    public void update()
    {
        for(int i = 0; i < drops; i++)
        {
            d[i].fall();
            repaint();
        }
    }

    public void paint(Graphics g)
    {
        for(int i = 0; i < drops; i++)
        {
            d[i].draw(g);
            repaint();
        }
    }
}



public class Runner extends JFrame
{

    public static void main(String[] args) 
    {

        JFrame obj = new JFrame();
        Panel j = new Panel();
        obj.setSize(640, 480);
        obj.setVisible(true);
        obj.setResizable(false);
        obj.setDefaultCloseOperation(EXIT_ON_CLOSE);
        obj.setTitle("Rain");
        obj.add(j);
        obj.setLocationRelativeTo(null);
    }
}

Can anybody help me

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You have a number of very important topics you need to understand

The first would be gaining a better understanding of how painting works in Swing:

Two main concerns crop up in your code:

  1. Overriding paint (and not calling super.paint). Generally, you are discouraged from overriding paint directly, instead, preference is given to overriding paintComponent (and making sure you call super.paintComponent in order to preserve established paint chain)
  2. Calling repaint from within paint. This is not a good idea, as it can cause the UI to become saturated with paint requests, increasing the pressure on the CPU and degrading the perform of the program and system.

This leads to some slight modifications that might look something like...

public class Panel extends JPanel {

    public static final int drops = 1;
    public RainDrop[] d = new RainDrop[drops];

    public Panel() {
        for (int i = 0; i < drops; i++) {
            d[i] = new RainDrop();
        }
    }

    public void update() {
        for (int i = 0; i < drops; i++) {
            d[i].fall();
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); 
        for (int i = 0; i < drops; i++) {
            d[i].draw(g);
            repaint();
        }
    }
}

Next, you need to gain some understanding into how the event dispatching process works and how concurrency should be used within the API.

Start with Concurrency in Swing.

The short version is:

  • Swing is single threaded
  • Swing is NOT thread safe

This means that, you should never perform any long running or blocking operations from within the context of the Event Dispatching Thread, but also, you should never update the UI or something the UI relies on from outside of the context of the EDT.

Remember, from the painting documentation, Swing uses a passive rendering approach. This means that the Swing API makes decisions about when and what should be painted, you can only make suggestions, and that painting may occur at any time, without your knowledge or input. This makes the API susceptible to thread race conditions and generate weird and hard to replicate paint issues.

This leads to the next step. You need some way to update the state of the rain drop(s) and schedule new paint cycles, all of which must be done in a none blocking manner but which can safely update the state of the UI.

The simplest solution to this is using a Swing Timer

This leads to following, slight, modification to the Panel class...

public class Panel extends JPanel {
    //...        
    public Panel() {
        //...            
        Timer timer = new Timer(10, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                update();
                repaint();
            }
        });
        timer.start();
    }
}

Every 10th of a second, the rain drop is updated and new paint cycle is scheduled, thus providing the "core" animation engine.

I'd also recommend some slight modifications to the Runner class...

public class Runner {

    public static void main(String[] args) {
        new Runner();
    }

    public Runner() throws HeadlessException {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                JFrame obj = new JFrame();
                Panel j = new Panel();
                obj.setSize(640, 480);
                obj.setDefaultCloseOperation(EXIT_ON_CLOSE);
                obj.setTitle("Rain");
                obj.add(j);
                obj.setLocationRelativeTo(null);
                obj.setResizable(false);
                obj.setVisible(true);
            }
        });
    }
}

This does a number of things...

  1. Removes the extends JFrame, as you're not actually using it and it just confuses the issue. Also, as a general recommendation, you should avoid extending directly from top level containers, lots of reasons, but basically, it couples your code and makes it inflexible
  2. Moves the creation of the UI into the context of the Event Dispatching Thread
  3. Calls setResiazable and setVisible last, as this can have an undesirable affect on the UI when it's displayed (displaying a blank screen) - Swing's layout API is lazy, so unless you tell it, it won't generally update itself.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...