Android Custom ListView Pagination Next/Previous

android pagination

In this example we look at how to page/paginate data in a listview in android. Paging/Paginating data is important nowadays in most applications because the mobile screen is fairly small and can only show about 5-10 items per viewport.

Let's start:
Here's our full project structure:

Project Structure


Step 1 - Create Android Project

  • In your android studio go to File -- New -- New Project.
  • Type Project Name.
  • Choose minimum sdk.
  • From templates choose empty activity.

Empty Activity Template


Step 2 - Add CardView Dependency.

The second step is to add CardView as a dependency in your build.gradle(Module:app). This is beacuse we will use CardViews as Viewitems in our ListView.

  1. Under your Gradle scripts in your project choose build.gradle(Module:app).
    Choose build.gradle module app

  2. Add our cardview as a dependency under the dependencies closure:
    compile 'com.android.support:cardview-v7:26.+'

Step 3. Prepare Drawable and Layout Resources.

We now need to add the images to use inside our ListView and also define the layouts for our activity as well as custom rows.

  1. Under the drawable add images to be rendered alongside text in the custom listview.
  2. We also need two layouts: activity_main.xml and model.xml.

  3. The activity_main.xml may already have been generated to you by android studio.So let's just add a ListView with two buttons below it. The buttons are next/previous buttons:

Here's the component tree:

Layout Component tree

4. Add the following code in activity_main.xml:

    <ListView
                android:id="@+id/listview"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                />
             <LinearLayout
                android:layout_weight="2"
                android:orientation="horizontal"
                android:layout_width="match_parent"
                android:layout_height="100dp">
                <Button
                    android:id="@+id/prevBtn"
                    android:text="Previous"
                    android:background="@color/colorAccent"
                    android:padding="10dp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
                <Button
                    android:id="@+id/nextBtn"
                    android:text="Next"
                    android:background="#009968"
                    android:padding="10dp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />

            </LinearLayout>

1. We also need to prepare our custom row model.

Here's the model layout design in android studio pallete:

Model.xml design

And here's its component tree:
Model.xml component tree

Add the following code in you model.xml. If you haven't created it, go ahead and create it.

    <?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:textAppearance="?android:attr/textAppearanceLarge"
                    android:text="Category"
                    android:id="@+id/categoryTxt"
                    android:textStyle="italic"
                    android:textColor="@color/colorPrimary"
                    android:layout_gravity="bottom"
                    android:padding="10dp"/>
            </LinearLayout>

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

Step 4 - Create a POJO class.

POJO stands for Plain Old Java Object. It's our data object class. The class that will define our business entity and it's properties. In this case that business entity is a galaxy. In that our ListView will hold a list of Galaxy objects.
Our Galaxy will have the following properties:

  • Name - String.
  • Description - String.
  • Category - String
  • Image - int

Each galaxy object will be represented in a cardview.
Here's how the class will appear:

    public class Galaxy {

        private String name,description,category;
        private int image;
        /*
        - Our constructor.
         */
        public Galaxy(String name, String description, int image,String category) {
            this.name = name;
            this.description = description;
            this.image = image;
            this.category=category;
        }
        /*
        - Our getters and setters.
         */
        public String getName() {
            return name;
        }
        public String getDescription() {
            return description;
        }
        public int getImage() {
            return image;
        }
        public String getCategory() {
            return category;
        }
    }

Step 5 - Create a DataHolder class.

We now a need a class that will act for us as the data source of our project. This class can then be easily replaced with a database or some other data osurce. In this case we use an arraylist as the data source.
The advantage of this is that you can easily obtain for example data from say database or from the cloud, populate this arraylist and our application will page it. However, it will be a client side pagination as opposed to a server side pagination.

1. Create a class :

    public class DataHolder {
    }

2. Add imports:

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

Our data source is going to be an arraylist.

3. Populate our data source with data.

We create a static method that where we populate our arraylist with data and return it.
First we instantiate the individual Galaxy objects and pass in the name, description, image and category.

     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;
        }

Step 6 - Create and Adapter class.

So far we have defined our data object as well as our data source. So the data part is set to go. However, that's complex data we are working with in that it comprises multiple text and images.
There is no way our ListView will be able to just render that complex data out of the blue. We need to help it. That's where the adapter class comes in. Adapter classes can adapt data to a adapterviews like listview. In this case we use the BaseAdapter implementation.

