Image Gallery by ViewPager Android

    In your application if you want display a series of images to the user, by the ole way, you can make use of the Gallery. The Gallery is a view that shows items (such as images) in a center-locked, horizontal scrolling list. But, with a bug in OS (can see more at this issue on Google code), it's no longer used anymore. No problem, with ViewPager, we found out an alternative solution.
    In this tutorial, you will learn how to use a ViewPager to make a gallery of images in your Android application. ViewPager allows the user to flip left and right through pages of data. Please watch this DEMO VIDEO for this sample project result:

Design activity layout

    In main activity (to run), I put a ViewPager and by each it's page, a single image will be shown. Under it, a HorizontalScrollView also provided for display images thumbnail:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".MainActivity">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.8"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/prev"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_weight="0.1"
            android:contentDescription="@string/app_name"
            android:src="@mipmap/prev" />

        <android.support.v4.view.ViewPager
            android:id="@+id/view_pager"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="0.8">

        </android.support.v4.view.ViewPager>

        <ImageView
            android:id="@+id/next"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_weight="0.1"
            android:contentDescription="@string/app_name"
            android:src="@mipmap/next" />
    </LinearLayout>

    <HorizontalScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.2">

        <LinearLayout
            android:id="@+id/container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal" />
    </HorizontalScrollView>
</LinearLayout>
    After running app, this main screen is like this output:

Activity programmatically code

    Firstly, assume you have some images stored in the res/mipmap-hdpi folder of your project, we will use theme in a drawable array then:
    The most important method in our activity is inflate each image to HorizontalScrollView to get their thumbnails. By using BitmapFactory, we'll decode drawables resource to Bitmap and set to ImageView. To avoid OutOfMemory error, provide a BitmapFactory.Options to managing:
options = new BitmapFactory.Options();
options.inSampleSize = 3;
options.inDither = false;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), images.get(i), options );
imageView.setImageBitmap(bitmap);
    And we will have a full inflateThumbnails() method:
private void inflateThumbnails() {
        for (int i = 0; i < images.size(); i++) {
            View imageLayout = getLayoutInflater().inflate(R.layout.item_image, null);
            ImageView imageView = (ImageView) imageLayout.findViewById(R.id.img_thumb);
            imageView.setOnClickListener(onChagePageClickListener(i));
            options = new BitmapFactory.Options();
            options.inSampleSize = 3;
            options.inDither = false;
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(), images.get(i), options );
            imageView.setImageBitmap(bitmap);
            //set to image view
            imageView.setImageBitmap(bitmap);
            //add imageview
            thumbnailsContainer.addView(imageLayout);
        }
    }
    And now, handle click event of Previous/Next buttons to change ViewPager page programmatically:
private View.OnClickListener onClickListener(final int i) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (i > 0) {
                    //next page
                    if (viewPager.getCurrentItem() < viewPager.getAdapter().getCount() - 1) {
                        viewPager.setCurrentItem(viewPager.getCurrentItem() + 1);
                    }
                } else {
                    //previous page
                    if (viewPager.getCurrentItem() > 0) {
                        viewPager.setCurrentItem(viewPager.getCurrentItem() - 1);
                    }
                }
            }
        };
    }
    By every thumbnail clicked, we also change ViewPager page, handle this event like this:
private View.OnClickListener onChagePageClickListener(final int i) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewPager.setCurrentItem(i);
            }
        };
    }
    In onCreate() method, initialize adapter for ViewPager and attach. Over here, you can see this full code of the main activity:
MainActivity.java
package devexchanges.info.imagegallerybyviewpager;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private ArrayList<Integer> images;
    private BitmapFactory.Options options;
    private ViewPager viewPager;
    private View btnNext, btnPrev;
    private FragmentStatePagerAdapter adapter;
    private LinearLayout thumbnailsContainer;
    private final static int[] resourceIDs = new int[]{R.mipmap.a, R.mipmap.b,
            R.mipmap.c, R.mipmap.d, R.mipmap.e, R.mipmap.f, R.mipmap.g};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        images = new ArrayList<>();

        //find view by id
        viewPager = (ViewPager) findViewById(R.id.view_pager);
        thumbnailsContainer = (LinearLayout) findViewById(R.id.container);
        btnNext = findViewById(R.id.next);
        btnPrev = findViewById(R.id.prev);

        btnPrev.setOnClickListener(onClickListener(0));
        btnNext.setOnClickListener(onClickListener(1));

        setImagesData();

        // init viewpager adapter and attach
        adapter = new ViewPagerAdapter(getSupportFragmentManager(), images);
        viewPager.setAdapter(adapter);

        inflateThumbnails();
    }

    private View.OnClickListener onClickListener(final int i) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (i > 0) {
                    //next page
                    if (viewPager.getCurrentItem() < viewPager.getAdapter().getCount() - 1) {
                        viewPager.setCurrentItem(viewPager.getCurrentItem() + 1);
                    }
                } else {
                    //previous page
                    if (viewPager.getCurrentItem() > 0) {
                        viewPager.setCurrentItem(viewPager.getCurrentItem() - 1);
                    }
                }
            }
        };
    }

    private void setImagesData() {
        for (int i = 0; i < resourceIDs.length; i++) {
            images.add(resourceIDs[i]);
        }
    }

    private void inflateThumbnails() {
        for (int i = 0; i < images.size(); i++) {
            View imageLayout = getLayoutInflater().inflate(R.layout.item_image, null);
            ImageView imageView = (ImageView) imageLayout.findViewById(R.id.img_thumb);
            imageView.setOnClickListener(onChagePageClickListener(i));
            options = new BitmapFactory.Options();
            options.inSampleSize = 3;
            options.inDither = false;
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(), images.get(i), options );
            imageView.setImageBitmap(bitmap);
            //set to image view
            imageView.setImageBitmap(bitmap);
            //add imageview
            thumbnailsContainer.addView(imageLayout);
        }
    }

    private View.OnClickListener onChagePageClickListener(final int i) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewPager.setCurrentItem(i);
            }
        };
    }
}

Create each Page/Fragment

    Every ViewPager's Fragment only contains an ImageView to display our full image. In this, also avoid OutOfMemory error when decode drawable to Bitmap, we perform like above activity. Moreover, recycle Bitmap when Fragment was destroyed. Full source code for each page (Fragment):
PageFragment.java
package devexchanges.info.imagegallerybyviewpager;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

public class PageFragment extends Fragment {

    private int imageResource;
    private Bitmap bitmap;

    public static PageFragment getInstance(int resourceID) {
        PageFragment f = new PageFragment();
        Bundle args = new Bundle();
        args.putInt("image_source", resourceID);
        f.setArguments(args);
        return f;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        imageResource = getArguments().getInt("image_source");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_page, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        ImageView imageView = (ImageView) view.findViewById(R.id.image);
        
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inSampleSize = 4;
        o.inDither = false;
        bitmap = BitmapFactory.decodeResource(getResources(), imageResource, o);
        imageView.setImageBitmap(bitmap);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        bitmap.recycle();
        bitmap = null;
    }
}

Some necessary files

    Customizing a ViewPager adapter based on FragmentStatePagerAdapter, we should use it instead of FragmentPagerAdapter to void persisting much data to memory. Adapter code so simple:
ViewPagerAdapter.java
package devexchanges.info.imagegallerybyviewpager;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;

import java.util.List;

public class ViewPagerAdapter extends FragmentStatePagerAdapter {

    private List<Integer> images;

    public ViewPagerAdapter(FragmentManager fm, List<Integer> imagesList) {
        super(fm);
        this.images = imagesList;
    }

    @Override
    public Fragment getItem(int position) {
        return PageFragment.getInstance(images.get(position));
    }

    @Override
    public int getCount() {
        return images.size();
    }
}
    Layout for each image thumbnail on activity:
item_image.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
 
    <ImageView
        android:id="@+id/img_thumb"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_marginTop="5dp"
        android:contentDescription="@string/app_name"
        android:scaleType="centerCrop" />
 
</LinearLayout>

Conclusions

    Through this post, I hope you can find out once more ViewPager application: making Photo gallery. For further details in my code, see full project on @Github.

Share


Previous post
« Prev Post
Next post
Next Post »