Skip to content
Related Articles

Related Articles

How to Build a Photo Viewing Application in Android?
  • Last Updated : 22 Mar, 2021

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 the 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. Below is the vide0 in which we will get to see what we are going to build in this article. 

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 in build.gradle file



Navigate to the app > Gradle Scripts > build.gradle(:app) and add the below dependency to it. We are using Picasso for loading images from paths in our ImageView

// below dependency for using Picasso image loading library

implementation ‘com.squareup.picasso:picasso:2.71828’

Now sync your project and we will move towards adding permissions in our AndroidManifest.xml file. 

Step 3: Adding permissions in our AndroidManifest.xml file

Navigate to the app > AndroidManifest.xml file and add the below permissions to it.

XML




<!-- permissions for reading external storage -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

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. 



XML




android:hardwareAccelerated="false"
android:largeHeap="true"

Step 4: Working with the activity_main.xml file

Navigate to the app > res > layout > activity_main.xml and add the below code to that file. Below is the code for the activity_main.xml file. 

XML




<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    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/idRVImages"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
  
</RelativeLayout>

Step 5: Creating an item for displaying in a RecyclerView 

Navigate to the app > res > layout > Right-click on it > New > layout Resource file and create a new layout resource file. Name the file as card_layout and add the below code to it. Comments are added in the code to get to know in more detail. 

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    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 6: Creating a new activity for displaying a single image

Navigate to the app > java > your app’s package name > Right-click on it > New > Empty 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. 

Step 7: Creating an adapter class for setting data to each item in our RecyclerView 

Navigate to the app > java > your app’s package name > Right-click on it > New Java class and name your class as RecyclerViewAdapter and add the below code to it. Comments are added in the code to get to know in more detail. 

Java




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.squareup.picasso.Picasso;
  
import java.io.File;
import java.util.ArrayList;
  
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder> {
      
    // creating a variable for our context and array list.
    private final Context context;
    private final ArrayList<String> imagePathArrayList;
  
    // on below line we have created a constructor.
    public RecyclerViewAdapter(Context context, ArrayList<String> imagePathArrayList) {
        this.context = context;
        this.imagePathArrayList = imagePathArrayList;
    }
  
    @NonNull
    @Override
    public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // Inflate Layout in this method whch we have created.
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout, parent, false);
        return new RecyclerViewHolder(view);
    }
  
    @Override
    public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
          
        // on below line we are getting th file from the
        // path which we have stored in our list.
        File imgFile = new File(imagePathArrayList.get(position));
          
        // on below line we are checking if tje file exists or not.
        if (imgFile.exists()) {
              
            // if the file exists then we are displaying that file in our image view using picasso library.
            Picasso.get().load(imgFile).placeholder(R.drawable.ic_launcher_background).into(holder.imageIV);
              
            // on below line we are adding click listener to our item of recycler view.
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                      
                    // inside on click listner we are creating a new intent
                    Intent i = new Intent(context, ImageDetailActivity.class);
                      
                    // on below line we are passing the image path to our new activity.
                    i.putExtra("imgPath", imagePathArrayList.get(position));
                      
                    // at last we are starting our activity.
                    context.startActivity(i);
                }
            });
        }
    }
  
    @Override
    public int getItemCount() {
        // this method returns 
        // the size of recyclerview
        return imagePathArrayList.size();
    }
  
    // View Holder Class to handle Recycler View.
    public static class RecyclerViewHolder extends RecyclerView.ViewHolder {
          
        // creating variables for our views.
        private final ImageView imageIV;
  
        public RecyclerViewHolder(@NonNull View itemView) {
            super(itemView);
            // initializing our views with their ids.
            imageIV = itemView.findViewById(R.id.idIVImage);
        }
    }
}

Step 8: Working with the MainActivity.java file

Go to the MainActivity.java file and refer to the following code. Below is the code for the MainActivity.java file. Comments are added inside the code to understand the code in more detail.

Java




import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.MediaStore;
import android.widget.Toast;
  
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
  
import java.util.ArrayList;
  
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
  
public class MainActivity extends AppCompatActivity {
      
    // on below line we are creating variables for
    // our array list, recycler view and adapter class.
    private static final int PERMISSION_REQUEST_CODE = 200;
    private ArrayList<String> imagePaths;
    private RecyclerView imagesRV;
    private RecyclerViewAdapter imageRVAdapter;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
          
        // we are calling a method to request
        // the permissions to read external storage.
        requestPermissions();
          
        // creating a new array list and
        // initializing our recycler view.
        imagePaths = new ArrayList<>();
        imagesRV = findViewById(R.id.idRVImages);
          