1. Create a Custom Adapter class.

    public class CustomAdapter{

    }

2. Import the following classes/packages at the top of your CustomAdapter 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;

3. Make our CustomAdapter class derive from BaseAdapter class.

    public class CustomAdapter extends BaseAdapter {
    }

BaseAdapter class is an abstract class that resides in the android.widget package. It itself derives from java.lang.Object class and implements ListAdapter and SpinnerAdapter interfaces.
Deriving from it is what makes our class an adapter. However, given that it's abstract, we have to implement a couple of methods, so proceed and add the following methods, constructor and fields as below:

        Context c;
        ArrayList<Galaxy> galaxies;

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

        @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;
        }

Step 7 - Create a Paginator class.

So by this time our code knows how to create a data source and bind the resultant complex data to a custom listview. Thus it can render our data as a long list of items.


However, we desire to page.paginate that data. So that say we can only have like 5 or 6 items per page. Then the user can naviage using the next/previous buttons.
So then we create a Paginator class that will be responsible for this pagination/paging of data. This class will work hand in hand with the mainactivity to perform paging of any arraylist of data.
The MainActivity will be responsible for maintaining the state of the current page, while the Paginator class will be responsible for pagination.

1. First create a Paginator class.

    public class Paginator {
    }

2. Add the following import:

    import java.util.ArrayList;

This is because we will be returning an arraylist of the current page items.

3. Define Constants that will help us in paging/pagination of data.

For example we need to know the total number of items and items per page. Add the following code as fields in our class.

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

4. Calculate the total number of pages.

We already know the total number of items in our list. We also know the items per page. Now we can use that to calculate the total number of pages we will have.

      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;

        }

5. Return current page's data dynamically.

So now we can obtain the current page's data and return it to the adapter so that it can be rendered. Hwoever, we need to know the current page. So this current page variable will be passed from the MainActivity and then we can use it to obtain that page's data.

        /*
        CURRENT PAGE's GalaxieS LIST
         */
        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;
            }
        }

Step 7 - Our MainActivity.

So far so good. We know how to define our data source. We know how to bind custom data from this data source to a custom listview. We even know to actually page data and return the current page's data.
However, the truth is that we need to have a way of maintaing the state of the buttons and the current page counter. Therefore this is where our MainActivity comes.
Thus not only will it maintain state for us, but it is also our launcher activity and where we actually bind our data to ListView. So this class responsibilities include:

  • Launch our application.
  • Maintain the current page's state in a counter variable.
  • Maintain next/previous button's state according to current page.
  • Reference our ListView and buttons from the activity_main.xml.
  • Instantiate our CustomAdapter class and set it to our ListView.

1. Create the activity - if it's not been generated yet.

Most probably the mainactivity has been generated by android studio. Howver, if not yet, then create a class and call it MainActivity.

    public class MainActivity{

    }

2. Add imports:

Add the follwoing imports at the top of your class.

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

3. Derive from AppCompatActivity

Extend the AppCompatActivity class to turn our simple class into an actual activity.

    public class MainActivity extends AppCompatActivity {
    }

AppCompatActivity resides in the android.support.v7.app package. It itself derives from android.support.v4.app.FragmentActivity.
It is a base class fr activities that use support library action bar features.

4. Add class fields.

Let's add the following class fields.

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

5. Initialize our views.

Let's define a method that initializes all our views(ListView and buttons).

       /*
        - Initialize ListView.
         */
        private void initializeViews()
        {
            listView= (ListView) findViewById(R.id.listview);
            nextBtn = (Button) findViewById(R.id.nextBtn);
            prevBtn = (Button) findViewById(R.id.prevBtn);
        }

6. Set adapter to the ListView.

       /*
        - Bind data to ListView.
         */
        private void bindData(int page) {
            adapter=new CustomAdapter(this,p.getCurrentGalaxys(page));
            listView.setAdapter(adapter);
        }

7.Toggle Next/Previous Buttons' states.

Enable or disable next/previous buttons according to current page:

       /*
        - Toggle button states
         */
        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);
            }

        }

That's it.

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


Previous Post Next Post