Processing is one of the most powerful libraries available today for creating visual algorithmic artworks, both 2D and 3D. It is open source, based on Java, and comes with a large variety of functions geared to making drawing and painting with code both fun and easy.
By using Processing's core library in your Android apps, you can create high-performance graphics and animations without having to deal with Android's OpenGL or Canvas APIs. Usually, you won't even have to bother with low-level tasks such as managing threads, creating render loops, or maintaining frame rates.
In this tutorial, I'll show you how to add Processing to an Android app and introduce you to some of its most useful features.
1. Project Setup
Processing comes with its own integrated development environment, which can be used to create Android apps. However, if you are an Android app developer already, I'm sure you'd prefer to use Android Studio instead. So go ahead and download the latest version of Processing's Android Mode.
Inside the ZIP file you downloaded, you'll find a file named processing-core.zip. Extract it and rename it to processing-core.jar using the command line or your operating system's file explorer.
Lastly, add the JAR file as one of the dependencies of your Android Studio project by placing it inside the app
module's libs folder.
You now have everything you need to start using Processing.
2. Creating a Canvas
Almost all of Processing's core functionality is available through the PApplet
class, which essentially serves as a canvas you can draw on. By extending it, you get easy access to all the methods it has to offer.
val myCanvas = object: PApplet() { // More code here }
To configure the canvas, you must override its settings()
method. Inside the method, you can specify two important configuration details: the desired dimensions of the canvas and whether it should use the 2D or the 3D rendering engine. For now, let's make the canvas as large as the device's screen and use the default 2D rendering engine. To do so, you can call the fullScreen()
shortcut method.
override fun settings() { fullScreen() }
The settings()
method is a special method that's needed only when you are not using Processing's own IDE. I suggest you don't add any more code to it.
If you to want to initialize any variables or change any drawing-related parameters—such as the background color of the canvas or the number of frames it should display per second—you should use the setup()
method instead. For example, the following code shows you how to use the background()
method to change the background color of the canvas to red:
override fun setup() { background(Color.parseColor("#FF8A80")) // Material Red A100 }
3. Displaying the Canvas
Because the canvas is still not a part of any activity, you won't be able to see it when you run your app. To display the canvas, you must first create a container for it inside your activity's layout XML file. A LinearLayout
widget or a FrameLayout
widget can be the container.
<FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/canvas_container"></FrameLayout>
A PApplet
instance cannot be directly added to the container you created. You must place it inside a PFragment
instance first and then call the setView()
method of the PFragment
instance to associate it with the container. The following code shows you how to do so:
// Place canvas inside fragment val myFragment = PFragment(myCanvas) // Display fragment myFragment.setView(canvas_container, this)
At this point, if you run the app, you should be able to see a blank canvas covering the entire screen of your device.
4. Drawing Simple Shapes
Now that you are able to see the canvas, let's start drawing. To draw inside the canvas, you must override the draw()
method of the PApplet
subclass you created earlier.
override fun draw() { // More code here }
It might not seem obvious immediately, but Processing, by default, tries to call the draw()
method as often as 60 times every second, as long as the canvas is being displayed. That means that you can easily create both still graphics and animations with it.
Processing has a variety of intuitively named methods that allow you to draw geometric primitives such as points, lines, ellipses, and rectangles. For instance, the rect()
method draws a rectangle, and the ellipse()
method draws an ellipse. Both the rect()
and ellipse()
methods expect similar arguments: the X and Y coordinates of the shape, its width, and its height.
The following code shows you how to draw a rectangle and an ellipse:
rect(100f, 100f, 500f, 300f) // Top-left corner is at (100,100) ellipse(350f, 650f, 500f, 400f) // Center is at (350,650)
Many of the methods are overloaded too, allowing you to slightly modify the basic shapes. For example, by passing a fifth parameter to the rect()
method, a corner radius, you can draw a rounded rectangle.
rect(100f, 900f, 500f, 300f, 100f) // Corner radius of 100 pixels
If you run your app now, you should see something like this:
If you want to change the border color of the shapes, you can call the stroke()
method and pass the desired color as an argument to it. Similarly, if you want to fill the shapes with a specific color, you can call the fill()
method. Both the methods should be called before you actually draw the shape.
The following code draws a blue triangle with a green outline:
stroke(Color.GREEN) fill(Color.BLUE) triangle(100f, 1600f, 300f, 1300f, 500f, 1600f)
If you run your app now, you'll be able to see the blue triangle, but you'll also notice that every other shape has also turned blue.
If the reason isn't obvious to you already, remember that the draw()
method is called repeatedly. That means that any configuration parameter you change during a draw cycle will have an effect on subsequent draw cycles. So in order to make sure that all your shapes are drawn with the right colors, it is a good idea to always explicitly specify the color of every shape you draw, right before you draw it.
For instance, by adding the following code at the beginning of the draw()
method, you can make the other shapes white again.
// Set the fill and stroke to white and black // before drawing the rectangles and ellipses stroke(Color.BLACK) fill(Color.WHITE)
At this point, the canvas will look like this:
5. Handling Touch Events
With Processing, handling touch events is extremely easy. You don't need any event handlers whatsoever. All you need to do is check if a boolean variable named mousePressed
is true to know when the user is touching the screen. Once you've confirmed that the user is touching the screen, you can use the mouseX
and mouseY
variables to determine the X and Y coordinates of the touch.
For example, the following code draws a new rectangle wherever the user touches the canvas.
// Check if user is touching the canvas if(mousePressed) { // Specify fill and stroke colors stroke(Color.RED) fill(Color.YELLOW) // Draw rectangle rect(mouseX.toFloat(), mouseY.toFloat(), 100f, 100f) }
If you run your app now and drag your finger across the screen, you should see a lot of yellow rectangles being drawn.
Before we move on, here's a quick tip: if at any point you wish to clear the canvas, you can simply call the background()
method again.
background(Color.parseColor("#FF8A80")) // Material Red A100
6. Working With Pixels
There's only so far you can get with simple primitives. If you are interested in creating intricate and complex artwork, you'll probably need access to the individual pixels of the canvas.
By calling the loadPixels()
method, you can load the colors of all the pixels of the canvas into an array named pixels
. By modifying the contents of the array, you can very efficiently modify the contents of the canvas. Lastly, once you've finished modifying the array, you should remember to call the updatePixels()
method to render the new set of pixels.
Note that the pixels
array is a one-dimensional, integer array whose size is equal to the product of the width and height of the canvas. Because the canvas is two-dimensional, converting the X and Y coordinates of a pixel into a valid index of the array involves use of the following formula:
// index = xCoordinate + yCoordinate * widthOfCanvas
The following example code, which sets the color of each pixel of the canvas based on its X and Y coordinates, should help you better understand how to use the pixels
array:
override fun draw() { loadPixels() // Load array // loop through all valid coordinates for(y in 0..height - 1) { for(x in 0..width - 1) { // Calculate index val index = x + y * width // Update pixel at index with a new color pixels[index] = Color.rgb(x % 255, y % 255, (x*y) % 255) } } // Render pixels with new colors updatePixels() }
The Color.rgb()
method you see above converts individual red, green, and blue values to an integer that represents a single color value that the Processing framework understands. Feel free to modify the arguments you pass to it, but do make sure that they are always within the range 0 to 255.
If you choose to run the code without any modifications, you should see a pattern that looks like this:
Conclusion
You now know how to create 2D graphics using the Processing language. With the skills you learned today, you can not only make your Android apps more appealing, but also create full-fledged games from scratch. You are limited only by your creativity!
To learn more about Processing, I suggest you spend some time browsing through the official reference pages.
And while you're here, check out some of our other posts on Android app development!
- Android SDKWhat Are Android Instant Apps?
- Android SDKHow to Upload Images to Firebase from an Android App
- Android SDKCreate a Material Design Tabbed Interface in an Android App
- Android SDKBuild a Music App With an Android App Template