Android GridView Pagination - Next/Previous

android pagination

Paging data is an important operation in software applications. It's importance is magnified even more with mobile devices. These devices normally have a smaller screen area. Thus users can only see a small amount of data per given viewport.

So we really need a way of paging data so as to give users the opportunity to view and work with even more data. There are various types of pagination that can be applied to mobile apps:

  • Next/Previous Pagination
  • Numbered Pagination.
  • Infinite/Endless Pagination.

In this class I use the Next/Previous pagination. We'll have two buttons: next and previous. The user clicks the next button to navigate to the next page. And previous button to navigate to a previous page.

If we reach the last page the next button gets disabled automatically. Likewise if we are in the first page the previous button gets disabled.

Componets we use

Here are some of the main components we use in our simple app:

1. GridView

GridView are columned lists. You use them to display data in grids. You can define the number of columns in the xml markup.

Like all views gridviews are declared first in the xml markup and then referenced from the java code.

GridView itself is a public class defined in android.widget package.

GridView derives from android.widget.AbsListView.

There are three ways of using a gridview:

  1. Define its xml markup
  2. Reference the gridview in java code.
  3. Set an adapter to it.

2. Buttons

Buttons are components we are all familiar with. Since the inception of the moder computer with graphical user interfaces, buttons are some of the most used UI widgets in any program.

The magic of a button relies on its ability to listen to click events.

Our example will employ two buttons : next and previous. These, we use for page navigation.

Buttons do derive from android.widget.TextView.

3. CardViews

To render our items in the GridView in a modern way, we'll use CardViews. CardViews are framelayouts that have rounded corners and shadows. We can also set their elevation properties as well as other properties.

So our GridView will comprise of multiple cardviews. Each card will hold an image and text. Each cardview will represent a single Galaxy object for us.

CardView are defined inside the android.support.v7.widget package. They derive from android.widget.FrameLayout.

We'll define our cardviews inside an xml layout.

Page Structure

Here's the project structure for our app.
GridView Pagination Project Structure

1. Let's create our project.

Go to File -- New Project

Specify project name and sdk version.

Choose the empty activity in your android studio templates.

GridView Pagination Create Project

2. Let's modify our build.gradle file.

We are modifying our build.gradle(app) file. We want to add some dependencies including a cardview dependency.

GridView Pagination

Add the following dependencies inside you dependencies closure in your build.gradle(Module:app) file. Note that you may change the versions according to SDK version.

    compile 'com.android.support:appcompat-v7:26.+'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha7'
    compile 'com.android.support:cardview-v7:26.+'

3. Let's prepare our resources.

Well resources in two forms:

  1. Drawables - add some images in your drawable folder. remember our gridview will display images and text.
  2. Layouts - We'll have two layoust : activity_main.xml and model.xml.

The activity_main.xml is the layout definition of our activity. It will hold our gridview and two buttons below it in linearlayout.

Here's how we defined it:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout 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"
        tools:context="com.tutorials.hp.gridviewpaging.MainActivity">

        <LinearLayout
            android:layout_width="368dp"
            android:layout_height="495dp"
            android:orientation="vertical"
            tools:layout_editor_absoluteY="8dp"
            tools:layout_editor_absoluteX="8dp">

            <GridView
                android:id="@+id/gridView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:numColumns="auto_fit" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:layout_weight="2"
                android:orientation="horizontal">

                <Button
                    android:id="@+id/prevBtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="@color/colorAccent"
                    android:padding="10dp"
                    android:text="Previous" />

                <Button
                    android:id="@+id/nextBtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="#009968"
                    android:padding="10dp"
                    android:text="Next" />

            </LinearLayout>
        </LinearLayout>

    </android.support.constraint.ConstraintLayout>

Then the model.xml is a custom grid definition for our gridview. We wanted our gridview to render images and text. Thus we need to show it how using the XML markup language.

And in this case our gridview grids will be based on CardViews.

