Responsive “Rain” Animation in Java: Tutorial
16 September 2017
In this tutorial, (and my first technical post) we’ll take a detailed look at what might be your first experience with graphics in Java. We’ll use the Swing and AWT libraries to make an animation of falling rain, with a little splash at the bottom. The cool part is that it will adjust the density of the rain so that it remains constantly proportional to the size of the window!
Making the step from complete beginner to intermediate Java developer can be tricky. Maybe you just finished reading a book on the basics or took a crash course, but you haven’t really built anything and are completely overwhelmed by the sudden learning curve of actually building something. I’ve found that a great way to get started is to follow along in someone else’s code, then make something similar after! After a few code-along projects, you’ll have learned enough to make something original!
Before we jump in, I want to preface by saying: this tutorial assumes basic knowledge of programming in Java and OOP design. That being said, I will explain as best I can as we go along.
First things first! I’ll be creating this project in Eclipse. If you don’t have it already, I’d recommend you download it here, since I’ll be showing to export your runnable JAR file (using Eclipse) at the end. It also has a built-in documentation feature that lets you scroll through the methods of a class when you start typing it, and hover your mouse over to read their documentation.
So, let’s dive in! Fire up Eclipse and create a new Java Project. Use whichever name you’d like, and whichever package name you’d like.
Now we’ll make 4 classes. Frame, Drop, Panel, and Splash.
The Frame class will contain all the methods and variables having to do with our JFrame object, which is basically the “window” of the app (the border, exit/minimize/maximize buttons, title etc.).
The Panel class will contain everything to do with our JPanel object, this is a component that will fit inside the JFrame, and will be where all of the graphics are displayed. It will also include all of the logic for the Drop and Splash animation on the screen.
The Drop class will define our Drop object. The Splash class, similarly, defines the Splash object.
Creating the Frame:
This class (Frame) extends JFrame. This means, we’re adding extra features to the existing class: JFrame (it comes from the swing library).
Later, we’ll be implementing the ActionListener interface. If you’re unsure about the meaning of interface, I’d recommend you read this thread on StackOverflow about it, but it’s not necessary to fully understand everything about interfaces for this project.
First, like any class, we need to declare our class variables.
We’ll set the initial dimensions of our JFrame. 750x750 is just what I chose, it can be whatever you want (within typical screen resolutions).
Then, we’ll add a Timer. A timer takes two inputs, a delay time in milliseconds, and an ActionListener, which we’ll leave as null for now. The timer will later be used to trigger the “refresh” of the animation. You can think of the delay time parameter as a refresh rate, so the lower the milliseconds, the faster the refresh rate.
At this point you’ll be getting a bunch of compiler warnings yelling at you that JFrame and Timer could not be found. To fix this, just import the required libraries by hitting CTRL+SHIFT+O. Or, if you’re not using Eclipse, you can manually import java.swing.*; and java.awt.*; the asterix means “all”. So you’re importing the entire swing and awt libraries.
Now we’ll make a simple constructor for the frame. We’ll leave it blank for now and come back to this later.
Next step is to write a method to initialize our frame. In this method, we’ll just call several setter methods from the JPanel class.
First, we’ll set the initial size to the defaults we declared earlier. We’ll set the close operation to EXIT_ON_CLOSE. This will ensure that the program stops running when we click the X button at the top right of the Frame.
You can set a title if you want, and set the background to any color you wish.
You’ll want to set resizable to true (this is the essence of the program!).
Lastly, to be able to see anything, you’ll need to set visible to true.
Now we’ll head back to our constructor and call the method we just wrote so that it looks like this:
We’ve wrote a fair amount of code, and it’s about time we test it. To do this, just write a main method that does nothing but create a new Frame object.
You should see a blank window when you run your code.
The Drop Class:
Now that we’ve got the JFrame up and running, let’s start making our Drop object. It will have 4 integer variables: velocity, x, y, and length. These variables are protected, meaning we cannot change them from outside the class, but we can access them.
We’ll make a simple constructor for the drops, that takes 3 integers as parameters. X and Y coordinates, and a velocity. I’m keeping the length constant, however, you could include it in the constructor to have it be variable if you want. (If you want to have random lengths.)
The only method we’ll put in this class is one called paint. This is a method from the Graphics class of the AWT library, and we’ll be overriding it several times. It takes a Graphics object as a parameter. What this method will do is call the drawLine() method from the Graphics class, which takes two points as parameters, and draws a line from the first to the second.
Next, we’ll update our value for y to be y+length+velocity. This will make the drop’s new location be the old y-coordinate, plus the length and velocity. This will make the drop move vertically down the screen at a speed depending on the length and the velocity (and the refresh rate of course).
Note about coordinates in JFrame:
The origin (0,0) is always at the top left of the window, and counts by pixels. So if you want your drop to move down, you have to increase your y-coordinate. And if you want to move right, increase your x-coordinate. There’s no such thing as negative coordinates.
The Splash Class:
This class will contain everything about the splash.
The splash will just be a little animation that appears when a drop reaches the bottom of the window.
It will have 2 integer variables: x, y.
It will also have a Boolean variable called isRight. This variable will help us determine which direction we want the splash drop to go. This is not entirely necessary, but I included it just in case later we want to have different things happen on the left than on the right.
Again, we’ll just have a simple constructor
And our only method will be a paint() override as before. This time, you can add different things to happen when the slash goes left than if the splash goes right, or, you could include it all in the same block (so they will always be symmetrical).
To get a line drawn diagonally upwards to the right, we use the drawLine() method, with points (x,y) and (x+length, y-length). This is because we want the y value to decrease(upwards) and x to increase (to the right). You can choose your length now. I chose 5.
Similarly, we’ll use drawLine() with points (x,y) and (x-length, y-length) to draw a line upwards to the left.
The Panel Class:
This class is the heart of your program. Here we’ll set up the panel where everything will be displayed, hold all the drops, and decide what happens to them. This class extends JPanel. Make sure you include this in the class declaration.
First, we need to initialize variables for the default width and height of our Panel, and declare an ArrayList of type Drop. If you’ve never used an ArrayList before, it’s essentially an Array, however someone created methods so that the Array can change its size conveniently. We’ll use this to store our Drops.
We’ll create a constructor, but leave it blank for now.
The first method we’ll write is one to initialize the panel. All we need to set here is the default size and set the background color.
We’ve written a lot of code, time to test it! Go back to your Frame class, and create a new Panel object in the class variables.
Then, inside the initializeFrame() method, set the content pane to be the panel you just made. This will link the JPanel to the JFrame.
Run your program, and you should see a black screen like this (assuming your background color is black):
Now, inside your Panel class we’ll write a method that creates the Drop arrayList. We’ll use a for loop who’s maximum index is the average of the height and width of the panel. This is very important because we’ll be calling this createDrops() method whenever the window size is changed. This will insure that the number of Drops on the screen at any given window size is proportional to the size of the window.
On each iteration, we’ll create a random x, y and velocity for our Drop. We’ll use the Math.random() method for this. To get a random X coordinate, we’ll multiply (Math.random()) (which gives a random double between 0 and 1, not including 1) by the width of the window. Since Math.random() is a double, and we need an integer for our random X coordinate, we need to parse from double to integer.
We’ll do something similar for the y-coordinate, except that we’ll limit the range. This is because, if we allow Drops to randomly appear for any y, we’ll get a disproportionate number of Drops near the bottom, since we have them all falling. To “even it out” we only allow Drops to appear near the top, and let them fall naturally.
We’ll limit the velocity to 10, simply because we don’t want them too fast. You can make the velocity vary as little or as much as you’d like.
Finally, we’ll create a new Drop, using these random values as parameters.
You can also add this method to your Panel constructor now.
Now we’ll write a method called changeDrop() that takes as parameter an integer. It generates a new Drop with new random values, and replaces the Drop at the given index. We’ll use this method later whenever a Drop reaches the bottom of the window.
Next step is to write yet another paint() method. This method is a little messy, but bear with me.
First, we’ll write an if-else-if block. If the number of Drops in our arrayList is less than the average of the height and width of the window, we’ll clear the arrayList, and create a new one with the updated window size. Similarly, else-if there are more drops than the average, we’ll clear and re-create the arrayList.
Next, we’ll write a for loop that paints the Drops at every index. Within this loop, we’ll add a conditional. If the y-coordinate of the Drops reach within 15 pixels from the bottom of the window, we’ll create and paint a Splash at the location of that Drop. If you had two separate paint methods for left and right splash, you’ll need to make two Splash objects, with different isRight values.
Lastly, if the y-coordinate of the drops reach the height of the window, we’ll call changeDrop() to change its location.
Run your code to make sure all this works. You should see a black screen with stationary gray Drops near the top. Like this:
Time to Get Moving:
Its finally time to make things move! To do this, head back to your Frame class.
First, we’ll modify the class declaration to implement ActionListener.
Inside the constructor for the Frame, we need to add a Frame ActionListener to the timer. This will make it so that every time the Timer reaches its delay value in milliseconds, an event is triggered.
Now we’ll need to add a method called start(), in which we’ll invoke the start() method on the timer object.
Next, we’ll make yet another paint override method. In this one, we’ll invoke clearRect(), which takes two points as parameters. We’ll make it clear the entire window. Then we’ll set the color for our Graphics object (in this case our Drops and Splashes) to white. Lastly, we’ll paint the Panel object. This will clear and paint the panel. Soon we’ll have our program do this repeatedly.
To have our Timer actually work, we need to write a method called actionPerformed() which takes an ActionEvent as an input. Inside this method, we’ll call the repaint() method, which invokes all our paint overrides.
The very last step in this project is to start the frame timer in the main method. This will cause the timer to start, which will trigger actionPerformed which will repaint; every few milliseconds.
Compile and run, and you should have an animation of rain falling! It should respond to changes in window size by changing the number of drops on screen, and should splash at the bottom of the window.
Optional: Exporting your Project as a Runnable JAR File
To be able to have your friends download and run your new program, the easiest way is to export as a JAR file. To do this, right click your project and select Export…
Next, choose Runnable JAR File. Make sure your build path and destination folder are correct, and click finish.
Congrats! You’re done! You can now run you program with a simple double click of the JAR file!
Now that you’ve made this, make sure you go through and understand why everything is working.
Play around with it! Add or modify features, make it your own! You can now apply these same concepts to a huge variety of projects.
I hope you enjoyed this tutorial as much as I enjoyed making it!
If you have any questions, don’t hesitate to ask in the comments or send me an email!
Hungry for more awesome tips, guides and articles?
Subscribe to the mailing list for free!