Android SQLite Pagination - ListView - Next/Previous ServerSide


Android SQLite Pagination - ListView - Next/Previous ServerSide tutorials.

In this class we see how to perform Next/Previous type of pagination with SQLite data with ListView as our adapterview.The user can add data dynamically with an input dialog and the padination will still work.If you are in the first page,the previous button is disabled while if you are in the last page the next button is enabled.

Project Demo

Intro

  • We page/paginate data with next/previous type of pagination.
  • The data we are paging/paginating is SQLite data.
  • We are performing our pagination on the server side,that is at the database level.
  • Our ListView comprises CardViews with textviews.
  • Each page has five CardView with data from SQLite.
  • We are using RushORM as our SQLite database Object Relational Mapper.
  • We use ListViewas our component.
  • We've used Android Studio as our IDE.
  • The code is well commented for easier understanding.

Common Questions we answer

With this simple example we explore the following :

  • How to page SQLite data in android.
  • How to perform SQLite server side pagination.
  • How to  page data in ListView.
  • How to perform CRUD with SQLite database.
  • How to insert,select data to and from SQLite database and ListView in android.
  • Android ListView with CardViews.
  • Display SQLite data in CardViews.
  • How to use Android RushORM.
  • Using Android with SQLite database and ListView.

Tools Used

  • IDE : Android Studio
  • OS : Windows 8.1
  • PLATFORM : Android
  • LANGUAGE : Java
  • TOPIC : SQLite,Pagination,ListView,CardView,RushORM

Source Code

Build.Gradle(Project)

  • This is our first build.gradle,at the project level.
  • Here we specify the maven repository from which we shall fetch our RushORM

 

allprojects {
    repositories {
        maven {
            url "http://maven.rushorm.com"
        }
        jcenter()
    }
}

     

Build.Gradle(App)

  • Here's our app level build.gradle.
  • We use RushORM for our SQLite database, hence we need to specify the dependency right here in the build.gradle app level.
  • We also user CardViews so lets add CardView support library dependency.

 

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:25.2.0'
    compile 'com.android.support:design:25.2.0'
    compile 'com.android.support:cardview-v7:25.2.0'
    compile 'co.uk.rushorm:rushandroid:1.2.0'
}

   

Android Manifest

  • To use RusORM we need to specify some meta data in our AndroidMainfets.xml file.
  • These meta data shall assist RushORM in generating our database and table from our Model classes.
  • You'll need for instance to provide the path of your model class,like for me its : com.tutorials.hp.sqlitelistviewpagination.mDB that host my Spacecraft model class.This assists RushORM generate my table appropriately.

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tutorials.hp.sqlitelistviewpagination">

    <application
        android:name=".MainApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!--META DATAS FOR OUR DATABASE-->
        <!--THIS FIRST LINE IS COMPULSORY.ADD YOUR DATABASE PACKAGE-->
        <meta-data android:name="Rush_classes_package" android:value="com.tutorials.hp.sqlitelistviewpagination.mDB" />
        <!-- Updating this will cause a database upgrade -->
        <meta-data android:name="Rush_db_version" android:value="1" />

        <!-- Database name -->
        <meta-data android:name="Rush_db_name" android:value="SpacecraftsDB.db" />

        <!-- Setting this to true will cause a migration to happen every launch,
        this is very handy during development although could cause data loss -->
        <meta-data android:name="Rush_debug" android:value="false" />

        <!-- Setting this to true mean that tables will only be created of classes that
        extend RushObject and are annotated with @RushTableAnnotation -->
        <meta-data android:name="Rush_requires_table_annotation" android:value="false" />

        <!-- Turning on logging can be done by settings this value to true -->
        <meta-data android:name="Rush_log" android:value="false" />

    </application>

</manifest>

   

MainApplication Class

  • This class derives from Application class.
  • Its a global class and we initialize our RushORM here.

 

package com.tutorials.hp.sqlitelistviewpagination;

