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….