Image Effects Creating Custom Image Cropping Interfaces in Android How to Create Simple Yet Effective PHP Overlay Understanding Real-Time Image Recognition How to add a shadow effect to an image with CSS How to crop an image in Flutter with Cloudinary How To Rotate an Image with Java Image Processing with Python Rotating an image with CSS Enhancing User Experience with a Responsive Image Slider Building a Python Image Recognition System Building an Interactive JavaScript Image Manipulation Tool Image Align Centering with HTML and CSS Efficient Image Cropping Techniques with Angular and Cloudinary Ultimate Guide to Photo Gallery on Android A Comprehensive Guide to Adding Text to Images on Android Mastering Background Changes in React Applications Comprehensive Guide on Changing Background on Android Devices Mastering Image Rotation in Java A Guide to Adding Text to Images with Python A Guide to Converting Images to Grayscale with Python Introduction Creating an Image Overlay with JavaScript Rotating an Image in Python Creating a Dynamic Photo Gallery with jQuery Creating An Interactive Photo Gallery Using JavaScript Mastering Overlay in Android Mastering Angular Overlay: A Comprehensive Guide Comprehensive Guide to Overlay in Flutter Mastering Overlay React for Responsive Design Solutions Create a Blurred Image with PHP: A Comprehensive Guide Guide to Using Blur Image in Flutter Mastering Blur Image in React Native Mastering Image Blurring in Python Mastering the Art of Image Blurring Mastering the Art of Image Blurring in Java The Ultimate Guide to Blurring Images on Android Understanding and Implementing Blur Image in JQuery An Extensive Walkthrough of Blurring Images with JavaScript How to Use HTML, CSS, and JavaScript to Make an Image Slider HTML Image Tag How to Crop GIFs? How to Align Images with CSS Ken Burns Effect – Complete Guide and How to Apply It Cartoonify – Complete Guide on Cartoonify Image Effect Mastering Web Aesthetics: A Comprehensive Guide to Gradient Fades Sepia Effect: The Ultimate Guide to the Sepia Photo Effect What is Vignette? Guide to Vignette Image Editing Pixelate – The Ultimate Guide to the Pixelation Effect How to Outline an Image: Enhancing Visual Appeal and Depth Make Your Photos Pop with Image Effects Upscale Image – Developers guide to AI-driven image upscaling Image Manipulation: History, Concepts and a Complete Guide A Full Guide to Object-aware Cropping Simplify Your Life with Automatic Image Tagging How To Resize Images In WordPress How To Create a Progress Bar For Asset Uploads Animated GIFs – What They Are And How To Create Them How To Automatically Improve Image Resolution AI Drop Shadow Get Image Dimensions From URLs Automatically Add Sepia Effect To Images Automatically Make an Image a Cartoon Automatically Add Blur Faces Effect To Images Automatically Add Background Removal Effect to an Image How to Resize an Image with React How to Easily Resize an Image with React Native

Ultimate Guide to Photo Gallery on Android

android photo gallery

The number of mobile users is expected to reach nearly 7.5 billion (source: Statista) – nearly every person on Earth! This vast user base makes the Android platform a canvas for creating impactful applications.

Photo galleries are essential features for any operating system, including Android. Visual media is king, and everyone needs a way to view their photos. A photo gallery is more than just a digital storage space. They can have additional features like photo editing (cropping, filters, rotation, etc.) and serve as a way to quickly browse, search, and filter images.

In this guide, we’ll explore how you can create a photo gallery on Android. We’ll be using Cloudinary to store and serve images within the cloud, freeing up space on our user’s device and even opening up our app to new features in the future.

In this article:

Before we begin, you’ll need a Cloudinary account. If you don’t have one already, you can sign up for a free account to get started. Once you’ve signed up, log in to your Cloudinary account and copy your Cloudinary API credentials for later.

text to images android

For this project, we will be using Android Studio. If you don’t have it installed, you can download the latest version from the Android Studio website.

android photo gallery

Initializing Our Android Studio Project

We will select an Empty Views Activity project in Android Studio to create our UI. Ensure that you choose a template that supports Java.

Next, we will name our project Photo Gallery and select its directory. Now, select the default programming language, Java, and the minimum SDK requirement for our project, Android 11. Finally, we will click on Finish to finish setting up our project.

android photo gallery

Once Grade finishes setting up your project dependencies, we can begin creating the UI for our app. Let’s start by heading over to our activity_main.xml file in our project’s res/layout folder. This file contains the main app screen. We will open this file in Android Studio.

android photo gallery

Here, we will first delete our Hello World textbox. Next, right-click on ConstraintLayout and select Change View…. Here, we will change our view to the relative layout.

android photo gallery