import android.app.Application;

import co.uk.rushorm.android.AndroidInitializeConfig;
import co.uk.rushorm.core.RushCore;

public class MainApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        AndroidInitializeConfig config=new AndroidInitializeConfig(getApplicationContext());
        RushCore.initialize(config);
    }
}

     

MainActivity Class

  • Our MainActivity,launcher activity.
  • First we reference views here : ListView,and next and previous buttons.
  • We have a variable counter called currentPage that shall be tracking the current page as the user navigates by clicking either next or previous buttons.
  • We have a method toggleButtons that toggle the state of the next and previous buttons appropriately.
  • We instantiate and call Paginator class that simply returns us the current page that we bind to our ListView.
  • If you click the floating action button,we display the input dialog where the user enters new data.
  • Calling save method save data to SQLite database.

 

package com.tutorials.hp.sqlitelistviewpagination;

import android.app.Dialog;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;

import com.tutorials.hp.sqlitelistviewpagination.mAdapterView.CustomAdapter;
import com.tutorials.hp.sqlitelistviewpagination.mAdapterView.Paginator;
import com.tutorials.hp.sqlitelistviewpagination.mDB.Spacecraft;

import co.uk.rushorm.core.RushCallback;

public class MainActivity extends AppCompatActivity {

    ListView lv;
    EditText nameEditText,propellantEditTxt;
    Button saveBtn,retrieveBtn,nextBtn,prevBtn;

    private int currentPage=0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        //INITIALIZE VIEWS
        lv= (ListView) findViewById(R.id.lv);
        nextBtn= (Button) findViewById(R.id.nextBn);
        prevBtn= (Button) findViewById(R.id.prevBtn);
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

        prevBtn.setEnabled(false);

        //FIRST PAGE WHEN LOADED
        if(new Paginator().getCurrentSpacecrafts(currentPage).size()>0)
        {
            lv.setAdapter(new CustomAdapter(MainActivity.this,new Paginator().getCurrentSpacecrafts(currentPage)));

        }else
        {
            nextBtn.setEnabled(false);
        }

        //DISPLAY DIALOG
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                displayDialog();
            }
        });

        //NEXT BUTTON CLICKED
        nextBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                currentPage+=1;
                lv.setAdapter(new CustomAdapter(MainActivity.this,new Paginator().getCurrentSpacecrafts(currentPage)));
                toggleButtons();
            }
        });

        //PREVIOUS BUTTON CLICKED
        prevBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                currentPage-=1;
                lv.setAdapter(new CustomAdapter(MainActivity.this,new Paginator().getCurrentSpacecrafts(currentPage)));
                toggleButtons();
            }
        });

    }

    /*
    DISPLAY INPUT DIALOG
     */
    private void displayDialog()
    {
        final Dialog d=new Dialog(this);
        d.setTitle("SQLITE DATA");
        d.setContentView(R.layout.dialog_layout);

        nameEditText= (EditText) d.findViewById(R.id.nameEditTxt);
        propellantEditTxt= (EditText) d.findViewById(R.id.propellantEditTxt);

        saveBtn= (Button) d.findViewById(R.id.saveBtn);
        retrieveBtn= (Button) d.findViewById(R.id.retrieveBtn);

        saveBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Spacecraft s=new Spacecraft();
                save(s);
                nameEditText.setText("");
                propellantEditTxt.setText("");
                toggleButtons();

            }
        });
        retrieveBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                lv.setAdapter(new CustomAdapter(MainActivity.this,new Paginator().getCurrentSpacecrafts(currentPage)));
                toggleButtons();

            }
        });
        d.show();

    }

    /*
    INSERT DATA
     */
    private void save(Spacecraft s)
    {
        s.setName(nameEditText.getText().toString());
        s.setPropellant(propellantEditTxt.getText().toString());
        s.save();
        lv.setAdapter(new CustomAdapter(MainActivity.this,new Paginator().getCurrentSpacecrafts(currentPage)));
    }

    /*
    TOGGLE PREVIOUS AND NEXT BUTTONS
     */
    private void toggleButtons()
    {
        if(currentPage==new Paginator().getTotalPages())
        {
            nextBtn.setEnabled(false);
            prevBtn.setEnabled(true);
        }else
        if(currentPage==0)
        {
            prevBtn.setEnabled(false);
            nextBtn.setEnabled(true);
        }else
        if(currentPage>=1 && currentPage < new Paginator().getTotalPages())
        {
            nextBtn.setEnabled(true);
            prevBtn.setEnabled(true);
        }

    }

}

   

