Android has become the cornerstone of mobile technology, powering hundreds of millions of devices across over 190 countries, as suggested by Geeks for Geeks. Creating a background image app for Android devices is a great way to experiment with how Android works with images and how to manipulate images to fit various screen sizes.
In this guide, we’ll explore the myriad ways you can personalize your Android device, starting with the simplest change, the background. We’ll learn how to change the background of an image by removing the background and then applying an underlay with Cloudinary’s toolkit.
In this article:
Step-By-Step Guide to Changing Android Backgrounds
Now that we know why you may want to change your image background let’s dive into a practical example. We will build an Android Java app that uses Cloudinary to add text overlays to your images.
Prerequisites
Before we begin, you’ll need a Cloudinary account. If you don’t already have one, you can sign up for a free account.
Now, log in to your Cloudinary account. Once you’ve signed in, navigate to Cloudinary’s Programmable Media section by clicking on the button in the top left corner of your page. From there, head over to the Dashboard tab. This is where you’ll find your Cloudinary API credentials. Copy these, as we will need them later.
To build this project, you’ll need the latest Android Studio on your system. If you don’t have it already, you can download the latest version from the Android Studio website.
With the installation now completed, let’s make our app!
Creating a User Interface
Start by opening Android Studio and selecting an Empty Views Activity project. Make sure that the template you use supports Java. Click on Next to navigate to the next window.
Now, we will define the details of our project. We will start by naming our project Cloudinary and selecting its directory. Next, we will choose the programming language Java and select the Minimum SDK requirement for our project as Android 11. Finally, we will click on Finish to finish setting up our project.
Gradle will take some time to download and set up all the project requirements. Once it finishes, your project should look something like this:
Finally, we can begin creating the UI for our app. To do this, head to activity_main.xml
in the res/layout
folder and open it in Android Studio. This will open up your main app screen.
Here, we will add some ImageView
objects to display the images the user wants to select. Next, we will add a few TextView
objects to help the user understand the purpose of each ImageView
object. We will also add a Button
object to help us transform our image.
Additionally, we will rename our objects by selecting each one and clicking on the Code button in the screen’s top-right corner. This will open up the object’s source code in the activity_main.xml
file. Here, we can change our objects’ text and variable names to our liking.
Here is what our app looks like:
Programmatically Creating Our Android App
Now that our UI is ready, we must set up and add the necessary modules for our project. To do this, head over to your build.gradle
file and add in your imports. We will add Picasso, a powerful image downloading and caching library for Android, as well as the Cloudinary Android SDK as our dependencies:
implementation("com.squareup.picasso:picasso:2.8") implementation("com.cloudinary:cloudinary-android:2.5.0")
Here is what our Gradle file looks like:
Next, we must allow our app to open images and call in the Cloudinary API. To do this, open up your AndroidManifest.xml
file and add permissions for reading image and video files along with accessing the internet:
<!-- Read Storage --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- Access Internet --> <uses-permission android:name="android.permission.INTERNET" />
Here is what our AndroidManifest.xml
file looks like:
With the setup complete, we can begin coding our app. We’ll start by opening MainActivity.java
, located in the com.example.cloudinary
folder of our project. Next, we will head to the MainActivity
class and start defining placeholder variables for our objects:
public class MainActivity extends AppCompatActivity { private static final String TAG = "Upload ###"; private static int IMAGE_REQ=1; private Uri imagePath; private Uri imagePath2; Map config = new HashMap(); private ImageView BackgroundImage; private ImageView Result; private ImageView Image; private Button Button;
Here, we’ve created three image variables: BackgroundImage
to hold our background picture, Image
for our top image, and Result
for the final image. We also have two variables named imagePath
and imagePath2
that help us store the location of our background and top image. We’ve also defined a config
variable to set up our Cloudinary API and a button
variable to call our transformation. Lastly, the TAG
variable will help us find those debugging messages we sprinkle throughout the code.
Next, we will define our onCreate()
function. We’ll start by populating our placeholder variables by retrieving our objects from our UI using the findViewById()
function:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BackgroundImage=findViewById(R.id.BackgroundImage); Result=findViewById(R.id.Result); Image=findViewById(R.id.Image); Button=findViewById(R.id.Button); ...
Next, we will define our Cloudinary API using the config
variable. We will also add on-click functions to both of our ImageView
’s, which will call in our requestPermission()
function (which we will define later):
... config.put("cloud_name", "your_cloud_name"); config.put("api_key","your_api_key"); config.put("api_secret","your_api_secret"); MediaManager.init(this, config); BackgroundImage.setOnClickListener(v -> { requestPermission(BackgroundImage); Log.d(TAG, ": "+"request permission"); }); Image.setOnClickListener(v -> { requestPermission(Image); Log.d(TAG, ": "+"request permission"); }); ...
Lastly, to complete our onCreate()
function, we will add an on-click listener function to our button:
... Button.setOnClickListener(v -> { MediaManager.get().upload(imagePath).option("public_id", "background").dispatch(); MediaManager.get().upload(imagePath2).option("public_id", "image").dispatch(); Transformation transformation = new Transformation().effect("background_removal").chain(). underlay(new Layer().publicId("background")).chain().flags("layer_apply"); String url = MediaManager.get().url().transformation(transformation).generate("image"); Log.d("URL", url); Picasso.get() .load(url) .into(Result); }); ...
Here, we use Cloudinary’s MediaManager
class to upload our images to our Cloudinary cloud. We then create a new Transformation
using Cloudianry’s Transformation class and remove the background of our top image.
Then, we use the .chain()
function to add an underlay transformation, adding the background image to our top image. Finally, we used MediaMagager
to generate and retrieve the URL of our resultant image, and then we used Picasso to display this image in the Result
ImageView
.
This is what our complete onCreate()
function looks like:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BackgroundImage=findViewById(R.id.BackgroundImage); Result=findViewById(R.id.Result); Image=findViewById(R.id.Image); Button=findViewById(R.id.Button); config.put("cloud_name", "your_cloud_name"); config.put("api_key","your_api_key"); config.put("api_secret","your_api_secret"); MediaManager.init(this, config); BackgroundImage.setOnClickListener(v -> { requestPermission(BackgroundImage); Log.d(TAG, ": "+"request permission"); }); Image.setOnClickListener(v -> { requestPermission(Image); Log.d(TAG, ": "+"request permission"); }); Button.setOnClickListener(v -> { MediaManager.get().upload(imagePath).option("public_id", "background").dispatch(); MediaManager.get().upload(imagePath2).option("public_id", "image").dispatch(); Transformation transformation = new Transformation().effect("background_removal").chain(). underlay(new Layer().publicId("background")).chain().flags("layer_apply"); String url = MediaManager.get().url().transformation(transformation).generate("image"); Log.d("URL", url); Picasso.get() .load(url) .into(Result); }); }
Now that we’ve finished the onCreate()
function, let’s examine the requestPermission()
function.
As the name suggests, the requestPermission()
function is responsible for requesting permissions from the user. It calls the Android system to request access to storage files and the internet. If the user grants permission, the function calls the selectImage()
function (which we will define next). Here is what our function looks like:
private void requestPermission(ImageView img) { if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED) { selectImage(img); }else { ActivityCompat.requestPermissions(MainActivity.this,new String[]{ Manifest.permission.READ_MEDIA_IMAGES },IMAGE_REQ); } }
Now that our requestPermission()
function is complete, we can define our selectImage()
function.
The selectImage()
function is a messenger that opens another app component. It does this by creating an Intent
object. You can think of Intent’s as a request slip. It tells the Android system what action you want to perform (in this case, selecting an image) and what kind of data you’re interested in (images).
We’ll then use this Intent
to launch an activity (which we will define later) to help us choose images. Here is what our function looks like:
private ImageView selectedImage; private void selectImage(ImageView img) { Intent intent=new Intent(); intent.setType("image/*"); // if you want to you can use pdf/gif/video intent.setAction(Intent.ACTION_GET_CONTENT); selectedImage = img; someActivityResultLauncher.launch(intent); }
Now that we’ve defined the permission handling and image selection functions, let’s revisit the activity we defined earlier. This activity is responsible for two main tasks: retrieving the path of our images and displaying the image in its designated ImageView
.
To create this, we’ll leverage an activity launcher. This launcher will fetch and compare the image data with the selected image to populate a Uri
variable. It then calls in Picasso to render the image onto the appropriate ImageView
. Here is what our activity looks like:
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { if (result.getResultCode() == Activity.RESULT_OK) { // There are no request codes Intent data = result.getData(); if (selectedImage == Image) { imagePath2 = data.getData(); Picasso.get().load(imagePath2).into(selectedImage); } else { imagePath=data.getData(); Picasso.get().load(imagePath).into(selectedImage); } } } });
With this, our code is now complete. Here is what our final MainActivity.java
looks like:
package com.example.cloudinary; import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import android.Manifest; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.widget.Button; import android.widget.ImageView; import com.cloudinary.Transformation; import com.cloudinary.android.MediaManager; import com.cloudinary.transformation.Layer; import com.squareup.picasso.Picasso; import java.util.HashMap; import java.util.Map; public class MainActivity extends AppCompatActivity { private static final String TAG = "Upload ###"; private static int IMAGE_REQ=1; private Uri imagePath; private Uri imagePath2; Map config = new HashMap(); private ImageView BackgroundImage; private ImageView Result; private ImageView Image; private Button Button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BackgroundImage=findViewById(R.id.BackgroundImage); Result=findViewById(R.id.Result); Image=findViewById(R.id.Image); Button=findViewById(R.id.Button); config.put("cloud_name", "your_cloud_name"); config.put("api_key","your_api_key"); config.put("api_secret","your_api_secret"); MediaManager.init(this, config); BackgroundImage.setOnClickListener(v -> { requestPermission(BackgroundImage); Log.d(TAG, ": "+"request permission"); }); Image.setOnClickListener(v -> { requestPermission(Image); Log.d(TAG, ": "+"request permission"); }); Button.setOnClickListener(v -> { MediaManager.get().upload(imagePath).option("public_id", "background").dispatch(); MediaManager.get().upload(imagePath2).option("public_id", "image").dispatch(); Transformation transformation = new Transformation().effect("background_removal").chain(). underlay(new Layer().publicId("background")).chain().flags("layer_apply"); String url = MediaManager.get().url().transformation(transformation).generate("image"); Log.d("URL", url); Picasso.get() .load(url) .into(Result); }); } private void requestPermission(ImageView img) { if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED) { selectImage(img); }else { ActivityCompat.requestPermissions(MainActivity.this,new String[]{ Manifest.permission.READ_MEDIA_IMAGES },IMAGE_REQ); } } private ImageView selectedImage; private void selectImage(ImageView img) { Intent intent=new Intent(); intent.setType("image/*");// if you want to you can use pdf/gif/video intent.setAction(Intent.ACTION_GET_CONTENT); selectedImage = img; someActivityResultLauncher.launch(intent); } // You can do the assignment inside onAttach or onCreate, i.e, before the activity is displayed ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { if (result.getResultCode() == Activity.RESULT_OK) { // There are no request codes Intent data = result.getData(); if (selectedImage == Image) { imagePath2 = data.getData(); Picasso.get().load(imagePath2).into(selectedImage); } else { imagePath=data.getData(); Picasso.get().load(imagePath).into(selectedImage); } } } }); }
Testing our Android App
All that is left is to test our app. We can do this by pressing Shift+F10 on our keyboard or clicking the Play button at the top-right corner of your screen.
Here is what our app looks like:
Now, select the images you want to upload. Here, we will select nature-mountains
as the background image and three-dogs
as the top image, which we downloaded from the Cloudinary demo cloud.
Finally, click on the transform button to see your final image:
Final Thoughts
This guide explored techniques for changing backgrounds on Android devices. Traditional approaches often involve complex image processing on the device, leading to potential performance bottlenecks and compatibility issues. Cloudinary simplifies this process by providing a robust Android SDK for image uploads and transformations.
Through Cloudinary’s API, you can effortlessly upload user-selected images and leverage various background removal and replacement options. Cloudinary’s built-in AI tools can handle automatic background removal, or you can upload pre-designed backgrounds for seamless integration. This cloud-based approach saves development time and resources and ensures consistent and high-quality background manipulation across different Android devices.
Ready to take your Android app’s background manipulation to the next level? Sign up for a free Cloudinary account and explore its powerful features today!
More from Cloudinary:
Change a Background With Cloudinary and Next.js
Creating a Transparent Effect on Background Images With CSS Opacity and Cloudinary