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 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 && 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 && 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();
}
}
}
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);
}
}
});

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.
24 Comments
hello cannot downloaad this library ? whats the issue?
Thanks for great tutorial. Btw I want to load photo not from DCIM but from URL?
Hello,
Can you tell me how to order the images? How are they sorted?
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
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
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.
I tried your project but it doesnt load any image … i only see a blank screen
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.
If possible make a full tutorial on this. Please.
Once again this tutorial is specially designed to show working of gallery android application. We can take this topics in our later tutorials
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.
You got the understanding remaining won’t matter cheers.
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
So how do I import pictures in it? Thanks
How can I use for it in mysql database. Please modify your source code and give me link.
You can create API to get data from your mysql and then load that images accordingly
Thank you bro. Now, i get it. Sorry for my mistake.
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.
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);
Please sir, tell me how to add image link….