Here's its full definition:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal" android:layout_width="match_parent"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_margin="5dp"
        card_view:cardCornerRadius="15dp"
        card_view:cardElevation="10dp"
        android:layout_height="200dp">

        <LinearLayout
             android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:text="Galaxy"
                android:id="@+id/nameTxt"
                android:padding="10dp"
                android:textColor="@color/abc_btn_colored_borderless_text_material"
                android:layout_alignParentTop="true"/>
            <LinearLayout
                android:orientation="horizontal"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
                <ImageView
                    android:id="@+id/galaxyImageview"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentLeft="true"
                    android:layout_alignParentTop="true"
                    android:layout_marginLeft="24dp"
                    android:src="@drawable/andromeda" />
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textAppearance="?android:attr/textAppearanceLarge"
                    android:text="Description"
                    android:id="@+id/descTxt"
                    android:padding="10dp"/>
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Category"
                    android:id="@+id/categoryTxt"
                    android:textStyle="italic"
                    android:layout_gravity="bottom"
                    android:padding="5dp"/>
            </LinearLayout>

        </LinearLayout>
    </android.support.v7.widget.CardView>

So those two are the layouts we work with.

4. Let's create our data object.

This is a POJO class. That is the custom business objects we are working with. Like in this case we are working with Galaxy objects. This app will display galaxies in a paginated gridview.

First create the class. It needs to be public as it will be used by other classes.

    public class Galaxy {}

Secondly we define its instance fields:

        private String name,description,category;
        private int image;

These are what make up the state of the Galaxy. It's data. So we can have several Galaxies with different names,descriptions and images. Hence different states.

Thirdly we create constructor and getter methods.
Here's our constructor. A constructor is a special method that gets called everyttime a class is instantiated.

        public Galaxy(String name, String description, int image,String category) {
            this.name = name;
            this.description = description;
            this.image = image;
            this.category=category;
        }

Here are the getter methods. These expose our data to other classes. They can get the name, description,category and image.

        public String getName() {
            return name;
        }
        public String getDescription() {
            return description;
        }
        public int getImage() {
            return image;
        }
        public String getCategory() {
            return category;
        }

So that's it.

5. Let's create our DataHolder class.

A class to hold for us the data. This class acts as some sort of database. We just hold data in memory. A basic class that just returns an arraylist of galaxies.

First create the class

    public class DataHolder {}

Secondly add the following imports on top of the class definition.

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

Thirdly we create a static method to return an arraylist of galaxies. Remember we need some data to page. One option may be to get or obtain the data from external sources such as:

  • Firebase
  • SQlite
  • MySQL
  • Realm
  • File System