        // calling a method to 
        // prepare our recycler view.
        prepareRecyclerView();
    }
  
    private boolean checkPermission() {
        // in this method we are checking if the permissions are granted or not and returning the result.
        int result = ContextCompat.checkSelfPermission(getApplicationContext(), READ_EXTERNAL_STORAGE);
        return result == PackageManager.PERMISSION_GRANTED;
    }
  
    private void requestPermissions() {
        if (checkPermission()) {
            // if the permissions are already granted we are calling
            // a method to get all images from our external storage.
            Toast.makeText(this, "Permissions granted..", Toast.LENGTH_SHORT).show();
            getImagePath();
        } else {
            // if the permissions are not granted we are
            // calling a method to request permissions.
            requestPermission();
        }
    }
      
    private void requestPermission() {
        //on below line we are requesting the rea external storage permissions.
        ActivityCompat.requestPermissions(this, new String[]{READ_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
    }
  
    private void prepareRecyclerView() {
          
        // in this method we are preparing our recycler view.
        // on below line we are initializing our adapter class.
        imageRVAdapter = new RecyclerViewAdapter(MainActivity.this, imagePaths);
          
        // on below line we are creating a new grid layout manager.
        GridLayoutManager manager = new GridLayoutManager(MainActivity.this, 4);
          
        // on below line we are setting layout
        // manager and adapter to our recycler view.
        imagesRV.setLayoutManager(manager);
        imagesRV.setAdapter(imageRVAdapter);
    }
  
    private void getImagePath() {
        // in this method we are adding all our image paths 
        // in our arraylist which we have created.
        // on below line we are checking if the device is having an sd card or not.
        boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
  
        if (isSDPresent) {
              
            // if the sd card is present we are creating a new list in
            // which we are getting our images data with their ids.
            final String[] columns = {MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID};
              
            // on below line we are creating a new 
            // string to order our images by string.
            final String orderBy = MediaStore.Images.Media._ID;
              
            // this method will stores all the images
            // from the gallery in Cursor
            Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null, null, orderBy);
              
            // below line is to get total number of images
            int count = cursor.getCount();
              
            // on below line we are running a loop to add 
            // the image file path in our array list.
            for (int i = 0; i < count; i++) {
                  
                // on below line we are moving our cursor position
                cursor.moveToPosition(i);
                  
                // on below line we are getting image file path
                int dataColumnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
                  
                // after that we are getting the image file path
                // and adding that path in our array list.
                imagePaths.add(cursor.getString(dataColumnIndex));
            }
            imageRVAdapter.notifyDataSetChanged();
            // after adding the data to our 
            // array list we are closing our cursor.
            cursor.close();
        }
    }
  
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        // this method is called after permissions has been granted.
        switch (requestCode) {
            // we are checking the permission code.
            case PERMISSION_REQUEST_CODE:
                // in this case we are checking if the permissions are accepted or not.
                if (grantResults.length > 0) {
                    boolean storageAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                    if (storageAccepted) {
                        // if the permissions are accepted we are displaying a toast message
                        // and calling a method to get image path.
                        Toast.makeText(this, "Permissions Granted..", Toast.LENGTH_SHORT).show();
                        getImagePath();
                    } else {
                        // if permissions are denied we are closing the app and displaying the toast message.
                        Toast.makeText(this, "Permissions denined, Permissions are required to use the app..", Toast.LENGTH_SHORT).show();
                    }
                }
                break;
        }
    }
}

Step 9: Working with the activity_image_detail.xml file. 

Navigate to the app > res > layout > activity_image_detail.xml and add the below code to that file. Below is the code for the activity_image_detail.xml file. 

XML




<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ImageDetailActivity">
  
    <!--image view to display our image-->
    <ImageView
        android:id="@+id/idIVImage"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:layout_centerInParent="true" />
  
</RelativeLayout>

Step 10: Working with ImageDetailActivity.java file 

Go to the ImageDetailActivity.java file and refer to the following code. Below is the code for the ImageDetailActivity.java file. Comments are added inside the code to understand the code in more detail.

Java




import android.os.Bundle;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;
  
import androidx.appcompat.app.AppCompatActivity;
  
import com.squareup.picasso.Picasso;
  
import java.io.File;
  
public class ImageDetailActivity extends AppCompatActivity {
      
    // creating a string variable, image view variable 
    // and a variable for our scale gesture detector class.
    String imgPath;
    private ImageView imageView;
    private ScaleGestureDetector scaleGestureDetector;
      
    // on below line we are defining our scale factor.
    private float mScaleFactor = 1.0f;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image_detail);
         
        // on below line getting data which we have passed from our adapter class.
        imgPath = getIntent().getStringExtra("imgPath");
          
        // initializing our image view.
        imageView = findViewById(R.id.idIVImage);
          
        // on below line we are initializing our scale gesture detector for zoom in and out for our image.
        scaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());
         
        // on below line we are getting our image file from its path.
        File imgFile = new File(imgPath);
          
        // if the file exists then we are loading that image in our image view.
        if (imgFile.exists()) {
            Picasso.get().load(imgFile).placeholder(R.drawable.ic_launcher_background).into(imageView);
        }
    }
  
    @Override
    public boolean onTouchEvent(MotionEvent motionEvent) {
        // inside on touch event method we are calling on
        // touch event method and pasing our motion event to it.
        scaleGestureDetector.onTouchEvent(motionEvent);
        return true;
    }
  
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        // on below line we are creating a class for our scale
        // listener and  extending it with gesture listener.
        @Override
        public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
              
            // inside on scale method we are setting scale
            // for our image in our image view.
            mScaleFactor *= scaleGestureDetector.getScaleFactor();
            mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
              
            // on below line we are setting 
            // scale x and scale y to our image view.
            imageView.setScaleX(mScaleFactor);
            imageView.setScaleY(mScaleFactor);
            return true;
        }
    }
}

Now run your app and see the output of the app. 

Output:

Note: Make sure to grant read storage permissions. 

Attention reader! Don’t stop learning now. Get hold of all the important Java Foundation and Collections concepts with the Fundamentals of Java and Java Collections Course at a student-friendly price and become industry ready. To complete your preparation from learning a language to DS Algo and many more,  please refer Complete Interview Preparation Course.

My Personal Notes arrow_drop_up
Recommended Articles
Page :