Next, search for the GridView object in the object palette and drop it in our UI. This will create a dynamic grid view for your app. Now, open up the code of our XML file by clicking on the Code button on the top-right corner highlighted in the image above. Here, we will add vertical and horizontal spacing to our GridView object and define the number of columns as auto_fit. Here is what our activity_main.xml file looks like:

android photo gallery

Here is what the source code of our file looks like:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <GridView
       android:id="@+id/myGrid"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:columnWidth="100dp"
       android:numColumns="auto_fit"
       android:verticalSpacing="10dp"
       android:horizontalSpacing="10dp"
       />

 </androidx.constraintlayout.widget.ConstraintLayout>

Next, we will create a custom dialog to display our image. To do this, right-click on the layout folder of your project and head to New > Layout Resource File. Here, we will set our file name as custom_dialog and paste the following code inside our XML file:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
   xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:layout_gravity="center"
   android:layout_margin="10dp"
   >
   <TextView
       android:id="@+id/txt_Image_name"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="Image Name"
       android:gravity="center"
       android:textSize="20sp"
       />
   <ImageView
       android:id="@+id/img"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_below="@+id/txt_Image_name"
       android:scaleType="centerCrop"
       android:src="@mipmap/ic_launcher_round" />
   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="horizontal"
       android:layout_below="@+id/img"
       >
       <Button
           android:id="@+id/btn_close"
           android:layout_width="0dp"
           android:layout_height="wrap_content"
           android:text="Close"
           android:textColor="#fff"
           android:background="#000"
           android:layout_weight="1"
           />
   </LinearLayout>
</RelativeLayout>

Here, we simply create a small component that shows an ImageView with a Close button. The purpose of this dialog box is to open up a picture once it has been clicked on. Here is what our dialog box looks like:

android photo gallery

Coding Our Android App

With our UI ready, we need to set up the necessary dependencies for our project. So, head over to your build.gradle file and add the dependencies below:

implementation("com.squareup.picasso:picasso:2.8")
implementation("com.cloudinary:cloudinary-android:2.5.0")
implementation("com.android.volley:volley:1.2.1")

Here, we are adding Picasso, a powerful image downloading and caching library for Android, to display our images, and the Cloudinary Android SDK is our dependency. We have also added Volley, which allows us to make API calls to retrieve JSON objects.

This is what our gradle file looks like:

android photo gallery

Next, we must give our app some permissions. Android apps require these permissions to access data on your phone and use the internet. To do this, open up your AndroidManifest.xml file and use the <uses-permission> tag to 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" />

With the project now set up, we can begin coding our app. We will begin opening up our MainActivity.java file in the com.example.cloudinary folder of our project. Here, we will navigate to the MainActivity class and start by defining an array that contains our URLs as well as a HashMap called config:

public class MainActivity extends AppCompatActivity {

   ArrayList<String> mImageUrls = new ArrayList<>();

   Map config = new HashMap();

Next, inside the onCreate() function, we start by setting the main screen to activity_main and define our Cloudinary API:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   // 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);

Next, we create our Cloudinary API URL and retrieve our GridView using the findViewById() function. We will also create an ImageAdapter object (which we will define later) and set our gridView’s adapter as the ImageAdapter object:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   // 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);

   String request_url = "https://res.cloudinary.com/<your_cloud_name>/image/list/<your_image_tag>.json";

   GridView gridView = findViewById(R.id.myGrid);
   ImageAdapter imageAdapter = new ImageAdapter(mImageUrls, this);
   gridView.setAdapter(imageAdapter);

Next, we create a StringRequest that calls on the Cloudinary API URL and retrieves all the images in a certain tag as a JSON object. Here, we will be defining the tag as "family". Next, we traverse over the items inside the JSON object, and for each image, we extract its public ID.

Finally, we use the MediaManager to create a Cloudinary URL with the same width and height as our ImageView before finally storing these URLs in our array. We then add this string request to our Volley’s queue:

...
// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);

// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, request_url,
       new Response.Listener<String>() {
           @Override
           public void onResponse(String response) {
               try {
                   // Convert the response to a JSONObject
                   JSONObject jsonObject = new JSONObject(response);

                   Log.d("JSON", String.valueOf(jsonObject));

                   // Get the JSONArray of resources
                   JSONArray resourcesArray = jsonObject.getJSONArray("resources");

                   // Loop through the array and get the public_id of each object
                   for (int i = 0; i < resourcesArray.length(); i++) {
                       JSONObject resourceObject = resourcesArray.getJSONObject(i);
                       String public_id = resourceObject.getString("public_id");

                       // Add the public_id to your mImageUrls list
                       mImageUrls.add(MediaManager.get().url().transformation(new Transformation().width(350).height(450).crop("pad")).generate(public_id));
                   }

                   // Notify your adapter that the data has changed
                   imageAdapter.notifyDataSetChanged();

               } catch (JSONException e) {
                   e.printStackTrace();
               }
           }
       }, new Response.ErrorListener() {
   @Override
   public void onErrorResponse(VolleyError error) {
       Log.e("Volley", "That didn't work!");
   }
});