Then page that data. Well here's what you can do. Obtain data from those sources then populate this arraylist. Then we will just page that data irrespective of the source. So you can easily use this tutorial to page data from firebase,mysql, sqlite or any other external database.

     public static List<Galaxy> getGalaxies()
        {
            ArrayList<Galaxy> galaxies=new ArrayList<>();

            Galaxy g=new Galaxy("Whirlpool",
                    "The Whirlpool Galaxy, also known as Messier 51a, M51a, and NGC 5194, is an interacting grand-design spiral Galaxy with a Seyfert 2 active galactic nucleus in the constellation Canes Venatici.",
                    R.drawable.whirlpool,"spiral");
            galaxies.add(g);

            g=new Galaxy("Ring Nebular",
                    "The Ring Nebula is a planetary nebula in the northern constellation of Lyra. Such objects are formed when a shell of ionized gas is expelled into the surrounding interstellar medium by a red giant star.",
                    R.drawable.ringnebular,"elliptical");
            galaxies.add(g);

            g=new Galaxy("IC 1011",
                    "C 1011 is a compact elliptical galaxy with apparent magnitude of 14.7, and with a redshift of z=0.02564 or 0.025703, yielding a distance of 100 to 120 Megaparsecs. Its light has taken 349.5 million years to travel to Earth.",
                    R.drawable.ic1011,"elliptical");
            galaxies.add(g);

            g=new Galaxy("Cartwheel",
                    "The Cartwheel Galaxy is a lenticular galaxy and ring galaxy about 500 million light-years away in the constellation Sculptor. It is an estimated 150,000 light-years diameter, and a mass of about 2.9–4.8 × 10⁹ solar masses; it rotates at 217 km/s.",
                    R.drawable.cartwheel,"irregular");
            galaxies.add(g);

            g=new Galaxy("Triangulumn",
                    "The Triangulum Galaxy is a spiral Galaxy approximately 3 million light-years from Earth in the constellation Triangulum",
                    R.drawable.triangulum,"spiral");
            galaxies.add(g);

            g=new Galaxy("Small Magellonic Cloud",
                    "The Small Magellanic Cloud, or Nubecula Minor, is a dwarf galaxy near the Milky Way. It is classified as a dwarf irregular galaxy.",
                    R.drawable.smallamgellonic,"irregular");
            galaxies.add(g);

            g=new Galaxy("Centaurus A",
                    " Centaurus A or NGC 5128 is a galaxy in the constellation of Centaurus. It was discovered in 1826 by Scottish astronomer James Dunlop from his home in Parramatta, in New South Wales, Australia.",
                    R.drawable.centaurusa,"elliptical");
            galaxies.add(g);

            g=new Galaxy("Ursa Minor",
                    "The Milky Way is the Galaxy that contains our Solar System." +
                            " The descriptive milky is derived from the appearance from Earth of the Galaxy – a band of light seen in the night sky formed from stars",
                    R.drawable.ursaminor,"irregular");
            galaxies.add(g);

            g=new Galaxy("Large Magellonic Cloud",
                    " The Large Magellanic Cloud is a satellite galaxy of the Milky Way. At a distance of 50 kiloparsecs, the LMC is the third-closest galaxy to the Milky Way, after the Sagittarius Dwarf Spheroidal and the.",
                    R.drawable.largemagellonic,"irregular");
            galaxies.add(g);

            g=new Galaxy("Milky Way",
                    "The Milky Way is the Galaxy that contains our Solar System." +
                            " The descriptive milky is derived from the appearance from Earth of the Galaxy – a band of light seen in the night sky formed from stars",
                    R.drawable.milkyway,"spiral");
            galaxies.add(g);

            g=new Galaxy("Andromeda",
                    "The Andromeda Galaxy, also known as Messier 31, M31, or NGC 224, is a spiral Galaxy approximately 780 kiloparsecs from Earth. It is the nearest major Galaxy to the Milky Way and was often referred to as the Great Andromeda Nebula in older texts.",
                    R.drawable.andromeda,"irregular");
            galaxies.add(g);

            g=new Galaxy("Messier 81",
                    "Messier 81 is a spiral Galaxy about 12 million light-years away in the constellation Ursa Major. Due to its proximity to Earth, large size and active galactic nucleus, Messier 81 has been studied extensively by professional astronomers.",
                    R.drawable.messier81,"elliptical");
            galaxies.add(g);

            g=new Galaxy("Own Nebular",
                    " The Owl Nebula is a planetary nebula located approximately 2,030 light years away in the constellation Ursa Major. It was discovered by French astronomer Pierre Méchain on February 16, 1781",
                    R.drawable.ownnebular,"elliptical");
            galaxies.add(g);

            g=new Galaxy("Messier 87",
                    "Messier 87 is a supergiant elliptical galaxy in the constellation Virgo. One of the most massive galaxies in the local universe, it is notable for its large population of globular clusters—M87 contains",
                    R.drawable.messier87,"elliptical");
            galaxies.add(g);

            g=new Galaxy("Cosmos Redshift",
                    "Cosmos Redshift 7 is a high-redshift Lyman-alpha emitter Galaxy, in the constellation Sextans, about 12.9 billion light travel distance years from Earth, reported to contain the first stars —formed ",
                    R.drawable.cosmosredshift,"irregular");
            galaxies.add(g);

            g=new Galaxy("StarBust",
                    "A starburst Galaxy is a Galaxy undergoing an exceptionally high rate of star formation, as compared to the long-term average rate of star formation in the Galaxy or the star formation rate observed in most other galaxies. ",
                    R.drawable.starbust,"irregular");
            galaxies.add(g);

            g=new Galaxy("Sombrero",
                    "Sombrero Galaxy is an unbarred spiral galaxy in the constellation Virgo located 31 million light-years from Earth. The galaxy has a diameter of approximately 50,000 light-years, 30% the size of the Milky Way.",
                    R.drawable.sombrero,"spiral");
            galaxies.add(g);

            g=new Galaxy("Pinwheel",
                    "The Pinwheel Galaxy is a face-on spiral galaxy distanced 21 million light-years away from earth in the constellation Ursa Major. ",
                    R.drawable.pinwheel,"spiral");
            galaxies.add(g);

            g=new Galaxy("Canis Majos Overdensity",
                    "The Canis Major Dwarf Galaxy or Canis Major Overdensity is a disputed dwarf irregular galaxy in the Local Group, located in the same part of the sky as the constellation Canis Major. ",
                    R.drawable.canismajoroverdensity,"irregular");
            galaxies.add(g);

            g=new Galaxy("Virgo Stella Stream",
                    " Group, located in the same part of the sky as the constellation Canis Major. ",
                    R.drawable.virgostellarstream,"spiral");
            galaxies.add(g);

            return galaxies;
        }

