Loading Images from the internet or device is a very hard task to do and create gallery android application becomes a lot more difficult as it contains images everywhere. If we load images directly in android, then it will take lots of run-time memory which leads to memory exceptions. This can be handled by using proper image caching. Building this image caching system can take a long time. There are many libraries that can be used to load images from the internet or device efficiently like Picasso, Glide, or Universal Image Loader.

These Libraries take well care of image caching and displaying. In this post, we will learn how to create a gallery android application. The application will display images from a specific bucket from device storage. Clicking on any image grid will display that image in full screen and at the bottom, there will be a strip of thumbnails with all images listed in it so that the user does not have to come back for selecting another image.

Download Code

Demo

How to create gallery android application - Android Picasso Image Library - | loopwiki.com

How to use Picasso

Adding Picasso library in your project is super easy just add following line of code inside dependency section of build.gradle(module app) file.

dependencies {

    /*add Picasso Library Here*/
    compile 'com.squareup.picasso:picasso:2.5.2'
}

Following is snippets how we can load image using Picasso inside ImageView

Picasso.with(context).load(Path_to_image).into(targetImageView);

 

How to create gallery android application

1.Create new android studio project File -> New Project with name as you want and select add no activity and proceed

2.Open build.gradle file and modify it according to following

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.2"
    defaultConfig {
        applicationId "com.loopwiki.androidmaterialgallery"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:26.+'
    compile 'com.android.support:design:26.+'
    compile 'com.android.support:support-v4:26.+'
    testCompile 'junit:junit:4.12'

    /*add Picasso Library Here*/
    compile 'com.squareup.picasso:picasso:2.5.2'
}

3.Create new class layout -> SquareLayout.java this class will help to keep ImageView in square ratio

package com.loopwiki.androidmaterialgallery;

/**
 * Created by amardeep on 11/3/2017.
 */

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.widget.RelativeLayout;

//square layout
public class SquareLayout extends RelativeLayout {

    public SquareLayout(Context context) {
        super(context);
    }

    public SquareLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SquareLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SquareLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, widthMeasureSpec);
    }
}

4. Create a new layout -> custom_row_gallery_item.xml. This will be the structure of a single grid. Add ImageView and TextView which will be used for displaying image thumbnail and image name respectively.

<?xml version="1.0" encoding="utf-8"?>
<com.loopwiki.androidmaterialgallery.SquareLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

<!--Thumbnail of gallery item-->
    <ImageView
        android:id="@+id/imageViewThumbnail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleType="centerCrop" />
<!--Textview for image name-->
    <TextView
        android:id="@+id/textViewImageName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/imageViewThumbnail"
        android:background="#89000000"
        android:ellipsize="end"
        android:lines="1"
        android:padding="4dp"
        android:textAppearance="@style/TextAppearance.AppCompat.Small" />


</com.loopwiki.androidmaterialgallery.SquareLayout>

5. Create new class package name->GalleryItem.java . This class is a blueprint of data inside a single gallery item. This class contains two fields Uri of image and name of the image.

package com.loopwiki.androidmaterialgallery;

/**
 * Created by amardeep on 11/3/2017.
 */
//This class represents single gallery item
public class GalleryItem {
    public String imageUri;
    public String imageName;
    public boolean isSelected = false;

    public GalleryItem(String imageUri, String imageName) {
        this.imageUri = imageUri;
        this.imageName = imageName;
    }
}

6. Create a new class Package name ->ScreenUtils.java. This class used to get the width and height of the screen. Actually we did not need to create this class we can get screen width and height directly but to keep things organized we are creating this class.

package com.loopwiki.androidmaterialgallery;

import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.DisplayMetrics;

/**
 * Created by amardeep on 11/3/2017.
 */

public class ScreenUtils {

    //static method to get screen width
    public static int getScreenWidth(Context context) {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.widthPixels;
    }

    //static method to get screen height
    public static int getScreenHeight(Context context) {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.heightPixels;
    }
}

7. Create a new class Package name -> GalleryUtils.java. This class helps to get images from device storage from the Download folder. You can modify  CAMERA_IMAGE_BUCKET_NAME to other folders like “/DCIM/Camera” or any other folder.

package com.loopwiki.androidmaterialgallery;

