Loading an image from internet is pretty easier using Volley library. But here is a much better solution than volley i.e Glide image library. When compared to volley, Glide wins in lot of scenarios in terms of performance and usability. Below are the advantages of Glide over volley.
- Supports fetching, decoding, and displaying video stills, images, and animated GIFs
- Placeholder can be added before the loading the media
- Loads thumbnail (blurred) first and then loads the high resolution image like in WhatsApp or Facebook.
- Crossfading effects between the media
- Supports image arbitrary transformations like loading image in circular shape or any other shape.
- Better Memory and disk caching mechanisms
- Works well with both Volley and OkHttp libraries
Demo Video
This article explains how to load images from internet. First all the images displayed in a grid manner and upon selecting the single image, a fullscreen image slider will be launched.
How to Use Glide Library?
Integrating Glide in your project is very easy. First add the glide dependency to your build.gradle.
dependencies { // glide implementation 'com.github.bumptech.glide:glide:3.7.0' }
Second load the image into ImageView using below code snippet.
String imgUrl = "http://api.codedecode.in/json/glide/image/The_Platform.jpg"; ImageView imageView = (ImageView) view.findViewById(R.id.thumbnail); Glide.with(mContext).load(imgUrl) .thumbnail(0.5f) .crossFade() .diskCacheStrategy(DiskCacheStrategy.ALL) .into(imageView);
Sample JSON
To build the app, I have created a sample JSON which contains the image.
JSON link: http://api.codedecode.in/json/glide/glide.json
[{ "name": "The Platform", "img": "http://api.codedecode.in/json/glide/image/The_Platform.jpg", "timestamp": " 20 March 2020" }, { "name": "The Gentlemen", "img": "http://api.codedecode.in/json/glide/image/The_Gentlemen.jpg", "timestamp": "24 January 2020" }, . . . }]
So let’s start by Creating a App
1. Create a new project in Android Studio from File ⇒ New ⇒ New Project. When it prompts you to select the default activity, select Blank Activity and proceed.
2. Open build.gradle and add Glide, Volley and RecyclerView dependencies. Volley is used to download the glide json by making HTTP call. RecyclerView is used to show the gallery images in a Grid fashion.
dependencies { // glide implementation 'com.github.bumptech.glide:glide:3.7.0' // volley implementation 'com.android.volley:volley:1.1.1' // RecyclerView implementation 'androidx.recyclerview:recyclerview:1.1.0' //Coordinator Layout implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" //Material Design implementation 'com.google.android.material:material:1.1.0' }
3. Create three packages named activity, adapter, app, model and helper and place your MainActivity.java under activity package. These packages helps in keeping your project organized.
4. Create a class named AppController.java under app package. This is a singleton class in which we initialize the volley’s core objects.
AppController.java
package com.codedecode.androidglideimagelibrary.app; import android.app.Application; import android.text.TextUtils; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.toolbox.Volley; public class AppController extends Application { public static final String TAG = AppController.class.getSimpleName(); private RequestQueue mRequestQueue; private static AppController mInstance; @Override public void onCreate() { super.onCreate(); mInstance = this; } public static synchronized AppController getInstance() { return mInstance; } public RequestQueue getRequestQueue() { if (mRequestQueue == null) { mRequestQueue = Volley.newRequestQueue(getApplicationContext()); } return mRequestQueue; } public <T> void addToRequestQueue(Request<T> req, String tag) { // set the default tag if tag is empty req.setTag(TextUtils.isEmpty(tag) ? TAG : tag); getRequestQueue().add(req); } public <T> void addToRequestQueue(Request<T> req) { req.setTag(TAG); getRequestQueue().add(req); } public void cancelPendingRequests(Object tag) { if (mRequestQueue != null) { mRequestQueue.cancelAll(tag); } } }
5. Open AndroidManifest.xml and add the AppController to <application> tag. Also add the INTERNET permission as we need to make HTTP calls.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.codedecode.androidglideimagelibrary"> <uses-permission android:name="android.permission.INTERNET" /> <application android:networkSecurityConfig="@xml/network_security_config" android:name=".app.AppController" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".activity.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Now our project is ready with all the dependencies added. Let’s start adding the grid first.
Adding the Grid View
6. Open the layout files activity_main.xml and add the recyclerView.
<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context=".activity.MainActivity"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> </com.google.android.material.appbar.AppBarLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".activity.MainActivity" tools:showIn="@layout/activity_main"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:scrollbars="vertical" /> </RelativeLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
7. Under helper package, create a class named SquareLayout.java. This class helps the images to display in square ratio in grid view.
SquareLayout.java
package com.codedecode.androidglideimagelibrary.helper; import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.util.AttributeSet; import android.widget.RelativeLayout; 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) { // Set a square layout. super.onMeasure(widthMeasureSpec, widthMeasureSpec); } }
8. Under res ⇒ layout, create a layout named gallery_thumbnail.xml. This layout contains an ImageView to display the thumbnail image in gallery view.
gallery_thumbnail.xml
<?xml version="1.0" encoding="utf-8"?> <com.codedecode.androidglideimagelibrary.helper.SquareLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black" android:orientation="vertical"> <ImageView android:id="@+id/thumbnail" android:layout_width="match_parent" android:layout_height="match_parent" android:adjustViewBounds="true" android:scaleType="centerCrop" /> </com.codedecode.androidglideimagelibrary.helper.SquareLayout>
9. Under adapter package, create a class named GalleryAdapter.java This is a adapter class which inflates the gallery_thumbnail.xml and renders the images in recyclerView.
GalleryAdapter.java
package com.codedecode.androidglideimagelibrary.adapter; import android.content.Context; import android.view.GestureDetector; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.codedecode.androidglideimagelibrary.R; import com.codedecode.androidglideimagelibrary.model.Image; import java.util.List; public class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.MyViewHolder> { private List<Image> images; private Context mContext; static class MyViewHolder extends RecyclerView.ViewHolder { ImageView thumbnail; MyViewHolder(View view) { super(view); thumbnail = view.findViewById(R.id.thumbnail); } } public GalleryAdapter(Context context, List<Image> images) { mContext = context; this.images = images; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.gallery_thumbnail, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { Image image = images.get(position); Glide.with(mContext).load(image.getIMG()) .thumbnail(0.5f) .crossFade() .diskCacheStrategy(DiskCacheStrategy.ALL) .into(holder.thumbnail); } @Override public int getItemCount() { return images.size(); } public interface ClickListener { void onClick(View view, int position); void onLongClick(View view, int position); } public static class RecyclerTouchListener implements RecyclerView.OnItemTouchListener { private GestureDetector gestureDetector; private ClickListener clickListener; public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) { this.clickListener = clickListener; gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { return true; } @Override public void onLongPress(MotionEvent e) { View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); if (child != null && clickListener != null) { clickListener.onLongClick(child, recyclerView.getChildPosition(child)); } } }); } @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { View child = rv.findChildViewUnder(e.getX(), e.getY()); if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) { clickListener.onClick(child, rv.getChildPosition(child)); } return false; } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { } @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } } }
10. Finally open MainActivity.java and do the below changes
- Download the json by making volley http request. fetchImages() method is used for this purpose.
- Parse the json and add the models to array list.
- Pass the array list to recyclerView’s adapter class.
MainActivity.java
package com.codedecode.androidglideimagelibrary.activity; import android.app.ProgressDialog; import android.os.Bundle; import android.util.Log; import android.view.View; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.FragmentTransaction; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.android.volley.Response; import com.android.volley.VolleyError; import com.android.volley.toolbox.JsonArrayRequest; import com.codedecode.androidglideimagelibrary.R; import com.codedecode.androidglideimagelibrary.adapter.GalleryAdapter; import com.codedecode.androidglideimagelibrary.app.AppController; import com.codedecode.androidglideimagelibrary.model.Image; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private String TAG = MainActivity.class.getSimpleName(); private static final String endpoint = "http://api.codedecode.in/json/glide/glide.json"; private ArrayList<Image> images; private ProgressDialog pDialog; private GalleryAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); RecyclerView recyclerView = findViewById(R.id.recycler_view); pDialog = new ProgressDialog(this); images = new ArrayList<>(); mAdapter = new GalleryAdapter(getApplicationContext(), images); RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(getApplicationContext(), 2); recyclerView.setLayoutManager(mLayoutManager); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(mAdapter); fetchImages(); } private void fetchImages() { pDialog.setMessage("Downloading json..."); pDialog.show(); JsonArrayRequest req = new JsonArrayRequest(endpoint, new Response.Listener<JSONArray>() { @Override public void onResponse(JSONArray response) { Log.d(TAG, response.toString()); pDialog.hide(); images.clear(); for (int i = 0; i < response.length(); i++) { try { JSONObject object = response.getJSONObject(i); Image image = new Image(); image.setName(object.getString("name")); image.setIMG(object.getString("img")); image.setTimestamp(object.getString("timestamp")); images.add(image); } catch (JSONException e) { Log.e(TAG, "Json parsing error: " + e.getMessage()); } } mAdapter.notifyDataSetChanged(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.e(TAG, "Error: " + error.getMessage()); pDialog.hide(); } }); // Adding request to request queue AppController.getInstance().addToRequestQueue(req); } }
If you run the app, you can see the images displayed in grid manner. Be sure that your device is connected to internet.
Fullscreen Image Slideshow
Now we’ll see how to build a fullscreen image slider with swiping functionality. We use a DialogFragment and ViewPager for this purpose.
11. Create a layout named image_fullscreen_preview.xml under res ⇒ layout. This layout is used to display the image in fullscreen view.
image_fullscreen_preview.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/RelativeLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black"> <ImageView android:id="@+id/image_preview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_centerInParent="true" android:scaleType="fitCenter" /> </RelativeLayout>
12. Under activity package, create a class named SlideshowDialogFragment.java. This is a fragment class which extends DialogFragment.
SlideshowDialogFragment.java
package com.codedecode.androidglideimagelibrary.activity; import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import androidx.fragment.app.DialogFragment; import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.codedecode.androidglideimagelibrary.R; import com.codedecode.androidglideimagelibrary.model.Image; import java.util.ArrayList; public class SlideshowDialogFragment extends DialogFragment { private String TAG = SlideshowDialogFragment.class.getSimpleName(); private ArrayList<Image> images; private ViewPager viewPager; private TextView lblCount, lblTitle, lblDate; private int selectedPosition = 0; static SlideshowDialogFragment newInstance() { SlideshowDialogFragment f = new SlideshowDialogFragment(); return f; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_image_slider, container, false); viewPager = v.findViewById(R.id.viewpager); lblCount = v.findViewById(R.id.lbl_count); lblTitle = v.findViewById(R.id.title); lblDate = v.findViewById(R.id.date); images = (ArrayList<Image>) getArguments().getSerializable("images"); selectedPosition = getArguments().getInt("position"); Log.e(TAG, "position: " + selectedPosition); Log.e(TAG, "images size: " + images.size()); MyViewPagerAdapter myViewPagerAdapter = new MyViewPagerAdapter(); viewPager.setAdapter(myViewPagerAdapter); viewPager.addOnPageChangeListener(viewPagerPageChangeListener); setCurrentItem(selectedPosition); return v; } private void setCurrentItem(int position) { viewPager.setCurrentItem(position, false); displayMetaInfo(selectedPosition); } // page change listener private ViewPager.OnPageChangeListener viewPagerPageChangeListener = new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { displayMetaInfo(position); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }; private void displayMetaInfo(int position) { lblCount.setText((position + 1) + " of " + images.size()); Image image = images.get(position); lblTitle.setText(image.getName()); lblDate.setText(image.getTimestamp()); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Black_NoTitleBar_Fullscreen); } // adapter public class MyViewPagerAdapter extends PagerAdapter { MyViewPagerAdapter() { } @Override public Object instantiateItem(ViewGroup container, int position) { LayoutInflater layoutInflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = layoutInflater.inflate(R.layout.image_fullscreen_preview, container, false); ImageView imageViewPreview = (ImageView) view.findViewById(R.id.image_preview); Image image = images.get(position); Glide.with(getActivity()).load(image.getIMG()) .thumbnail(0.5f) .crossFade() .diskCacheStrategy(DiskCacheStrategy.ALL) .into(imageViewPreview); container.addView(view); return view; } @Override public int getCount() { return images.size(); } @Override public boolean isViewFromObject(View view, Object obj) { return view == ((View) obj); } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } } }
13. Open MainActivity.java and add the click event to recyclerView in onCreate() method.
recyclerView.addOnItemTouchListener(new GalleryAdapter.RecyclerTouchListener(getApplicationContext(), recyclerView, new GalleryAdapter.ClickListener() { @Override public void onClick(View view, int position) { Bundle bundle = new Bundle(); bundle.putSerializable("images", images); bundle.putInt("position", position); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); SlideshowDialogFragment newFragment = SlideshowDialogFragment.newInstance(); newFragment.setArguments(bundle); newFragment.show(ft, "slideshow"); } @Override public void onLongClick(View view, int position) { } }));
Run the app once more and try tapping on image. You should see the fullscreen image slider with swiping functionality enabled.
Happy Coding 🙂
Yash Pawar
Yash is hardcore programmer and programming has been his passion since he compiled his first hello-world program. Solving real problems of developers through tutorials has always been interesting part for him.