How to Build a Photo Viewing Application in Android?
Last Updated :
27 Mar, 2025
Gallery app is one of the most used apps which comes pre-installed on many Android devices and there are several different apps that are present in Google Play to view the media files present in your device. In this article, we will be simply creating a Gallery app in which we can view all the photos which we have stored on our device. Along with that, we can view individual photos in our app as well.
What we are going to build in this article?
We will be building a simple application in which we will be simply displaying the list of photos in the grid format and on clicking on the photo we can view that photo and can zoom in the photo to view it properly.
Important: This project cannot be run from Android 13+ since the permission READ_EXTERNAL_STORAGE is deprecated.
Step by Step Implementation
Step 1: Create a New Project
To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio.
Note that select Java as the programming language.
Step 2: Add the dependency for image loading
Navigate to the Gradle Scripts > build.gradle.kts (Module :app) and add the below dependency to it. We are using Glide for loading images from paths in our ImageView.
dependencies {
...
implementation ("com.github.bumptech.glide:glide:4.16.0")
}
Now sync your project.
Step 3: Adding permissions to read external storage
Navigate to the app > manifests > AndroidManifest.xml file and add the below permissions to it.
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
As we are loading all the images from our storage at a time so we have to add 2 attributes to our application tag in the AndroidManifest.xml file. Navigate to the AndroidManifest.xml file and add below two lines in your application tag of Manifest file.
<application
...
android:hardwareAccelerated="false"
android:largeHeap="true"
...
</application>
Step 4: Working with the layout files
Navigate to the app > res > layout > activity_main.xml and add the below code to that file. Now, create a new layout for each item of the recycler view and add the below code to it.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<!--recycler view for displaying the list of images-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
item_rv.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="3dp"
android:elevation="8dp"
app:cardCornerRadius="8dp">
<!--Image view for displaying the image
in our card layout in recycler view-->
<ImageView
android:id="@+id/idIVImage"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:scaleType="centerCrop" />
</androidx.cardview.widget.CardView>
Step 5: Creating a new activity for displaying a single image
Navigate to the app > java > {package-name}, Right-click on it, New > Empty Views Activity and name your activity as ImageDetailActivity and create a new activity. We will be using this activity to display our single image from the list of different images.
ImageDetailActivity.java
package org.geeksforgeeks.demo;
import android.os.Bundle;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.bumptech.glide.Glide;
import java.io.File;
public class ImageDetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_image_detail);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
String imagePath = getIntent().getStringExtra("imgPath");
if (imagePath != null) {
ImageView imageView = findViewById(R.id.image);
Glide.with(this).load(new File(imagePath)).into(imageView);
}
}
}
ImageDetailActivity.kt
package org.geeksforgeeks.demo
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.bumptech.glide.Glide
import java.io.File
class ImageDetailActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_image_detail)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
val imagePath = intent.getStringExtra("imgPath")
Glide.with(this).load(File(imagePath!!)).into(findViewById(R.id.image))
}
}
activity_image_detail.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ImageDetailActivity">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
Step 6: Creating an adapter class for the RecyclerView
Navigate to the app > java > {package-name}, Right-click on it, New Java/Kotlin class and name your class as Adapter and add the below code to it.
Adapter File:
Adapter.java
package org.geeksforgeeks.demo;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.io.File;
import java.util.ArrayList;
public class Adapter extends RecyclerView.Adapter<Adapter.RecyclerViewHolder> {
private final Context context;
private final ArrayList<String> imagePathArrayList;
public Adapter(Context context, ArrayList<String> imagePathArrayList) {
this.context = context;
this.imagePathArrayList = imagePathArrayList;
}
public static class RecyclerViewHolder extends RecyclerView.ViewHolder {
private final ImageView imageIV;
public RecyclerViewHolder(@NonNull View itemView) {
super(itemView);
imageIV = itemView.findViewById(R.id.idIVImage);
}
}
@NonNull
@Override
public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_rv, parent, false);
return new RecyclerViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
File imgFile = new File(imagePathArrayList.get(position));
if (imgFile.exists()) {
Glide.with(context).load(imgFile).into(holder.imageIV);
holder.itemView.setOnClickListener(v -> {
Intent intent = new Intent(context, ImageDetailActivity.class);
intent.putExtra("imgPath", imagePathArrayList.get(position));
context.startActivity(intent);
});
}
}
@Override
public int getItemCount() {
return imagePathArrayList.size();
}
}
Adapter.kt
package org.geeksforgeeks.demo
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import java.io.File
class Adapter (
private val context: Context,
private val imagePathArrayList: ArrayList<String>
) : RecyclerView.Adapter<Adapter.RecyclerViewHolder>() {
inner class RecyclerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
internal val imageIV: ImageView = itemView.findViewById<ImageView>(R.id.idIVImage)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.item_rv, parent, false)
return RecyclerViewHolder(view)
}
override fun getItemCount(): Int = imagePathArrayList.size
override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
val imgFile = File(imagePathArrayList[position])
if (imgFile.exists()) {
Glide.with(context).load(imgFile).into(holder.imageIV)
holder.itemView.setOnClickListener {
val intent = Intent(context, ImageDetailActivity::class.java)
intent.putExtra("imgPath", imagePathArrayList[position])
context.startActivity(intent)
}
}
}
}
Step 7: Working with the MainActivity file
Go to the MainActivity file and refer to the following code. Below is the code for the MainActivity file. Comments are added inside the code to understand the code in more detail.
MainActivity File:
MainActivity.java
package org.geeksforgeeks.demo;
import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CODE = 200;
private ArrayList<String> imageUris;
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
imageUris = new ArrayList<>();
recyclerView = findViewById(R.id.recyclerView);
requestPermissions();
prepareRecyclerView();
}
private boolean checkPermission() {
int result = ContextCompat.checkSelfPermission(getApplicationContext()
, Manifest.permission.READ_EXTERNAL_STORAGE);
return result == PackageManager.PERMISSION_GRANTED;
}
private void requestPermissions() {
if (checkPermission()) {
Toast.makeText(this, "Permissions granted.", Toast.LENGTH_SHORT).show();
fetchImagePaths();
} else {
requestPermission();
}
}
private void requestPermission() {
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
PERMISSION_REQUEST_CODE
);
}
private void prepareRecyclerView() {
Adapter adapter = new Adapter(this, imageUris);
GridLayoutManager manager = new GridLayoutManager(this, 4);
recyclerView.setLayoutManager(manager);
recyclerView.setAdapter(adapter);
}
private void fetchImagePaths() {
boolean isSDPresent = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
if (isSDPresent) {
String[] columns = {MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID};
String orderBy = MediaStore.Images.Media._ID;
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
columns,
null,
null,
orderBy
);
if (cursor != null) {
int count = cursor.getCount();
for (int i = 0; i < count; i++) {
cursor.moveToPosition(i);
int dataColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
imageUris.add(cursor.getString(dataColumnIndex));
}
cursor.close();
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0) {
boolean storageAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
if (storageAccepted) {
Toast.makeText(this, "Permissions Granted.", Toast.LENGTH_SHORT).show();
fetchImagePaths();
}
else {
Toast.makeText(this, "Permissions denied. Permissions are required to use the app."
, Toast.LENGTH_SHORT).show();
}
}
}
}
}
MainActivity.kt
package org.geeksforgeeks.demo
import android.Manifest.permission
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
private lateinit var imageUris: ArrayList<String>
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: Adapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
imageUris = ArrayList()
recyclerView = findViewById(R.id.recyclerView)
requestPermissions()
prepareRecyclerView()
}
private fun checkPermission(): Boolean {
val result = ContextCompat.checkSelfPermission(applicationContext, permission.READ_EXTERNAL_STORAGE)
return result == PackageManager.PERMISSION_GRANTED
}
private fun requestPermissions() {
if (checkPermission()) {
Toast.makeText(this, "Permissions granted..", Toast.LENGTH_SHORT).show()
imagePath
} else {
requestPermission()
}
}
private fun requestPermission() {
ActivityCompat.requestPermissions(
this,
arrayOf(permission.READ_EXTERNAL_STORAGE),
PERMISSION_REQUEST_CODE
)
}
private fun prepareRecyclerView() {
adapter = Adapter(this, imageUris)
val manager = GridLayoutManager(this, 4)
recyclerView.layoutManager = manager
recyclerView.adapter = adapter
}
private val imagePath: Unit
get() {
val isSDPresent = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
if (isSDPresent) {
val columns = arrayOf(MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID)
val orderBy = MediaStore.Images.Media._ID
val cursor = contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
columns,
null,
null,
orderBy
)
val count = cursor!!.count
for (i in 0 until count) {
cursor.moveToPosition(i)
val dataColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA)
imageUris.add(cursor.getString(dataColumnIndex))
}
cursor.close()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
PERMISSION_REQUEST_CODE ->
if (grantResults.isNotEmpty()) {
val storageAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED
if (storageAccepted) {
Toast.makeText(this, "Permissions Granted..", Toast.LENGTH_SHORT).show()
imagePath
} else {
Toast.makeText(this, "Permissions denied, Permissions are required to use the app..", Toast.LENGTH_SHORT).show()
}
}
}
}
companion object {
private const val PERMISSION_REQUEST_CODE = 200
}
}
Note: Make sure to grant read storage permissions.
Refer to the following github repo to get the entire code: Photo_Viewer_Android
Output:
Similar Reads
How to Build a Photo Viewing Application in Android using Jetpack Compose?
Gallery App is one of the most used Applications which comes pre-installed on many Android devices and there are several different applications that are present in Google Play to view the media files present in a device. In this article, we will be building a simple Gallery Application for Android u
10 min read
How to Build a Wordle Game Application in Android?
Wordle is a famous game in which we have to guess a specific word from the given suggestions. We can get to know the length of the word from that we have to guess the word. In this article, we will take a look at How to create a simple Wordle Game Application in which users will be given 6 chances t
13 min read
How to Create a Voting Application in Android?
In general, voting means comparing two or more entities on the basis of certain conditions. In this article, we will create a simple voting application that uses two different programming languages namely Java and Python, and ask the user to vote for their preferred language. A sample GIF is given b
3 min read
How to Build a Sticky Notes Application in Android Studio?
We as humans tend to forget some small but important things, and to resolve this we try to write those things up and paste them somewhere, we often have eyes on. And in this digital era, the things we are most likely to see are laptop, computer, or mobile phone screens. For this, we all have once us
11 min read
How to Build a Step Counting Application in Android Studio?
Many of us have used the step counter on our phones while we go for walk or run. It counts the total steps that the user has walked and displays it on the screen. The other name for the step counter is a pedometer. But have you ever thought about how can an app count our steps? What is the coding be
7 min read
How to Create a Paint Application in Android?
We all have once used the MS-Paint in our childhood, and when the system was shifted from desks to our palms, we started doodling on Instagram Stories, Hike, WhatsApp, and many more such apps. But have you ever thought about how these functionalities were brought to life? So, In this article, we wil
8 min read
How to Build a Weather App in Android?
In this project, we will be building a weather application. This application will show the temperature of a location. To fetch weather information we will need an API. An API(Application Programming Interface) is a function that allows applications to interact and share data using various components
6 min read
How to Build a Sensor App in Android?
Android mobile phones have sensors, so we can perform various functions like Controlling screen brightness, Monitoring acceleration along a single axis, Motion detection, etc. In this article, we will be building an application that will determine the intensity of light in the room with the help of
5 min read
How to Build Spin the Bottle Game Application in Android?
In this article, we will be building a Spin the Bottle Game Project using Java/Kotlin and XML in Android. The application is based on a multiplayer game. One player spins the bottle and the direction in which the bottle spins will determine the player who gets selected for a task or any other stuff.
5 min read
How to Build a Simple Torch App in Android using Kotlin?
Torch Application is a very basic application that every beginner level android developer should definitely try to build while learning Android. In this article, we will be creating an application in which we will simply display a toggle button to switch on and switch off the torch. Note: If you are
4 min read