// Add the request to the RequestQueue.
queue.add(stringRequest);
...

To complete our onCreate() function, we add an on-click function that will show our Dialog Box, which we will define later. Below is our complete onCreate() function:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   // Initializing Cloudinary configuration
   config.put("cloud_name", "your_");
   config.put("api_key","your_api_key");
   config.put("api_secret","your_api_secret");
   MediaManager.init(this, config);

   String request_url = "https://res.cloudinary.com/<your_cloud_name>/image/list/<your_image_tag>.json";

   GridView gridView = findViewById(R.id.myGrid);
   ImageAdapter imageAdapter = new ImageAdapter(mImageUrls, this);
   gridView.setAdapter(imageAdapter);

   // Instantiate the RequestQueue.
   RequestQueue queue = Volley.newRequestQueue(this);

   // Request a string response from the provided URL.
   StringRequest stringRequest = new StringRequest(Request.Method.GET, request_url,
           new Response.Listener<String>() {
               @Override
               public void onResponse(String response) {
                   try {
                       // Convert the response to a JSONObject
                       JSONObject jsonObject = new JSONObject(response);

                       Log.d("JSON", String.valueOf(jsonObject));

                       // Get the JSONArray of resources
                       JSONArray resourcesArray = jsonObject.getJSONArray("resources");

                       // Loop through the array and get the public_id of each object
                       for (int i = 0; i < resourcesArray.length(); i++) {
                           JSONObject resourceObject = resourcesArray.getJSONObject(i);
                           String public_id = resourceObject.getString("public_id");

                           // Add the public_id to your mImageUrls list
                           mImageUrls.add(MediaManager.get().url().transformation(new Transformation().width(350).height(450).crop("pad")).generate(public_id));
                       }

                       // Notify your adapter that the data has changed
                       imageAdapter.notifyDataSetChanged();

                   } catch (JSONException e) {
                       e.printStackTrace();
                   }
               }
           }, new Response.ErrorListener() {
       @Override
       public void onErrorResponse(VolleyError error) {
           Log.e("Volley", "That didn't work!");
       }
   });

   // Add the request to the RequestQueue.
   queue.add(stringRequest);

   gridView.setOnItemClickListener((parent, view, position, id) -> {
       String item_pos = mImageUrls.get(position);
       ShowDialogBox(item_pos);
   });
}

Next, we will define our ShowDialogBox() function. The function starts by creating a dialog box and setting the view as the custom_dialog. Next, it retrieves the objects from the view and stores them in variables. Finally, it extracts the URL of the image using the position parameter and then uses Picasso to display it. It also adds an on-click function to the Cancel button to close the dialog.

Following is the complete MainActivity.java code:

package com.example.photogallery;

import androidx.appcompat.app.AppCompatActivity;

import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.cloudinary.Transformation;
import com.cloudinary.android.MediaManager;
import com.squareup.picasso.Picasso;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

   ArrayList<String> mImageUrls = new ArrayList<>();

   Map config = new HashMap();

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       // 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);

       String request_url = "https://res.cloudinary.com/<your_cloud_name>/image/list/<your_image_tag>.json";

       GridView gridView = findViewById(R.id.myGrid);
       ImageAdapter imageAdapter = new ImageAdapter(mImageUrls, this);
       gridView.setAdapter(imageAdapter);

       // Instantiate the RequestQueue.
       RequestQueue queue = Volley.newRequestQueue(this);

       // Request a string response from the provided URL.
       StringRequest stringRequest = new StringRequest(Request.Method.GET, request_url,
               new Response.Listener<String>() {
                   @Override
                   public void onResponse(String response) {
                       try {
                           // Convert the response to a JSONObject
                           JSONObject jsonObject = new JSONObject(response);

                           Log.d("JSON", String.valueOf(jsonObject));

                           // Get the JSONArray of resources
                           JSONArray resourcesArray = jsonObject.getJSONArray("resources");

                           // Loop through the array and get the public_id of each object
                           for (int i = 0; i < resourcesArray.length(); i++) {
                               JSONObject resourceObject = resourcesArray.getJSONObject(i);
                               String public_id = resourceObject.getString("public_id");

                               // Add the public_id to your mImageUrls list
                               mImageUrls.add(MediaManager.get().url().transformation(new Transformation().width(350).height(450).crop("pad")).generate(public_id));
                           }

                           // Notify your adapter that the data has changed
                           imageAdapter.notifyDataSetChanged();

                       } catch (JSONException e) {
                           e.printStackTrace();
                       }
                   }
               }, new Response.ErrorListener() {
           @Override
           public void onErrorResponse(VolleyError error) {
               Log.e("Volley", "That didn't work!");
           }
       });

       // Add the request to the RequestQueue.
       queue.add(stringRequest);

       gridView.setOnItemClickListener((parent, view, position, id) -> {
           String item_pos = mImageUrls.get(position);
           ShowDialogBox(item_pos);
       });
   }

   public void ShowDialogBox(final String item_pos){
       final Dialog dialog = new Dialog(this);

       dialog.setContentView(R.layout.custom_dialog);

       //Getting custom dialog views
       TextView Image_name = dialog.findViewById(R.id.txt_Image_name);
       ImageView Image = dialog.findViewById(R.id.img);
       Button btn_Close = dialog.findViewById(R.id.btn_close);

       //extracting name
       int index = item_pos.lastIndexOf("/");
       String name = item_pos.substring(index+1);
       Image_name.setText(name);

       Picasso.get().load(item_pos).into(Image);

       btn_Close.setOnClickListener(v -> dialog.dismiss());

       dialog.show();
   }
}

