Android MySQL Retrofit Full Course – INSERT SELECT UPDATE DELETE SEARCH PAGINATION → Listing Scientists in a RecyclerView with Loadmore Pagination – Camposha

Android MySQL Retrofit Full Course – INSERT SELECT UPDATE DELETE SEARCH PAGINATION

Listing Scientists in a RecyclerView with Loadmore Pagination

Listing Scientists in a RecyclerView

Introduction

We are creating a full android mysql crud app. Some of the functionalities of the app include listing scientists in a recyclerview. We will however be paging/paginating our data. This pagination will occur at the server side. We will also add a search functionality.

Concepts You will Learn

By the end of this lesson you will have learnt the following:

  1. How to render data on a recyclerview.
  2. How to perform a Load more pagination on a recyclerview.
  3. How to perform a search filter on mysql data.

NB/= Pagination and Search will occur at the server side. We’ll had looked at that in a PHP class earlier.

Step 1.

Create a file called ScientistsActivity.java and add the following code imports:

package info.camposha.retrofitmysqlcrud.Views;

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.OnScrollListener;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.AbsListView;
import android.widget.ProgressBar;

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

import info.camposha.retrofitmysqlcrud.Helpers.MyAdapter;
import info.camposha.retrofitmysqlcrud.Helpers.Utils;
import info.camposha.retrofitmysqlcrud.R;
import info.camposha.retrofitmysqlcrud.Retrofit.ResponseModel;
import info.camposha.retrofitmysqlcrud.Retrofit.RestApi;
import info.camposha.retrofitmysqlcrud.Retrofit.Scientist;
import io.github.inflationx.viewpump.ViewPumpContextWrapper;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

Then create class below those imports:

public class ScientistsActivity extends AppCompatActivity

Make it implement the following interfaces:

public class ScientistsActivity extends AppCompatActivity
        implements SearchView.OnQueryTextListener,MenuItem.OnActionExpandListener{

We’ve just created a class extending AppCompatActivity. We’ve then made it implement two interfaces:

  1. OnQueryTextListener – To listen to text change events of our serachview.
  2. OnActionExpandListener – Raised when a menu item is expanded.

Then define our instance fields:

     //We define our instance fields
    private RecyclerView rv;
    private MyAdapter mAdapter;
    private LinearLayoutManager layoutManager;
    public ArrayList<Scientist> allPagesScientists = new ArrayList();
    private List<Scientist> currentPageScientists;
    private Boolean isScrolling = false;
    private int currentScientists, totalScientists, scrolledOutScientists;
    private ProgressBar mProgressBar;

You can see we’ve declared Recyclerview which is our adapterview. The MyAdapter is our RecyclerView adapter. LinearLayoutManager will allow us position items in our recyclerview.
allPagesArrayList will hold all the scientists that have been downloaded so far.

currentPageScientists will hold the current scientists as we paginate our data. For example if we are in page 3, then this arraylist will hold data for page alone while allPagesScientsits will hold data for pages 1,2 and 3.

isScrolling will tell us when the user is scrolling the recyclerview. By default we set it to false.

currentScientists tell us the current page.It’s an intger as are totalScientists and scrolledOutScientists.

Our ProgressBar will be shown while we are downloading our data.

Add this method after the declarations above:

    private void initializeViews() {
        mProgressBar = findViewById(R.id.mProgressBarLoad);
        mProgressBar.setIndeterminate(true);
        Utils.showProgressBar(mProgressBar);
        rv = findViewById(R.id.mRecyclerView);
    }

The above method is just referencing our views like progress bar and recyclerview.

Then add the following code:

    /**
     * This method will setup oir RecyclerView
     */
    private void setupRecyclerView() {
        layoutManager = new LinearLayoutManager(this);
        mAdapter = new MyAdapter(this, allPagesScientists);
        rv.setAdapter(mAdapter);
        rv.setLayoutManager(layoutManager);
    }

The above method is setting up our recyclerview. First we instantiate our LinearLayoutManager,then our adapter and set them to our recyclerview.

Then create a method like below:

    /**
     * This method will download for us data from php mysql based on supplied query string
     * as well as pagination parameters. We are basiclally searching or selecting data
     * without seaching. However all the arriving data is paginated at the server level.
     */
    private void retrieveAndFillRecyclerView(final String action, String queryString,
     final String start, String limit) {

That method, as the name suggests will fetch our data from mysql and populate our recyclerview. However alot of things will happen in this method. You can see we are passing 4 strings.

They include:

  1. action – The action we want to perform. Whether it’s downloading all data or downloading paged data. The above method is capable of both.
  2. queryString – We said we will be searching against MySQL. Well this is the query or search term that we pass over to be sent to the server.
  3. start – Where we are starting our pagination. it’s also called offset. For example, are we starting pagination at row number 5, row number 10 or what. That is the offset.
  4. limit – The number of rows we are fetching.

Then add the following code in the method:

        mAdapter.searchString = queryString;
        RestApi api = Utils.getClient().create(RestApi.class);
        Call<ResponseModel> retrievedData;

We have set our search string to a variable in our adapter. There it will be highlighted as we search. Then we have instantiated our RestApi using the create() method. Then we’ve defined a Retrofit2.Call object.

Then add the following code:

        if (action.equalsIgnoreCase("GET_PAGINATED")) {
            retrievedData = api.search("GET_PAGINATED", queryString, start, limit);
            Utils.showProgressBar(mProgressBar);
        } else if (action.equalsIgnoreCase("GET_PAGINATED_SEARCH")) {
            Utils.showProgressBar(mProgressBar);
            retrievedData = api.search("GET_PAGINATED_SEARCH", queryString, start, limit);
        } else {
            Utils.showProgressBar(mProgressBar);
            retrievedData = api.retrieve();
        }

The GET_PAGINATED is an action string we will send to the server if we want to download paginated data, but without applying a search constraint. The GET_PAGINATED_SEARCH will be sent if we want to paginate a search result.

api.search() will perform a search against our database.However note that the GET_PAGINATED will search with an empty constraint, thus returning all records. Otherwise the GET_PAGINATED_SEARCH will send a search term typed the user.

api.retrieve() will perform a HTTP GET request, SELECTING all rows.

Now add the following code:

        retrievedData.enqueue(new Callback<ResponseModel>() {

We are invoking the enqueue method which will enqueue our HTTP Call. That call will be maded asynchronously. Thus we need to rely on callback methods for response.

Let’s then come and handle those responses, starting with a positive response:

            @Override
            public void onResponse(Call<ResponseModel> call, Response<ResponseModel>
             response) {
                Log.d("RETROFIT", "CODE : " + response.body().getCode());
                Log.d("RETROFIT", "MESSAGE : " + response.body().getMessage());
                Log.d("RETROFIT", "RESPONSE : " + response.body().getResult());
                currentPageScientists = response.body().getResult();

                if (currentPageScientists != null && currentPageScientists.size() > 0) {
                    if (action.equalsIgnoreCase("GET_PAGINATED_SEARCH")) {
                        allPagesScientists.clear();
                    }
                    for (int i = 0; i < currentPageScientists.size(); i++) {
                        allPagesScientists.add(currentPageScientists.get(i));
                    }

                } else {
                    if (action.equalsIgnoreCase("GET_PAGINATED_SEARCH")) {
                        allPagesScientists.clear();
                    }
                }
                mAdapter.notifyDataSetChanged();
                Utils.hideProgressBar(mProgressBar);
            }

Take a look at those parameters in our onResponse(): Call<ResponseModel> call and Response<ResponseModel> response. Clearly you see the generic type is a ResponseModel object. That ResponseModel will contain our message, our message code and our list of scientists.

That’s why we are logging them out as you you can see above.

We assign the list of scientists we have just downlaoded into the currentPageScientists arraylist. If the user was searching then we will clear the allPagesScientists. We then add the current page scientists into our allPagesScientists.

If the currentPageScientists list is empty, that is if we receive an empty list from the server, we check if the user was searching. If that is the case, then we clear allPagesScientists.Why? Well because the user has typed a search term which hits not match in the server therefore we clear our list to indicate there is no search item.

Live Video Lesson(Recommended)

Then we refresh our adapter and hide the progressbar:

                mAdapter.notifyDataSetChanged();
                Utils.hideProgressBar(mProgressBar);

Then we handle the failure response:

            @Override
            public void onFailure(Call<ResponseModel> call, Throwable t) {
                Utils.hideProgressBar(mProgressBar);
                Log.d("RETROFIT", "ERROR: " + t.getMessage());
                Utils.showInfoDialog(ScientistsActivity.this, "ERROR", t.getMessage());
            }
        });
    }

Well that’s the end of that method.

Let’s now come and listen to recyclerview scroll events. This method will allow us implement our load more pagination. Add the following code:

    /**
     * We will listen to scroll events. This is important as we are implementing scroll to
     * load more data pagination technique
     */
    private void listenToRecyclerViewScroll() {
        rv.addOnScrollListener(new OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView rv, int newState) {
                //when scrolling starts
                super.onScrollStateChanged(rv, newState);
                //check for scroll state
                if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
                    isScrolling = true;
                }
            }
            @Override
            public void onScrolled(RecyclerView rv, int dx, int dy) {
                // When the scrolling has stopped
                super.onScrolled(rv, dx, dy);
                currentScientists = layoutManager.getChildCount();
                totalScientists = layoutManager.getItemCount();
                scrolledOutScientists = ((LinearLayoutManager) rv.getLayoutManager()).
                findFirstVisibleItemPosition();

                if (isScrolling && (currentScientists + scrolledOutScientists ==
                 totalScientists)) {
                    isScrolling = false;

                    if (dy > 0) {
                        // Scrolling up
                        retrieveAndFillRecyclerView("GET_PAGINATED",
                         mAdapter.searchString,
                         String.valueOf(totalScientists), "5");

                    } else {
                        // Scrolling down
                    }
                }
            }
        });
    }