import android.content.Context;
import android.database.Cursor;
import android.os.Environment;
import android.provider.MediaStore;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by amardeep on 10/25/2017.
 */

public class GalleryUtils {

    //Define bucket name from which you want to take images Example '/DCIM/Camera' for camera images
    public static final String CAMERA_IMAGE_BUCKET_NAME = Environment.getExternalStorageDirectory().toString() + "/Download";

    //method to get id of image bucket from path
    public static String getBucketId(String path) {
        return String.valueOf(path.toLowerCase().hashCode());
    }

    //method to get images
    public static List<GalleryItem> getImages(Context context) {
        final String[] projection = {MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DATA};
        final String selection = MediaStore.Images.Media.BUCKET_ID + " = ?";
        final String[] selectionArgs = {GalleryUtils.getBucketId(CAMERA_IMAGE_BUCKET_NAME)};
        final Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                projection,
                selection,
                selectionArgs,
                null);
        ArrayList<GalleryItem> result = new ArrayList<GalleryItem>(cursor.getCount());
        if (cursor.moveToFirst()) {
            final int dataColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            final int nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME);
            do {
                GalleryItem galleryItem = new GalleryItem(cursor.getString(dataColumn), cursor.getString(nameColumn));
                result.add(galleryItem);
            } while (cursor.moveToNext());
        }
        cursor.close();
        return result;

    }
}

8. Create a new class which will serve as Adapter for our Main gallery view on app startup package name->GalleryAdapter.java add following code to it

package com.loopwiki.androidmaterialgallery;

import android.content.Context;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.squareup.picasso.Picasso;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by amardeep on 11/3/2017.
 */

public class GalleryAdapter extends RecyclerView.Adapter {
    //Declare GalleryItems List
    List<GalleryItem> galleryItems;
    Context context;
    //Declare GalleryAdapterCallBacks
    GalleryAdapterCallBacks mAdapterCallBacks;

    public GalleryAdapter(Context context) {
        this.context = context;
        //get GalleryAdapterCallBacks from contex
        this.mAdapterCallBacks = (GalleryAdapterCallBacks) context;
        //Initialize GalleryItem List
        this.galleryItems = new ArrayList<>();
    }

    //This method will take care of adding new Gallery items to RecyclerView
    public void addGalleryItems(List<GalleryItem> galleryItems) {
        int previousSize = this.galleryItems.size();
        this.galleryItems.addAll(galleryItems);
        notifyItemRangeInserted(previousSize, galleryItems.size());

    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View row = inflater.inflate(R.layout.custom_row_gallery_item, parent, false);
        return new GalleryItemHolder(row);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        //get current Gallery Item
        GalleryItem currentItem = galleryItems.get(position);
        //Create file to load with Picasso lib
        File imageViewThoumb = new File(currentItem.imageUri);
        //cast holder with gallery holder
        GalleryItemHolder galleryItemHolder = (GalleryItemHolder) holder;
        //Load with Picasso
        Picasso.with(context)
                .load(imageViewThoumb)
                .centerCrop()
                .resize(ScreenUtils.getScreenWidth(context) / 2, ScreenUtils.getScreenHeight(context) / 3)//Resize image to width half of screen and height 1/3 of screen height
                .into(galleryItemHolder.imageViewThumbnail);
        //set name of Image
        galleryItemHolder.textViewImageName.setText(currentItem.imageName);
        //set on click listener on imageViewThumbnail
        galleryItemHolder.imageViewThumbnail.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //call onItemSelected method and pass the position and let activity decide what to do when item selected
                mAdapterCallBacks.onItemSelected(position);
            }
        });

    }

    @Override
    public int getItemCount() {
        return galleryItems.size();
    }

    public class GalleryItemHolder extends RecyclerView.ViewHolder {
        ImageView imageViewThumbnail;
        TextView textViewImageName;

        public GalleryItemHolder(View itemView) {
            super(itemView);
            imageViewThumbnail = itemView.findViewById(R.id.imageViewThumbnail);
            textViewImageName = itemView.findViewById(R.id.textViewImageName);

        }
    }

    //Interface for communication of Adapter and MainActivity
    public interface GalleryAdapterCallBacks {
        void onItemSelected(int position);
    }


}

Breaking GalleryAdapter

Following method used to manage to add of new gallery items and to notify Adapter to update its view whenever new items get added