Now that we’ve completed our MainActivity class, we need to define our ImageAdapter class to extend the BaseAdapter class. The purpose of this class is to handle the loading and displaying of images in a grid view. We will begin by defining a constructor for the class, which will store the array of URLs as well as the context of our code:

public class ImageAdapter extends BaseAdapter {
   private List<String> mThumbUrls;
   private Context mContext;

   public ImageAdapter(List<String> mThumbUrls, Context mContext) {
       this.mThumbUrls = mThumbUrls;
       this.mContext = mContext;
   }

Next, we override some function of the BaseAdapter class as follows:

public class ImageAdapter extends BaseAdapter {
   private List<String> mThumbUrls;
   private Context mContext;

   public ImageAdapter(List<String> mThumbUrls, Context mContext) {
       this.mThumbUrls = mThumbUrls;
       this.mContext = mContext;
   }

Finally, we will override the most important method in the adapter, i.e., getView(). We override the getView() method from the BaseAdapter class so that it creates and populates the view for each image item in the grid view. It first tries to retrieve an existing ImageView from the convertView parameter.

This parameter is reused to improve performance by recycling existing views instead of creating new ones for each item. If convertView is null, it creates a new ImageView using the provided context and sets its layout parameters and scale type. It then uses Picasso to load the image from the URL stored at the specified position in the mThumbUrls list. The loaded image is then set to the ImageView. Finally, the method returns the ImageView, which will be displayed in the grid view.

Here is our complete ImageAdapter class:

package com.example.photogallery;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;

import com.squareup.picasso.Picasso;

import java.util.List;

public class ImageAdapter extends BaseAdapter {
   private List<String> mThumbUrls;
   private Context mContext;

   public ImageAdapter(List<String> mThumbUrls, Context mContext) {
       this.mThumbUrls = mThumbUrls;
       this.mContext = mContext;
   }

   @Override
   public int getCount() {
       return mThumbUrls.size();
   }

   @Override
   public Object getItem(int position) {
       return this;
   }

   @Override
   public long getItemId(int position) {
       return 0;
   }

   @Override
   public View getView(int position, View convertView, ViewGroup parent) {
       ImageView imageView = (ImageView) convertView;

       if(imageView == null){
           imageView = new ImageView(mContext);
           imageView.setLayoutParams(new GridView.LayoutParams(350,450));
           imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
       }

       Picasso.get().load(mThumbUrls.get(position)).into(imageView);

       return imageView;
   }
}

Testing Our Photo Gallery App

Now that our code is ready, all we need to do is run our app.

Here is what our app looks like:

android photo gallery

Now click on an image to show it inside a dialog box:

android photo gallery

Wrapping Up

This guide provided a roadmap for creating robust photo galleries on Android devices. While the core functionalities can be built using native libraries, Cloudinary offers a compelling alternative. Its cloud-based storage, image delivery optimization, and transformation capabilities can significantly enhance your photo gallery app. Cloudinary streamlines image management ensures fast loading times, and provides your users a wide range of image manipulation options.

So, what are you waiting for? Take your Android photo gallery app to the next level with Cloudinary. Sign up for a free account and explore its powerful features designed for exceptional photo management.

More from Cloudinary:

Building an Image Gallery Using Contentful and Cloudinary

Building an Image Gallery with Next.js Parallel and Intercepting Routes: A Cloudinary Integration Guide

Last updated: Sep 8, 2024