Ok. Let's proceed.

6. Let's create our custom adapter.

Adapter classes normally adapt data to adapterview. In this case our adapterview is a gridview. And our adapter is a class we are creating calledCustomAdapter.

To make it an adapter we derive from android.widget.BaseAdapter.

So first, create the class:

    public class CustomAdapter{}

Secondly add these imports above the class:

    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;
    import java.util.ArrayList;

Thirdly make the class derive from BaseAdapter

    public class CustomAdapter extends BaseAdapter {}

The moment we do the above it will ask us to override a couple of methods. Well before that let's add some instance fields:

        Context c;
        ArrayList<Galaxy> galaxies;

Then we define our constructor to take an arraylist as well as context object:

        public CustomAdapter(Context c, ArrayList<Galaxy> galaxies) {
            this.c = c;
            this.galaxies = galaxies;
        }

Then override the methods as follows:

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

        @Override
        public Object getItem(int i) {
            return galaxies.get(i);
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public View getView(int i, View view, ViewGroup viewGroup) {
            if(view==null)
            {
                view= LayoutInflater.from(c).inflate(R.layout.model,viewGroup,false);
            }
            //reference widgets
            TextView nameTxt=view.findViewById(R.id.nameTxt);
            TextView descTxt=view.findViewById(R.id.descTxt);
            TextView categoryTxt=view.findViewById(R.id.categoryTxt);
            ImageView galaxyImg=view.findViewById(R.id.galaxyImageview);

            //bind data to widgets
            Galaxy g= (Galaxy) getItem(i);
            nameTxt.setText(g.getName());
            descTxt.setText(g.getDescription());
            categoryTxt.setText(g.getCategory());
            galaxyImg.setImageResource(g.getImage());

            return view;
        }

Clearly you can guess the actions of the methods from just their names:

  1. getCount() - returns the total number of items to be rendered in the gridview per viewport.
  2. getItem() - returns a single item object.
  3. getItemId() - returns the item's id.
  4. getView() - returns an inflated view. The view gets inflated from the xml model specification.

So yah that's it.

7. Let's create our Paginator class.

The class responsible for paging/paginating data. A very simple class that will work with the others to give us paginated data.

The workings of this class are rather simple.

We first create the class then add one import:

    import java.util.ArrayList;
    public class Paginator {}

Secondly Define two constants inside the class:

     public static final int TOTAL_NUM_ITEMS = DataHolder.getGalaxies().size();
     public static final int ITEMS_PER_PAGE = 6;

These will specify us explicitly the overall total number of items to be worked with and items per page.

Thirdly, we use the above constants to calculate the total number of our pages:

        public int getTotalPages() {
            int remainingItems=TOTAL_NUM_ITEMS % ITEMS_PER_PAGE;
            if(remainingItems>0)
            {
                return TOTAL_NUM_ITEMS / ITEMS_PER_PAGE;
            }
            return (TOTAL_NUM_ITEMS / ITEMS_PER_PAGE)-1;

        }

Then lastly in this paginator class, we create a method to return us the items we want to show in the current page.

Well how do we know whether we are in page 1 or page 2? Well an integer representing the current page gets passed us from the mainactivity class.

        public ArrayList<Galaxy> getCurrentGalaxys(int currentPage) {
            int startItem = currentPage * ITEMS_PER_PAGE;
            int lastItem = startItem + ITEMS_PER_PAGE;

            ArrayList<Galaxy> currentGalaxys = new ArrayList<>();

            //LOOP THRU LIST OF GALAXIES AND FILL CURRENTGALAXIES LIST
            try {
                for (int i = 0; i < DataHolder.getGalaxies().size(); i++) {

                    //ADD CURRENT PAGE'S DATA
                    if (i >= startItem && i < lastItem) {
                        currentGalaxys.add(DataHolder.getGalaxies().get(i));
                    }
                }
                return currentGalaxys;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

Then our last class.

8. Let's move to the MainActivity.java

Our MainActivity. Activities represent a User interface screen and the data to work with on that screen.

Activities are a fundamental component in android. Other components comprise: BroadcastReceiver, Service and ContentProvider.

You become an activity by deriving from android.app.Activity. Or it's support variations like AppCompatActivity.

So let's create our activity and add imports:

    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.GridView;

    public class MainActivity extends AppCompatActivity {}

Then we add these instance fields:

        GridView gridView;
        Button nextBtn, prevBtn;
        Paginator p = new Paginator();
        private int totalPages =p.getTotalPages();
        private int currentPage = 0;
        CustomAdapter adapter;

Then we reate a method to initialize our two widgets: gridview and buttons.

       private void initializeViews()
        {
            gridView= (GridView) findViewById(R.id.gridView);
            nextBtn = (Button) findViewById(R.id.nextBtn);
            prevBtn = (Button) findViewById(R.id.prevBtn);
        }

Then a method to bind data to our gridview via our customadapter.

        private void bindData(int page) {
            adapter=new CustomAdapter(this,p.getCurrentGalaxys(page));
            gridView.setAdapter(adapter);
        }

With pagination we need to maintain button states. When a button comes enabled or not. So we create a method called toggleButtons() to do so.

``language

        private void toggleButtons() {
            //SINGLE PAGE DATA
            if (totalPages <= 1) {
                nextBtn.setEnabled(false);
                prevBtn.setEnabled(false);
            }
            //LAST PAGE
            else if (currentPage == totalPages) {
                nextBtn.setEnabled(false);
                prevBtn.setEnabled(true);
            }
            //FIRST PAGE
            else if (currentPage == 0) {
                prevBtn.setEnabled(false);
                nextBtn.setEnabled(true);
            }
            //SOMEWHERE IN BETWEEN
            else if (currentPage >= 1 && currentPage <= totalPages) {
                nextBtn.setEnabled(true);
                prevBtn.setEnabled(true);
            }
        }

Lastly we come and override our onCreate() method.


        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            this.initializeViews();
            this.bindData(currentPage);
            prevBtn.setEnabled(false);

            //NAVIGATE
            nextBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    currentPage += 1;
                    bindData(currentPage);
                    toggleButtons();
                }
            });
            prevBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    currentPage -= 1;
                    bindData(currentPage);
                    toggleButtons();
                }
            });
        }

So that's it. Pagination in gridview android.

How do You Feel after reading this?

According to scientists, we humans have 8 primary innate emotions: joy, acceptance, fear, surprise, sadness, disgust, anger, and anticipation. Feel free to tell us how you feel about this article using these emotes or via the comment section. This feedback helps us gauge our progress.

Help me Grow.

I set myself some growth ambitions I desire to achieve by this year's end regarding this website and my youtube channel. Am halfway. Help me reach them by:




Recommendations


What do You Think

Dear readers drop us your comments below. We are building a community of students and learners. Start by dropping us your suggestions below. What tutorials do you want us to do for example? Where can we improve? What are some awesome resources out there? Do you have any code you want to share with us?
By the way that example or snippet you have lying in your computer can really help beginner programmers. We can share it here with other students.

Previous Post Next Post