//This method will take care of adding new Gallery items to RecyclerView
public void addGalleryItems(List<GalleryItem> galleryItems) {
        int previousSize = this.galleryItems.size();
        this.galleryItems.addAll(galleryItems);
        notifyItemRangeInserted(previousSize, galleryItems.size());

    }

Now when user will click on any gallery item how we can notify activity that which item is clicked ? For that we have interface class GalleryAdapterCallBacks which will handle communication between activity and Adapter.

//Interface for communication of Adapter and MainActivity
    public interface GalleryAdapterCallBacks {
        //call this method to notify about item is clicked 
        void onItemSelected(int position);
    }

9. Now we will create activity which will contain Recyclerview.  So create new layout -> actvitiy_main.xml. Add Recyclerview in layout as shown below

<?xml version="1.0" encoding="utf-8"?>
<!--Define Gallery RecyclerView-->
<android.support.v7.widget.RecyclerView 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/recyclerViewGallery"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical"
    tools:context="com.loopwiki.androidmaterialgallery.MainActivity"
    tools:showIn="@layout/activity_main">


</android.support.v7.widget.RecyclerView>


Create new class under Package name -> MainActivity.java

package com.loopwiki.androidmaterialgallery;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.DialogFragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.Toast;

import java.util.List;

//Remember to implement  GalleryAdapter.GalleryAdapterCallBacks to activity  for communication of Activity and Gallery Adapter
public class MainActivity extends AppCompatActivity implements GalleryAdapter.GalleryAdapterCallBacks {
    //Deceleration of list of  GalleryItems
    public List<GalleryItem> galleryItems;
    //Read storage permission request code
    private static final int RC_READ_STORAGE = 5;
    GalleryAdapter mGalleryAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //setup RecyclerView
        RecyclerView recyclerViewGallery = (RecyclerView) findViewById(R.id.recyclerViewGallery);
        recyclerViewGallery.setLayoutManager(new GridLayoutManager(this, 2));
        //Create RecyclerView Adapter
        mGalleryAdapter = new GalleryAdapter(this);
        //set adapter to RecyclerView
        recyclerViewGallery.setAdapter(mGalleryAdapter);
        //check for read storage permission
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
            //Get images
            galleryItems = GalleryUtils.getImages(this);
            // add images to gallery recyclerview using adapter
            mGalleryAdapter.addGalleryItems(galleryItems);
        } else {
            //request permission
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, RC_READ_STORAGE);
        }


    }


    @Override
    public void onItemSelected(int position) {
        //create fullscreen SlideShowFragment dialog
        SlideShowFragment slideShowFragment = SlideShowFragment.newInstance(position);
        //setUp style for slide show fragment
        slideShowFragment.setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogFragmentTheme);
        //finally show dialogue
        slideShowFragment.show(getSupportFragmentManager(), null);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == RC_READ_STORAGE) {
            if (grantResults.length > 0 &amp;&amp; grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //Get images
                galleryItems = GalleryUtils.getImages(this);
                // add images to gallery recyclerview using adapter
                mGalleryAdapter.addGalleryItems(galleryItems);
            } else {
                Toast.makeText(this, "Storage Permission denied", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

Braking MainActivity

The following Piece of code will initialize and will setup Recyclerview. Set layout manager as GridLayoutManager with span count of 2 ( Read this post if you want to know more about layout managers refer this post Android RecyclerView Tutorial – Layout Managers )

//setup RecyclerView
        RecyclerView recyclerViewGallery = (RecyclerView) findViewById(R.id.recyclerViewGallery);
        recyclerViewGallery.setLayoutManager(new GridLayoutManager(this, 2));
        //Create RecyclerView Adapter
        mGalleryAdapter = new GalleryAdapter(this);
        //set adapter to RecyclerView
        recyclerViewGallery.setAdapter(mGalleryAdapter);

Beginning in Android 6.0 (API level 23), users grant permissions to apps while the app is running, not when they install the app. So we need to ask Read Storage permission before getting images from the device or it will lead to a crash. In the following snippet, we are first checking user already granted permission. Case 1: User already granted permission we will query images from Device storage. Now case 2: when if a user is launching app for the first time then permission is not given yet so ask for permission.

 //check for read storage permission
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
            //Get images
            galleryItems = GalleryUtils.getImages(this);
            // add images to gallery recyclerview using adapter
            mGalleryAdapter.addGalleryItems(galleryItems);
        } else {
            //request permission
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, RC_READ_STORAGE);
        }

After asking permission we will get the result of permission in onRequestPermissionsResult() method. Check the result. Case 1: user granted permission then query images. Case 2: user denied permission then show an error message to the user or handle it accordingly following piece of code handles the result of permission.

 @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == RC_READ_STORAGE) {
            if (grantResults.length > 0 &amp;&amp; grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //Get images
                galleryItems = GalleryUtils.getImages(this);
                // add images to gallery recyclerview using adapter
                mGalleryAdapter.addGalleryItems(galleryItems);
            } else {
                Toast.makeText(this, "Storage Permission denied", Toast.LENGTH_SHORT).show();
            }
        }
    }

