Android has dominated the mobile market, with around 71% of the global market share, according to Statcounter. Developers are drawn to Android for its rich ecosystem and potential to reach millions of users. Developers can use the Android SDK to build apps for phones, tablets, wearables, and TVs. They can also use the Android NDK to build native code that runs on Android devices.
Adding text to images on Android may not be an essential feature, but it’s nice. It allows you to add captions or other text to photos, which is useful for sharing on social media.
In this article, we’ll learn about adding text to images on Android. We’ll demonstrate how to leverage the Cloudinary API to streamline this process, making it more efficient and user-friendly. So, let’s dive in and start adding life to our images with text!
In this article:
Understanding the Importance of Adding Text to Images
Adding text to images is a powerful technique that serves multiple purposes. It enhances communication by providing context and emphasizing key messages, making the content more engaging and accessible.
Text overlays can also direct viewers’ attention to specific details, helping to convey a message more effectively. In marketing, text on images can highlight product features or calls to action, significantly impacting viewer retention and brand recognition. Plus, in SEO, image alt text can improve website ranking and drive traffic, as search engines use this text to index and understand images.
How to Add Text to Image on Android
Let’s go over an example from start to finish on how you can create an app in Android Java that uses Cloudinary, a media asset management platform, to add text overlays to your images.
For this tutorial, we will use Cloudinary to add text overlays to our images. So head over to Cloudinary and sign up for a free account.
Once you’ve signed up, go to Cloudinary and log in to your account. Next, click the Programmable Media button at the top left corner of your screen and go to the Dashboard tab. Here, you will see your Cloudinary API credentials. Copy these, as we will be using them later in the tutorial.
Since we will be creating an Android app, you will need to have the latest version of Android Studio installed on your system. You can download the latest version from the Android Studio website if you don’t have it installed.
Initializing Our Android Studio Project
Open up Android Studio and select an Empty Views Activity project. Make sure you use the project highlighted in the image below, as other templates may not support Java. Click on Next to navigate to the next window.
Next, we will rename our project. Here, we have named our project Text Overlay. Select the directory to save your project and select Java as the project language. Optionally, you can select the Minimum SDK requirement for your project (for now, we have left it as Android 11). Finally, click on Finish to finish setting up your project.
Creating a new project will take some time to build as Gradle will download the requirements for your project, but once completed, your project should look something like this:
Now, we can begin adding the necessary modules for the project.
To do this, open up your build.gradle
file and add in your imports. Here, we will be using Picasso, a powerful image downloading and caching library for Android, as well as the Cloudinary Android SDK:
implementation("com.squareup.picasso:picasso:2.8") implementation("com.cloudinary:cloudinary-android:2.5.0")
Next, we will open up our AndroidManifest.xml
file and add some 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" />
Creating a User Interface For Our App
To create our UI, we will start by opening activity_main.xml
, located in the res/layout
folder. This will open your main app screen. We’ll add some ImageView
objects to display the images the user wants to select. Then, we add a few TextView
objects to help the user understand the purpose of the ImageView
objects.
To rename our objects, we need to select each object and click on the Code button in the screen’s top-right corner to open up its source XML file. Here, we can change the text and variable names of our objects to our liking:
For now, let’s revert to our original screen by clicking on the Design button and adding a few more objects. Here, we will add a button that calls an Android overlay transformation of the images and shows the result in the bottom ImageView
. Finally, we will add an EditText
object to help the user type the text they want overlay. Here is what our final UI looks like:
Coding Our Android App
With the setup out of the way, we can begin coding our application. Start by opening up MainActivity.java
, located in the com.example.cloudinary
folder of our project. Next, we will head to the MainActivity
class, where we will begin by defining new variables that will serve as placeholders:
private static final String TAG = "Upload ###"; private static int IMAGE_REQ=1; private Uri imagePath; Map config = new HashMap(); private ImageView Image; private ImageView Result; private Button button; private EditText Edit;
Here, we have defined two ImageView
variables, Image
and Result
. The Image
variable will store our base image, while the Result
variable will store the resultant image. The imagePath
variable will store the location of our base image, while the Edit
variable will help us call our EditText
object. We’ve also defined a Map
variable called config
that will help us initialize our Cloudinary API. Finally, the TAG
variable will find our debug statements in our code.
Now that our variables are set up, we can start working on our onCreate()
function. We will begin by retrieving our objects from our XML file using the findViewById()
function:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Initializing views Image=findViewById(R.id.Image); Result=findViewById(R.id.Result); button=findViewById(R.id.button); Edit = findViewById(R.id.editText);
Next, we will use the config variable to define our Cloudinary API. We will also add an on-click function to our image using the setOnClickListener()
. These functions will call a requestPermission()
function, which we will also define later:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Initializing views Image=findViewById(R.id.Image); Result=findViewById(R.id.Result); button=findViewById(R.id.button); Edit = findViewById(R.id.editText); // Initializing Cloudinary configuration 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); // Setting click listeners Image.setOnClickListener(v -> { requestPermission(); Log.d(TAG, ": "+"request permission"); }); ...
To complete our onCreate()
function, we will define an on-click function for our button:
... button.setOnClickListener(v -> { // Button click action Log.d(TAG, ": "+" button clicked"); // Upload images to Cloudinary MediaManager.get().upload(imagePath).option("public_id", "image").dispatch(); String text = Edit.getText().toString(); // Generate URL for the result image String url = MediaManager.get().url().transformation(new Transformation() .width(500).crop("scale").chain() .overlay(new TextLayer().fontFamily("Verdana").fontSize(75).fontWeight("bold").text(text)).chain() .flags("layer_apply")).generate("image"); Log.d("URL", url); // Load and display the result image using Picasso Picasso.get() .load(url) .into(Result); }); ...
In this function, we first use Cloudinary’s MediaManager
object to upload our base image to the Cloudinary cloud with its public ID defined as Image
. Next, we use the Edit
object to retrieve the text the user inputs in the App.
We then use MediaManager’s .transform()
method and generate a new overlay transformation to overlay the text onto the image. Finally, we log this URL to the console before using Picasso to display the image on the Result
ImageView
. Here is what our complete onCreate()
function looks like:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Initializing views Image=findViewById(R.id.Image); Result=findViewById(R.id.Result); button=findViewById(R.id.button); Edit = findViewById(R.id.editText); // Initializing Cloudinary configuration 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); // Setting click listeners Image.setOnClickListener(v -> { requestPermission(); Log.d(TAG, ": "+"request permission"); }); button.setOnClickListener(v -> { // Button click action Log.d(TAG, ": "+" button clicked"); // Upload images to Cloudinary MediaManager.get().upload(imagePath).option("public_id", "image").dispatch(); String text = Edit.getText().toString(); // Generate URL for the result image String url = MediaManager.get().url().transformation(new Transformation() .width(500).crop("scale").chain() .overlay(new TextLayer().fontFamily("Verdana").fontSize(75).fontWeight("bold").text(text)).chain() .flags("layer_apply")).generate("image"); Log.d("URL", url); // Load and display the result image using Picasso Picasso.get() .load(url) .into(Result); }); }
Now that our onCreate()
function is complete let’s define our requestPermission()
function.
The requestPermission()
function simply works as you’d think. It triggers an Android System request, requesting permission from the user to access storage files as well as internet usage. If the permission is granted, the function calls in a selectImage()
function. Here is what our function looks like:
private void requestPermission() { if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED) { selectImage(); }else { ActivityCompat.requestPermissions(MainActivity.this,new String[]{ Manifest.permission.READ_MEDIA_IMAGES },IMAGE_REQ); } }
Next, let’s code our selectImage()
function.
The selectImage()
function creates a new Intent
object, which is essentially a messaging object that is used to request any action from another app component. Next, we will filter files based on the image and, finally, use the intent to launch an activity. Here is what our function looks like:
private void selectImage() { Intent intent=new Intent(); intent.setType("image/*");// if you want to you can use pdf/gif/video intent.setAction(Intent.ACTION_GET_CONTENT); someActivityResultLauncher.launch(intent); }
Finally, we will define our activity.
Our activity needs to perform two functions: retrieve the location of our base image and display our base image in the relevant ImageView
. To do this, we will create an activity launcher to retrieve our image’s data. It will then use this data to populate the imagePath
variable and then use Picasso to load the image onto our 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(); // Handle result for Base image imagePath=data.getData(); Picasso.get().load(imagePath).into(Image); } } });
With this, our code is now complete. Here is what our MainActivity.java
looks like:
package com.example.textoverlay; 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.EditText; import android.widget.ImageView; import com.cloudinary.Transformation; import com.cloudinary.android.MediaManager; import com.cloudinary.transformation.TextLayer; 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; Map config = new HashMap(); private ImageView Image; private ImageView Result; private Button button; private EditText Edit; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Initializing views Image=findViewById(R.id.Image); Result=findViewById(R.id.Result); button=findViewById(R.id.button); Edit = findViewById(R.id.editText); // Initializing Cloudinary configuration 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); // Setting click listeners Image.setOnClickListener(v -> { requestPermission(); Log.d(TAG, ": "+"request permission"); }); button.setOnClickListener(v -> { // Button click action Log.d(TAG, ": "+" button clicked"); // Upload images to Cloudinary MediaManager.get().upload(imagePath).option("public_id", "image").dispatch(); String text = Edit.getText().toString(); // Generate URL for the result image String url = MediaManager.get().url().transformation(new Transformation() .width(500).crop("scale").chain() .overlay(new TextLayer().fontFamily("Verdana").fontSize(75).fontWeight("bold").text(text)).chain() .flags("layer_apply")).generate("image"); Log.d("URL", url); // Load and display the result image using Picasso Picasso.get() .load(url) .into(Result); }); } // Request permission to access gallery private void requestPermission() { if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED) { selectImage(); }else { ActivityCompat.requestPermissions(MainActivity.this,new String[]{ Manifest.permission.READ_MEDIA_IMAGES },IMAGE_REQ); } } // Select the image from the gallery private void selectImage() { Intent intent=new Intent(); intent.setType("image/*");// if you want to you can use pdf/gif/video intent.setAction(Intent.ACTION_GET_CONTENT); someActivityResultLauncher.launch(intent); } // Activity Result Launcher for selecting image 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(); // Handle result for Base image imagePath=data.getData(); Picasso.get().load(imagePath).into(Image); } } }); }
Now, all we need to do is run our app. To do this, simply click the Play button at the top-right corner of your screen or press Shift+F10 on your keyboard. This will launch your emulator and run your app.
Here is what our app looks like:
Now, let’s select our images. Click on the top image icon to prompt you with a permission request. After accepting, you can choose the photos in your phone gallery or storage. Here, we select three-dogs
, which we downloaded from the Cloudinary cloud.
Next, add in the text you want to overlay and click on the Transform button to see your resultant image:
Here is what our image looks like:
Choosing the Right Parameters
Cloudinary offers a variety of parameters to customize your text overlays and make them visually appealing. For instance, if you want to change your font, you can use the .fontFamily()
method to define your font. You can pair this up with the .fontSize()
method and add the .fontWeight()
method to customize the style of your text.
MediaManager.get().url().transformation(new Transformation() .width(500).crop("scale").chain() .overlay(new TextLayer().fontFamily("Verdana").fontSize(75).fontWeight("bold").textDecoration("underline").letterSpacing(14).text("Dogs")).chain() .flags("layer_apply")).generate("samples/animals/three-dogs.jpg");
You can even change the color of your text by adding the .color()
method to your transformation:
MediaManager.get().url().transformation(new Transformation() .width(500).crop("scale").chain() .color("#FFFF00").overlay(new TextLayer().fontFamily("Times").fontSize(90).fontWeight("bold").text("Nature")).chain().flags("layer_apply").gravity("south").y(20)).generate("samples/landscapes/nature-mountains.jpg");
You can take a look at the Cloudinary documentation to learn more.
Wrapping Up
This guide focused on adding text overlays to images within Android applications. Traditionally, this required custom development for text rendering and image manipulation. Cloudinary offers a user-friendly solution with an intuitive API for dynamically generating text overlays. You can specify various fonts, styles, text colors, and positioning options within your code. Cloudinary handles the image manipulation on its servers, eliminating the need for complex on-device processing and ensuring consistent rendering and performance across different Android devices.
Managing text overlays efficiently through Cloudinary’s API allows for updates to text content, positioning adjustments, or even font swaps on the fly. This approach simplifies development for Android apps and offers several benefits, including reduced development time, consistent text rendering across devices, and dynamic control over text overlays within your app.
Enhance your Android app’s visual communication with Cloudinary’s text overlay features! Sign up for a free account and explore its intuitive text manipulation tools.
More from Cloudinary:
8 Image Transformations Developers Can Make On The Fly – Learning By Example