Android ListView dynamically load more items when scroll to bottom

    In this tutorial I am discussing about adding load more functionality to listview. It will be useful when you want to show data into paged format instead of loading huge amount of data into ListView.
    This project load data (JSON) from an URL to parse. Project descriptions:
  • Loading JSON data from an URL by using an AsyncTask and parse it to my own object.
  • In ListView, showing only 5 items for first time.
  • Using OnScrollListener() for ListView to detect scrolling reach to it's bottom.
  • Continue loading data and show in ListView.

Update info

With Material Design technology, RecyclerView is the alternate widget for ListView in creating list view/table view now. Please read my newer post if you would like to make an endless RecyclerView with a ProgressBar at the bottom when data is loading...
1. Start Eclipse or Android Studio and create a new android project.
2. Create an own POJO to use for ListView item:
CountryInfor.java
package com.blogspot.hongthaiit.androidloadmorelistview;

public class CountryInfor {
 private String id;
 private String name;
 
 public String getId() {
  return id;
 }
 
 public void setId(String id) {
  this.id = id;
 }
 
 public String getName() {
  return name;
 }
 
 public void setName(String name) {
  this.name = name;
 }
}
3. Custom an AsyncTask to downloading data from Internet:
LoadCountriesFromUrlTask.java
package com.blogspot.hongthaiit.androidloadmorelistview;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.util.Log;

public class LoadCountriesFromUrlTask extends AsyncTask<Void, Void, String> {

 private Activity activity;
 private String url;
 private ProgressDialog dialog;
 private final static String TAG = LoadCountriesFromUrlTask.class.getSimpleName();

 public LoadCountriesFromUrlTask(Activity activity, String url) {
  super();
  this.activity = activity;
  this.url = url;
 }

 @Override
 protected void onPreExecute() {
  super.onPreExecute();
  // Create a progress dialog
  dialog = new ProgressDialog(activity); 
  // Set progress dialog title
  dialog.setTitle("ListView Load More Tutorial");
  // Set progress dialog message
  dialog.setMessage("Loading more...");
  dialog.setIndeterminate(false);
  // Show progress dialog
  dialog.show(); 
 }

 @Override
 protected String doInBackground(Void... params) {

  // call load JSON from url method
  return loadJSON(this.url).toString();
 }

 @Override
 protected void onPostExecute(String result) {
  ((MainActivity) activity).parseJsonResponse(result);
  dialog.dismiss();
  Log.i(TAG, result);
 }

 public JSONObject loadJSON(String url) {
  // Creating JSON Parser instance
  JSONParser jParser = new JSONParser();

  // getting JSON string from URL
  JSONObject json = jParser.getJSONFromUrl(url);

  return json;
 }

 private class JSONParser {

  private InputStream is = null;
  private JSONObject jObj = null;
  private String json = "";

  // constructor
  public JSONParser() {

  }

  public JSONObject getJSONFromUrl(String url) {

   // Making HTTP request
   try {
    // defaultHttpClient
    DefaultHttpClient httpClient = new DefaultHttpClient();
    HttpPost httpPost = new HttpPost(url);

    HttpResponse httpResponse = httpClient.execute(httpPost);
    HttpEntity httpEntity = httpResponse.getEntity();
    is = httpEntity.getContent();

   } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
   } catch (ClientProtocolException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   }

   try {
    BufferedReader reader = new BufferedReader(new InputStreamReader(is, "iso-8859-1"),
      8);
    StringBuilder sb = new StringBuilder();
    String line = null;
    while ((line = reader.readLine()) != null) {
     sb.append(line + "\n");
    }
    is.close();
    json = sb.toString();
   } catch (Exception e) {
    Log.e("Buffer Error", "Error converting result " + e.toString());
   }

   // try parse the string to a JSON object
   try {
    jObj = new JSONObject(json);
   } catch (JSONException e) {
    Log.e("JSON Parser", "Error parsing data " + e.toString());
   }

   // return JSON String
   return jObj;
  }
 }
}
4. Create an activity to run:
- It's layout simple like this:
activity_main.xml
<RelativeLayout 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" >

    <LinearLayout
        android:id="@+id/layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#000055"
        android:orientation="horizontal" >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/country"
            android:textColor="@android:color/white"
            android:textSize="22sp" />
    </LinearLayout>

    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/layout"
        android:scrollbars="none" />

</RelativeLayout>
- In activity programmatically code, we must set OnScrollListener for the ListView:

private OnScrollListener onScrollListener() {
  return new OnScrollListener() {

   @Override
   public void onScrollStateChanged(AbsListView view, int scrollState) {
    int threshold = 1;
    int count = listView.getCount();

    if (scrollState == SCROLL_STATE_IDLE) {
     if (listView.getLastVisiblePosition() >= count - threshold && pageCount < 2) {
      Log.i(TAG, "loading more data");
      // Execute LoadMoreDataTask AsyncTask
      getDataFromUrl(url_page2);
     }
    }
   }

   @Override
   public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
     int totalItemCount) {
   }

  };
 }