create gallery android application gallery view screen

Building Full Screen Dialogue

10. Now when the user will select an image from the above grid we will launch a full-screen dialogue fragment. This dialogue will contain ViewPager to display the selected images. Also, ViewPager will help to load the next or previous image on-screen swipes. This dialogue also contains a horizontal Recyclerview strip in which we will load small size thumbnails of all images so that the user does not have to go back to select another image. Now first we will prepare our ViewPager  Adapter and Horizontal Recyclerview strip Adapter. Create a new layout  -> pager_item.xml  this layout will contain ImageView.

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="fitCenter"
    android:id="@+id/imageViewThumbnail"
    android:orientation="vertical">

</ImageView>

11.Create new class Package name -> SlideShowPagerAdapter.java this class will serve as Adpater for viewpager

package com.loopwiki.androidmaterialgallery;

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import com.squareup.picasso.Picasso;

import java.io.File;
import java.util.List;

/**
 * Created by amardeep on 11/3/2017.
 */

public class SlideShowPagerAdapter extends PagerAdapter {

    Context mContext;
    //Layout inflater
    LayoutInflater mLayoutInflater;
    //list of Gallery Items
    List<GalleryItem> galleryItems;

    public SlideShowPagerAdapter(Context context, List<GalleryItem> galleryItems) {
        mContext = context;
        mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //set galleryItems
        this.galleryItems = galleryItems;
    }

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

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == ((ImageView) object);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View itemView = mLayoutInflater.inflate(R.layout.pager_item, container, false);
        ImageView imageView = (ImageView) itemView.findViewById(R.id.imageViewThumbnail);
        //load current image in viewpager
        Picasso.with(mContext).load(new File(galleryItems.get(position).imageUri)).fit().into(imageView);
        container.addView(itemView);
        return itemView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((ImageView) object);
    }

}

12. Create new layout -> custom_row_gallery_strip_item.xml. This is structure of single item in horizontal Recyclerview strip.

<?xml version="1.0" encoding="utf-8"?>
<com.loopwiki.androidmaterialgallery.SquareLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/squareLayout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/imageViewThumbnail"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:padding="2dp"
        android:scaleType="fitXY" />


</com.loopwiki.androidmaterialgallery.SquareLayout>

12. Now we need to create Adapter for Horizontal Recyclerview Strip. Create new class Package name -> GalleryStripAdapter.java this class will serve as adapter for horizontal strip Recyclerview.

package com.loopwiki.androidmaterialgallery;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ThumbnailUtils;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;


import java.util.List;


public class GalleryStripAdapter extends RecyclerView.Adapter {
    //Declare list of GalleryItems
    List<GalleryItem> galleryItems;
    Context context;
    GalleryStripCallBacks mStripCallBacks;
    GalleryItem mCurrentSelected;