We’ve started by invoking the addOnScrollListener() method. Use this method as the setOnScrollListener has since been deprecated. We pass an annonymous class, OnScrollListener to our addOnScrollListener method.

This forces us to override or implement two methods:

  1. onScrollStateChanged() and
  2. onScrolled

The first will be raised when our scroll state changes. If the newState equals AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL), then the user is actually scrolling. Thus we set the isScrolling property to true.

The second callback method, the onScrolled() will be invoked when the scrolling has stopped. Thus we get the current page and load data for that page.

Let’s proceed.

Add the following code to finish this activity:


 /**
     * We inflate our menu. We show SearchView inside the toolbar
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.scientists_page_menu, menu);
        MenuItem searchItem = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) searchItem.getActionView();
        searchView.setOnQueryTextListener(this);
        searchView.setIconified(true);
        searchView.setQueryHint("Search");
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_new:
                Utils.openActivity(this, CRUDActivity.class);
                finish();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public boolean onQueryTextSubmit(String query) {
        return false;
    }

    @Override
    public boolean onQueryTextChange(String query) {
        retrieveAndFillRecyclerView("GET_PAGINATED_SEARCH", query, "0", "5");
        return false;
    }

    @Override
    public boolean onMenuItemActionExpand(MenuItem item) {
        return false;
    }

    @Override
    public boolean onMenuItemActionCollapse(MenuItem item) {
        return false;
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase));
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        this.finish();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scientists);

        initializeViews();
        this.listenToRecyclerViewScroll();
        setupRecyclerView();
        retrieveAndFillRecyclerView("GET_PAGINATED", "", "0", "5");
    }
}

//end

That’s it. Now move over to the next lesson.

(1) Comment

  • IOSTREAM17 September 17, 2019 @ 6:20 am

    Good job.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

(2) Comments

  • IOSTREAM17 July 5, 2019 @ 6:55 am

    good job well done

    • Admin bar avatar
      Oclemy July 5, 2019 @ 7:08 am

      Thank you very much.

Comments are closed.

X