And this is the main activity full code:
MainActivity.java
package com.blogspot.hongthaiit.androidloadmorelistview;

import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.util.Log;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;

public class MainActivity extends Activity {

 private final static String url_page1 = "http://www.json-generator.com/api/json/get/ckDtZPkgJe?indent=2";
 private final static String url_page2 = "http://www.json-generator.com/api/json/get/bVifgkcmxu?indent=2";
 private final static String TAG = MainActivity.class.getSimpleName();
 
 private int pageCount = 0;
 
 private CountryAdapter adapter;
 private ListView listView;
 private ProgressDialog dialog;
 private ArrayList<CountryInfor> countries;

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

  listView = (ListView) findViewById(R.id.list);
  setListViewAdapter();
  getDataFromUrl(url_page1);
  listView.setOnScrollListener(onScrollListener());
 }

 private void setListViewAdapter() {
  countries = new ArrayList<CountryInfor>();
  adapter = new CountryAdapter(this, R.layout.item_listview, R.id.country_name, countries);
  listView.setAdapter(adapter);
 }
 
 // calling asynctask to get json data from internet
 private void getDataFromUrl(String url) {
  new LoadCountriesFromUrlTask(this, url).execute();
 }

 private OnScrollListener onScrollListener() {
  return new OnScrollListener() {

   @Override
   public void onScrollStateChanged(AbsListView view, int scrollState) {
    int threshold = 1;
    int count = listView.getCount();

    if (scrollState == SCROLL_STATE_IDLE) {
     if (listView.getLastVisiblePosition() >= count - threshold && pageCount < 2) {
      Log.i(TAG, "loading more data");
      // Execute LoadMoreDataTask AsyncTask
      getDataFromUrl(url_page2);
     }
    }
   }

   @Override
   public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
     int totalItemCount) {
   }

  };
 }

 //parsing json after getting from Internet
 public void parseJsonResponse(String result) {
  Log.i(TAG, result);
  pageCount++;
  try {
   JSONObject json = new JSONObject(result);
   JSONArray jArray = new JSONArray(json.getString("message"));
   for (int i = 0; i < jArray.length(); i++) {
    
    JSONObject jObject = jArray.getJSONObject(i);
    CountryInfor country = new CountryInfor();
    country.setId(jObject.getString("id"));
    country.setName(jObject.getString("name"));
    countries.add(country);
   }
   
   adapter.notifyDataSetChanged();
   if (dialog != null) {
    dialog.dismiss();
   }
  } catch (JSONException e) {
   e.printStackTrace();  
  }
 }
}
5. Layout for each ListView row:
item_listview.xml
<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" >

    <TextView
        android:id="@+id/country_name"
        android:layout_width="wrap_content"
        android:layout_height="150dp"
        android:layout_gravity="center"
        android:gravity="center"
        android:drawableLeft="@drawable/earth"
        android:drawablePadding="10dp"
        android:textColor="@android:color/holo_green_dark"
        android:textSize="20sp" />

</LinearLayout>
6. Custom a ListView adapter based on ArrayAdapter:
CountryAdapter.java
package com.blogspot.hongthaiit.androidloadmorelistview;

import java.util.List;

import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class CountryAdapter extends ArrayAdapter<CountryInfor> {

 private Activity activity;

 public CountryAdapter(Activity activity, int resource, int textViewResourceId,
   List<CountryInfor> countries) {
  super(activity, resource, textViewResourceId, countries);
  this.activity = activity;

 }

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {

  ViewHolder holder = null;
        LayoutInflater inflater = 
                (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        // If holder not exist then locate all view from UI file.
        if (convertView == null) {
            // inflate UI from XML file
            convertView = inflater.inflate(R.layout.item_listview, parent, false);
            // get all UI view
            holder = new ViewHolder(convertView);
            // set tag for holder
            convertView.setTag(holder);
        }  else {
            // if holder created, get tag from view
            holder = (ViewHolder) convertView.getTag();
        }
  
  CountryInfor country = getItem(position);
  
  holder.countryName.setText(country.getName());
  holder.countryName.setCompoundDrawablesWithIntrinsicBounds(R.drawable.earth, 0, 0, 0);

  return convertView;
 }

 private static class ViewHolder {
  private TextView countryName;

  public ViewHolder(View v) {
   countryName = (TextView) v.findViewById(R.id.country_name);
  }
 }
}
7.  Open your AndroidManifest.xml file and add Internet permission:
AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.blogspot.hongthaiit.androidloadmorelistview"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="21" />
    
    <uses-permission android:name="android.permission.INTERNET" />

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

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

</manifest>
8. Build and run program, we'll see our result: :D

pic name pic name pic name
(sorry for ads)

Share


Previous post
« Prev Post
Next post
Next Post »