    public GalleryStripAdapter(List<GalleryItem> galleryItems, Context context, GalleryStripCallBacks StripCallBacks, int CurrentPosition) {
        //set galleryItems
        this.galleryItems = galleryItems;
        this.context = context;
        //set stripcallbacks
        this.mStripCallBacks = StripCallBacks;
        //set current selected
        mCurrentSelected = galleryItems.get(CurrentPosition);
        //set current selected item as selected
        mCurrentSelected.isSelected = true;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View row = inflater.inflate(R.layout.custom_row_gallery_strip_item, parent, false);
        SquareLayout squareLayout = row.findViewById(R.id.squareLayout);
        return new GalleryStripItemHolder(squareLayout);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        //get Curent Gallery Item
        GalleryItem mCurrentItem = galleryItems.get(position);
        //get thumb square size 1/6 of screen width
        final int thumbSize = ScreenUtils.getScreenWidth(context) / 6;
        //cast holder to galleryStripItemHolder
        GalleryStripItemHolder galleryStripItemHolder = (GalleryStripItemHolder) holder;
        //get thumb size bitmap by using ThumbnailUtils
        Bitmap ThumbImage = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(mCurrentItem.imageUri),
                thumbSize, thumbSize);
        //set thumbnail
        galleryStripItemHolder.imageViewThumbnail.setImageBitmap(ThumbImage);
        //set current selected
        if (mCurrentItem.isSelected) {
            galleryStripItemHolder.imageViewThumbnail.setBackgroundColor(ContextCompat.getColor(context, android.R.color.white));
        } else {
            //value 0 removes any background color
            galleryStripItemHolder.imageViewThumbnail.setBackgroundColor(0);
        }
        galleryStripItemHolder.imageViewThumbnail.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //call onGalleryStripItemSelected on click and pass position
                mStripCallBacks.onGalleryStripItemSelected(position);
            }
        });
    }

    @Override
    public int getItemCount() {
        return galleryItems.size();
    }

    public class GalleryStripItemHolder extends RecyclerView.ViewHolder {
        ImageView imageViewThumbnail;

        public GalleryStripItemHolder(View itemView) {
            super(itemView);
            imageViewThumbnail = itemView.findViewById(R.id.imageViewThumbnail);
        }
    }

    //interface for communication on gallery strip interactions
    public interface GalleryStripCallBacks {
        void onGalleryStripItemSelected(int position);
    }

    //Method to highlight  selected item on gallery strip
    public void setSelected(int position) {
        //remove current selection
        mCurrentSelected.isSelected = false;
        //notify recyclerview that we changed  item to update its view
        notifyItemChanged(galleryItems.indexOf(mCurrentSelected));
        //select gallery item
        galleryItems.get(position).isSelected = true;
        //notify recyclerview that we changed  item to update its view
        notifyItemChanged(position);
        //set current selected
        mCurrentSelected = galleryItems.get(position);

    }

    //method to remove selection
    public void removeSelection() {
        mCurrentSelected.isSelected = false;
    }


}

Breaking GalleryStripAdapter

Following code will create small thumbnail and set it to ImageView.

//get Curent Gallery Item
        GalleryItem mCurrentItem = galleryItems.get(position);
        //get thumb square size 1/6 of screen width
        final int thumbSize = ScreenUtils.getScreenWidth(context) / 6;
        //cast holder to galleryStripItemHolder
        GalleryStripItemHolder galleryStripItemHolder = (GalleryStripItemHolder) holder;
        //get thumb size bitmap by using ThumbnailUtils
        Bitmap ThumbImage = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(mCurrentItem.imageUri),
                thumbSize, thumbSize);
        //set thumbnail
        galleryStripItemHolder.imageViewThumbnail.setImageBitmap(ThumbImage);

Now when user will select any image then we need to highlight that item on Horizontal Recyclerview Strip for that we used following snippets

if (mCurrentItem.isSelected) {
            galleryStripItemHolder.imageViewThumbnail.setBackgroundColor(ContextCompat.getColor(context, android.R.color.white));
        } else {
            //value 0 removes any background color
            galleryStripItemHolder.imageViewThumbnail.setBackgroundColor(0);
        }

But when user will select any other image on Horizontal Recyclerview Strip then we have change selected image highlight and done by using following code

//Method to highlight  selected item on gallery strip
    public void setSelected(int position) {
        //remove current selection
        mCurrentSelected.isSelected = false;
        //notify recyclerview that we changed  item to update its view
        notifyItemChanged(galleryItems.indexOf(mCurrentSelected));
        //select gallery item
        galleryItems.get(position).isSelected = true;
        //notify recyclerview that we changed  item to update its view
        notifyItemChanged(position);
        //set current selected
        mCurrentSelected = galleryItems.get(position);

    }