Paginator Class

  • Paginator class responsible for pagination of our SQlite data.
  • The class has a method getCurrentSpacecrafts() that simply returns the current paged data to the caller.
  • It updates the total items count in the database via the constructor.

 

package com.tutorials.hp.sqlitelistviewpagination.mAdapterView;

import com.tutorials.hp.sqlitelistviewpagination.mDB.Spacecraft;

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

import co.uk.rushorm.core.RushSearch;

public class Paginator {

    private int TOTAL_NUM_ITEMS=0;
    private int ITEMS_PER_PAGE=0;

    public Paginator() {
        try
        {
            TOTAL_NUM_ITEMS= (int) new RushSearch().count(Spacecraft.class);
            ITEMS_PER_PAGE=5;
        }catch (Exception e)
        {

        }

    }

    /*
    TOTAL NUMBER OF PAGES
     */
    public int getTotalPages()
    {
        return TOTAL_NUM_ITEMS/ITEMS_PER_PAGE;
    }

    /*
    CURRENT PAGE SPACECRAFTS LIST
     */
    public List<Spacecraft> getCurrentSpacecrafts(int currentPage)
    {
        int startItem=currentPage*ITEMS_PER_PAGE;

        List<Spacecraft> currentSpacecrafts=new ArrayList<>();
        try
        {
            currentSpacecrafts=new RushSearch().limit(ITEMS_PER_PAGE).offset(startItem).find(Spacecraft.class);

        }catch (Exception e)
        {
            e.printStackTrace();
        }

        return currentSpacecrafts;
    }

}

   

Spacecraft Model class

  • Is our model class.
  • This class shall be mapped to our table by RushORM.
  • The class derives from RushORM.

 

package com.tutorials.hp.sqlitelistviewpagination.mDB;

import co.uk.rushorm.core.RushObject;

public class Spacecraft extends RushObject {

    private String name,propellant;

    //AN EMPTY CONSTRUCTOR
    public Spacecraft() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPropellant() {
        return propellant;
    }

    public void setPropellant(String propellant) {
        this.propellant = propellant;
    }
}

     

CustomAdapter class

  • Remember we are using a Custom ListView with CardViews hence we need an adapter class to adapt our data to custom itemviews.
  • The class derives from BaseAdapter.

 

package com.tutorials.hp.sqlitelistviewpagination.mAdapterView;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;

import com.tutorials.hp.sqlitelistviewpagination.R;
import com.tutorials.hp.sqlitelistviewpagination.mDB.Spacecraft;

import java.util.List;

public class CustomAdapter extends BaseAdapter {

    Context c;
    List<Spacecraft> spacecrafts;
    LayoutInflater inflater;

    /*
    GET CONTEXT AS WELL AS LIST OF SPACECRAFTS
     */
    public CustomAdapter(Context c, List<Spacecraft> spacecrafts) {
        this.c = c;
        this.spacecrafts = spacecrafts;
    }

    /*
    TOTAL NUM OF SPACECRAFTS TO BIND
     */
    @Override
    public int getCount() {
        return spacecrafts.size();
    }

    /*
    GET SPACECRAFT
     */
    @Override
    public Object getItem(int position) {
        return spacecrafts.get(position);
    }

    /*
    SPACECRAFT ID
     */
    @Override
    public long getItemId(int position) {
        return position;
    }