13. Seems our adapters and layout are ready now will create Full screen dialogue fragment. Create new layout -> fragment_silde_show.xml. Add ViewPager and at bottom add Recyclerview.

<RelativeLayout 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:orientation="vertical"
    tools:context="com.loopwiki.androidmaterialgallery.SlideShowFragment">

<!--Viewpager to show slideshow of images-->
    <android.support.v4.view.ViewPager
        android:id="@+id/viewPagerGallery"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/recyclerViewGalleryStrip" />
<!--Image name textview-->
    <TextView
        android:id="@+id/textViewImageName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/recyclerViewGalleryStrip"
        android:background="#aa000000"
        android:ellipsize="end"
        android:gravity="center_horizontal"
        android:padding="16dp"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

        <!--Gallery strip Recyclerview-->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerViewGalleryStrip"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true" />

</RelativeLayout>

14. Create new class Package name -> SlideShowFragment.java. This dialogue will get launched when user will select image.

package com.loopwiki.androidmaterialgallery;


import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;


//Remember to implement  GalleryStripAdapter.GalleryStripCallBacks to fragment  for communication of fragment and GalleryStripAdapter
public class SlideShowFragment extends DialogFragment implements GalleryStripAdapter.GalleryStripCallBacks {
    //declare static variable which will serve as key of current position argument
    private static final String ARG_CURRENT_POSITION = "position";
    //Declare list of GalleryItems
    List<GalleryItem> galleryItems;
    //Deceleration of  Gallery Strip Adapter
    GalleryStripAdapter mGalleryStripAdapter;
    // //Deceleration of  Slide show View Pager Adapter
    SlideShowPagerAdapter mSlideShowPagerAdapter;
    //Deceleration of viewPager
    ViewPager mViewPagerGallery;
    TextView textViewImageName;
    RecyclerView recyclerViewGalleryStrip;

    private int mCurrentPosition;
    //set bottom to visible of first load
    boolean isBottomBarVisible = true;


    public SlideShowFragment() {
        // Required empty public constructor
    }

    //This method will create new instance of SlideShowFragment
    public static SlideShowFragment newInstance(int position) {
        SlideShowFragment fragment = new SlideShowFragment();
        //Create bundle
        Bundle args = new Bundle();
        //put Current Position in the bundle
        args.putInt(ARG_CURRENT_POSITION, position);
        //set arguments of SlideShowFragment
        fragment.setArguments(args);
        //return fragment instance
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //initialise GalleryItems List
        galleryItems = new ArrayList<>();
        if (getArguments() != null) {
            //get Current selected position from arguments
            mCurrentPosition = getArguments().getInt(ARG_CURRENT_POSITION);
            //get GalleryItems from activity
            galleryItems = ((MainActivity) getActivity()).galleryItems;
        }

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_silde_show, container, false);
        mViewPagerGallery = view.findViewById(R.id.viewPagerGallery);
        // set On Touch Listener on mViewPagerGallery to hide show bottom bar
        mViewPagerGallery.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                    if (isBottomBarVisible) {
                        //bottom bar is visible make it invisible
                        FadeOutBottomBar();
                    } else {
                        //bottom bar is invisible make it visible
                        FadeInBottomBar();
                    }
                }
                return false;
            }

        });
        textViewImageName = view.findViewById(R.id.textViewImageName);
        //Initialise View Pager Adapter
        mSlideShowPagerAdapter = new SlideShowPagerAdapter(getContext(), galleryItems);
        //set adapter to Viewpager
        mViewPagerGallery.setAdapter(mSlideShowPagerAdapter);
        recyclerViewGalleryStrip = view.findViewById(R.id.recyclerViewGalleryStrip);
        //Create GalleryStripRecyclerView's Layout manager
        final RecyclerView.LayoutManager mGalleryStripLayoutManger = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);
        //set layout manager of GalleryStripRecyclerView
        recyclerViewGalleryStrip.setLayoutManager(mGalleryStripLayoutManger);
        //Create GalleryStripRecyclerView's Adapter
        mGalleryStripAdapter = new GalleryStripAdapter(galleryItems, getContext(), this, mCurrentPosition);
        //set Adapter of GalleryStripRecyclerView
        recyclerViewGalleryStrip.setAdapter(mGalleryStripAdapter);
        //tell viewpager to open currently selected item and pass position of current item
        mViewPagerGallery.setCurrentItem(mCurrentPosition);
        //set image name textview's text according to position
        textViewImageName.setText(galleryItems.get(mCurrentPosition).imageName);
        //Add OnPageChangeListener to viewpager to handle page changes
        mViewPagerGallery.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                //set image name textview's text on any page selected
                textViewImageName.setText(galleryItems.get(position).imageName);
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                //first check When Page is scrolled and gets stable
                if (state == ViewPager.SCROLL_STATE_IDLE) {
                    //get current  item on view pager
                    int currentSelected = mViewPagerGallery.getCurrentItem();
                    //scroll strip smoothly to current  position of viewpager
                    mGalleryStripLayoutManger.smoothScrollToPosition(recyclerViewGalleryStrip, null, currentSelected);
                    //select current item of viewpager on gallery strip at bottom
                    mGalleryStripAdapter.setSelected(currentSelected);

                }

            }
        });
        return view;
    }

    //Overridden method by GalleryStripAdapter.GalleryStripCallBacks for communication on gallery strip item selected
    @Override
    public void onGalleryStripItemSelected(int position) {
        //set current item of viewpager
        mViewPagerGallery.setCurrentItem(position);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        //remove selection on destroy
        mGalleryStripAdapter.removeSelection();
    }

    //Method to fadeIn bottom bar which is image textview name
    public void FadeInBottomBar() {
        //define alpha animation
        AlphaAnimation fadeIn = new AlphaAnimation(0.0f, 1.0f);
        //set duration
        fadeIn.setDuration(1200);
        //set animation listener
        fadeIn.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                //set textview visible on animation ends
                textViewImageName.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        //start animation
        textViewImageName.startAnimation(fadeIn);
        isBottomBarVisible = true;
    }

    public void FadeOutBottomBar() {
        //define alpha animation
        AlphaAnimation fadeOut = new AlphaAnimation(1.0f, 0.0f);
        //set duration
        fadeOut.setDuration(1200);
        //set animation listener
        fadeOut.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                //set textview Visibility gone on animation ends
                textViewImageName.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        //start animation
        textViewImageName.startAnimation(fadeOut);
        isBottomBarVisible = false;

    }
}

Breaking SlideShowFragment

Following code is used to set visibility on and off with smooth animation of image name TextView on touching ViewPager

mViewPagerGallery.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                    if (isBottomBarVisible) {
                        //bottom bar is visible make it invisible
                        FadeOutBottomBar();
                    } else {
                        //bottom bar is invisible make it visible
                        FadeInBottomBar();
                    }
                }
                return false;
            }

        });

 //Method to fadeIn bottom bar which is image textview name
    public void FadeInBottomBar() {
        //define alpha animation
        AlphaAnimation fadeIn = new AlphaAnimation(0.0f, 1.0f);
        //set duration
        fadeIn.setDuration(1200);
        //set animation listener
        fadeIn.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                //set textview visible on animation ends
                textViewImageName.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        //start animation
        textViewImageName.startAnimation(fadeIn);
        isBottomBarVisible = true;
    }

    public void FadeOutBottomBar() {
        //define alpha animation
        AlphaAnimation fadeOut = new AlphaAnimation(1.0f, 0.0f);
        //set duration
        fadeOut.setDuration(1200);
        //set animation listener
        fadeOut.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                //set textview Visibility gone on animation ends
                textViewImageName.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        //start animation
        textViewImageName.startAnimation(fadeOut);
        isBottomBarVisible = false;

    }

Following is snippets used to handle Scrolling Horizontal Recyclerview Strip , selecting items on strip on ViewPager page changes and setting  text of image name TextView on-page changes  of  ViewPager

mViewPagerGallery.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                //set image name textview's text on any page selected
                textViewImageName.setText(galleryItems.get(position).imageName);
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                //first check When Page is scrolled and gets stable
                if (state == ViewPager.SCROLL_STATE_IDLE) {
                    //get current  item on view pager
                    int currentSelected = mViewPagerGallery.getCurrentItem();
                    //scroll strip smoothly to current  position of viewpager
                    mGalleryStripLayoutManger.smoothScrollToPosition(recyclerViewGalleryStrip, null, currentSelected);
                    //select current item of viewpager on gallery strip at bottom
                    mGalleryStripAdapter.setSelected(currentSelected);

                }

            }
        });