    /*
    RETURN VIEW ITEM TO BE RENDERED IN LISTVIEW
     */
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        if(inflater==null)
        {
            inflater= (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }
        if(convertView==null)
        {
            convertView=inflater.inflate(R.layout.model,parent,false);
        }

        TextView nameTxt= (TextView) convertView.findViewById(R.id.nameTxt);
        TextView propTxt= (TextView) convertView.findViewById(R.id.propellantTxt);

        //BIND DATA

        nameTxt.setText(spacecrafts.get(position).getName());
        propTxt.setText(spacecrafts.get(position).getPropellant());

        //SET CLICK
        convertView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(c, spacecrafts.get(position).getName(), Toast.LENGTH_SHORT).show();
            }
        });

        return convertView;
    }

}

     

ActivityMain Layout

  • Is our activitymain.xml.
  • Contains our contentmain.xml.
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.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="com.tutorials.hp.sqlitelistviewpagination.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.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" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

 

ContentMain Layout

  • Main Layout.
  • We specify Views and widgets xml code here.
  • This layout shall get inflated into our MainActivity interface.
  • Our views comprise ListView and next and previous buttons.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.tutorials.hp.sqlitelistviewpagination.MainActivity"
    tools:showIn="@layout/activity_main">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ListView
            android:id="@+id/lv"
            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/nextBn"
                android:text="Next"
                android:background="#009968"
                android:padding="10dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </LinearLayout>

</RelativeLayout>

Model Layout

  • Shall essentially get inflated to our ListView viewitems.
  • The rootView is a CardView  

    <?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="10dp"
    card_view:cardElevation="5dp"
    
    android:layout_height="200dp">
    
    <RelativeLayout
        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="Name"
            android:id="@+id/nameTxt"
            android:padding="10dp"
            android:layout_alignParentTop="true"
            />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Propellant"
            android:id="@+id/propellantTxt"
            android:padding="10dp"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true"
            android:textColor="@color/abc_btn_colored_borderless_text_material"
            android:textStyle="italic" />
    
    </RelativeLayout>
    </android.support.v7.widget.CardView>

     

Input Dialog Layout

  • This is SQLite and we are performing data entry so we need input dialog with edittexts and save button.
  • Shall get displayed when floating action button is clicked.

 

<?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="500dp"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_margin="1dp"
        card_view:cardCornerRadius="10dp"
        card_view:cardElevation="5dp"

        android:layout_height="match_parent">

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

        <android.support.design.widget.TextInputLayout
            android:id="@+id/nameLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <EditText
                android:id="@+id/nameEditTxt"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:singleLine="true"
                android:hint= "Name" />
        </android.support.design.widget.TextInputLayout>

        <android.support.design.widget.TextInputLayout
            android:id="@+id/propellantLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <EditText
                android:id="@+id/propellantEditTxt"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:singleLine="true"
                android:hint= "Propeant" />
        </android.support.design.widget.TextInputLayout>

        <Button android:id="@+id/saveBtn"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Save"
            android:clickable="true"
            android:background="@color/colorAccent"
            android:layout_marginTop="40dp"
            android:textColor="@android:color/white"/>
        <Button android:id="@+id/retrieveBtn"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Reload"
            android:clickable="true"
            android:background="@color/colorAccent"
            android:layout_marginTop="40dp"
            android:textColor="@android:color/white"/>

</LinearLayout>

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

Download

Here are the resources related to this tutorial.

No. Location Link
1. GitHub Direct Download)
2. GitHub Browse
3. YouTube Video Tutorial
4. YouTube Our YouTube Channel

How To Run

  • Download the project above.
  • You'll get a zipped file,extract it.
  • Open the Android Studio.
  • Now close, already open project
  • From the Menu bar click on File >New> Import Project
  • Now Choose a Destination Folder, from where you want to import project.
  • Choose an Android Project.
  • Now Click on “OK“.
  • Done, your done importing the project,now edit 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