create gallery android application output screen slideshow fragment

15. Open values -> styles.xml. and paste following theme in it.

<resources>

    <! – Base application theme. – >
    <style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
        <! – Customize your theme here. – >
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="windowActionBarOverlay">true</item>
    </style>

    <style name="AppTheme.Transparent" parent="AppTheme">
        <item name="windowActionBarOverlay">true</item>
    </style>

    <!--Full screen dialogue theme – >
    <style name="DialogFragmentTheme" parent="AppTheme.Transparent">
        <item name="android:paddingRight">0dp</item>
        <item name="android:paddingLeft">0dp</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:windowNoTitle">true</item>
    </style>


</resources>

Now create new style.xml which will apply for android API level 21 and greater (If you don’t know how to create this version files then take look at this nice answer on stack overflow  Click here ). Add following styles to it

<resources>

    <! – Base application theme. – >
    <style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
        <! – Customize your theme here. – >
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="windowActionBarOverlay">true</item>
    </style>

    <style name="AppTheme.Transparent" parent="AppTheme">
        <item name="windowActionBarOverlay">true</item>

    </style>

    <!--Full screen dialogue theme – >
    <style name="DialogFragmentTheme" parent="AppTheme.Transparent">
        <item name="android:paddingRight">0dp</item>
        <item name="android:paddingLeft">0dp</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:windowNoTitle">true</item>
    </style>


</resources>

16.Open values -> colors.xml. and paste following colors in it.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>
</resources>

17.Open AndroidManifest.xml. and modify it as following. Remember to to add READ_EXTERNAL_STORAGE permission.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.loopwiki.androidmaterialgallery">

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:theme="@style/AppTheme.Transparent">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

20. Now run your application and see the results If you still have any queries, please post them in the comments section below, I will be happy to help you.

Author

Hello there, My name is Amardeep founder of loopwiki.com. I have experience in many technologies like Android, Java, Php, etc. In this variety of technologies, I love Android App Development. If you have any idea and you want me to develop for you then let's have chat Conatct

24 Comments

  1. Thanks for great tutorial. Btw I want to load photo not from DCIM but from URL?

  2. Hello,
    Can you tell me how to order the images? How are they sorted?

  3. hello sir thanks for sharing your android skills and how can we let the image load from the drawable folder instead of the dowload folder

  4. hello sir thanks for sharing your android skills and how can we let the image load from the drawable folder instead of the galary

    • look buddy drawable folder images are accessed by using dynamic id’s. It is possible but will not be wise to load from it

  5. Thanks a lot man! You saved my day. This is very hard actually but atleast this worked for me.
    the picasso libraries are changed actually.

  6. I tried your project but it doesnt load any image … i only see a blank screen

  7. How to get this same result using Firebase and Picasso. Please help. I have different categories and when an image from specific category is selected it will display like this.

    • Once again this tutorial is specially designed to show working of gallery android application. We can take this topics in our later tutorials

  8. This is a great tutorial, but when I install this, the images are not stick together
    according to this code .resize(ScreenUtils.getScreenWidth(context) / 2, ScreenUtils.getScreenHeight(context) / 3)
    there are 2 pictures in a row, but the next two pictures are in next screen(scroll up by the screen height). I cannot figure out why….. There is a big gap between two rows, the size of the gap is the 2/3 screen height.

  9. thank its good tutorial . i want add another folder how to add folders like camera ,Bluetooth….. this program not support multiple folders please help me

    • you can create dynamic content just create adapter which will loop though all folders and you can fetch this in background using async tasks

  10. Pyae Phone Kyaw Reply

    Hello,
    Thank you for your tutorial. But when i install on my phone which is android version 7.0, it is not work as you shown in above video. Application is loading with gray screen and nothing happen.

    • Hello mate, this application shows images from the Download directory. If it is not showing images then you probably don’t have any images in Download directory.

  11. Kalpesh Bhangare Reply

    Thank you very much for this excellent tutorial. I have a question, How to make recyclerview scroll horizontally?

    • You can define orientation parameter while setting layout manager following is example
      LayoutManager layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);

      set this layout manager to recyclerview
      recyclerView.setLayoutManager(layoutManager);

Write A Comment