Thursday, August 27, 2015

Android using SQLite Select,Insert,Update,Delete

Android using SQLite Select,Insert,Update,Delete

Last time I told you all about SQLite and use the basic information, I believe that friends of SQLite have some understanding, and that it today, I'll share with you how to use SQLite in Android.
Now the mainstream mobile devices like the Android, iPhone and so the use SQLite as the storage engine of complex data, in our time developing applications for the mobile device, perhaps we should use to our SQLite to store large amounts of data, so we need to have a mobile device SQLite development skills on. For the Android platform, the system built a rich API for developers to operate SQLite, we can easily complete access to the data.
Here to tell you about SQLite common method of operation, for convenience, I will write the code in the Activity's onCreate in:

    protected  void  onCreate (Bundle savedInstanceState) { 
        Super .onCreate (savedInstanceState); 
        // Open or create test.db database 
        SQLiteDatabase db = openOrCreateDatabase ( "test.db" , Context.MODE_PRIVATE,  null ); 
        db.execSQL ( "DROP TABLE IF EXISTS person" ); 
        // Create the person table 
        Person person =  new  Person (); =  "John" ; 
        person.age =  30 ; 
        // Insert data 
        db.execSQL ( "?? INSERT INTO person VALUES (NULL,,)" ,  new  Object [] {, person.age}); 
         =  "David" ; 
        person.age =  33 ; 
        // ContentValues ​​store data in the form of key-value pairs 
        ContentValues ​​cv =  new  ContentValues ​​(); 
        cv.put ( "name" ,; 
        cv.put ( "age" , person.age); 
        // Insert the data ContentValues 
        db.insert ( "person" ,  null , cv); 
        cv =  new  ContentValues ​​(); 
        cv.put ( "age" ,  35 ); 
        // Update the data 
        db.update ( "person" , cv,  "name =?" ,  new  String [] { "John" }); 
        Cursor c = db.rawQuery ( "SELECT * FROM person WHERE age> =?" ,  new  String [] { "33" }); 
        while  (c.moveToNext ()) { 
            int  _id = c.getInt (c.getColumnIndex ( "_id" )); 
            String name = c.getString (c.getColumnIndex ( "name" )); 
            int  age = c.getInt (c.getColumnIndex ( "age" )); 
            Log.i ( "db" ,  "_id =>"  + _id +  ", name =>"  + name +  ", age =>"  + age); 
        c.close (); 
        // Delete data 
        db.delete ( "person" ,  "? age <" ,  new  String [] { "35" }); 
        // Close the current database 
        db.close (); 
        // Delete test.db database 
// DeleteDatabase ("test.db"); 

After executing the above code, the system will generate a "test.db" database file in / data / data / [PACKAGE_NAME] / databases directory, as shown:

The above code basically cover most of the database operation; for add, update, and delete it, we can use

db.executeSQL (String sql); 
db.executeSQL (String SQL, Object [] bindArgs); // SQL statement using placeholders, then the second parameter is the actual parameter set 

In addition to the unified form, they each have their own methods of operation:

db.insert (String table, String nullColumnHack, ContentValues ​​values); 
db.update (String table, Contentvalues ​​values, String whereClause, String whereArgs); 
db.delete (String table, String whereClause, String whereArgs); 

The first parameter of the above three methods are said to be operating table; insert the second parameter indicates that if the insertion of the data in each column is empty, you need to specify the name of a column in a row, this system column is set to NULL, not errors; insert the third parameter is the variable ContentValues ​​type, is key to the composition of the Map, key representatives of the column name, value representing the columns values ​​to be inserted; update of the second parameters are very similar, but it is key to update the field value value for the latest third parameter whereClause expressed WHERE expression, such as "age>? and age <?" and so on, the last parameter is a placeholder whereArgs The actual parameter values; Arguments delete method is the same.
Here's what queries. Query with respect to several operating above is more complicated, because we are faced with a variety of query conditions, so the system also takes into account this complexity, provides us with a more extensive inquiry form:

db.rawQuery (String sql, String [] selectionArgs); 
db.query (String table, String [] columns, String selection, String [] selectionArgs, String groupBy, String having, String orderBy); 
db.query (String table, String [] columns, String selection, String [] selectionArgs, String groupBy, String having, String orderBy, String limit); 
db.query (String distinct, String table, String [] columns, String selection, String [] selectionArgs, String groupBy, String having, String orderBy, String limit);

The above are some common query method, the first one of the most simple, all the SQL statements are organized into a string, using placeholders instead of the actual parameters, selectionArgs is a placeholder for the actual parameter set; following several parameters are very similar, columns indicate the name of the column all set to query, selection represents the conditional statement after WHERE, you can use placeholders, groupBy designated grouping column names, having specified grouping criteria, with groupBy use, orderBy specify the sort column name, limit specified paging parameters, distinct can specify "true" or "false" indicates whether or not to filter duplicates. Note that, selection, groupBy, having, orderBy, limit these parameters are not included in "WHERE", "GROUP BY", "HAVING", "ORDER BY", "LIMIT" and other SQL keywords.
Finally, they also return a Cursor object that represents the data set cursor, somewhat similar to JavaSE the ResultSet.
Here is a common method Cursor object:

c.move ( int  offset);  // current position as a reference, move to the specified line 
c.moveToFirst ();     // move to the first row 
c.moveToLast ();      // move to the last row 
c.moveToPosition ( int  position);  // move to the specified line 
c.moveToPrevious ();  // move to the previous line 
c.moveToNext ();      // move to the next line 
c.isFirst ();         // points to the first article 
c.isLast ();      // points to last 
c.isBeforeFirst ();   // points to the first before 
; c.isAfterLast ()     // if point after the last one 
c.isNull ( int  columnIndex);   // the specified column is empty (column cardinality 0) 
c.isClosed ();        // cursor is turned off 
c.getCount ();        // total number of data items 
c.getPosition ();     // Returns the number of rows in the cursor is pointing to the current 
c.getColumnIndex (String columnName); // Returns the name of a column corresponding to the column index value 
c.getString ( int  columnIndex);    // Returns the value of the specified column of the current row 

In the above code example, has used this method for several commonly used some more information about, you can refer to the official documentation for instructions.
Finally, after we completed the operation of the database, remember to call SQLiteDatabase the close () method releases the database connection, or prone SQLiteException.
SQLite is above the basic applications, but in the actual development, in order to better manage and maintain the database, we will package a database operation class inherits from SQLiteOpenHelper class, then the class-based, repackaging our business logic methods
Here, we have an example to explain the specific usage, we create a new project named db, structured as follows:

Which DBHelper inherited SQLiteOpenHelper, as a base class to maintain and manage the database, DBManager is based on DBHelper, encapsulates the common business method, Person is our person table corresponding JavaBean, MainActivity that we display interface.
Let us first look at DBHelper:

public  synchronized  SQLiteDatabase getReadableDatabase () { 
    if  (mDatabase! =  null  && mDatabase.isOpen ()) { 
        // If mDatabase not empty and has opened the direct return 
        return  mDatabase; 
    if  (mIsInitializing) { 
        // If you are initializing an exception is thrown 
        throw  new  IllegalStateException ( "getReadableDatabase Called recursively" ); 
    // Start instantiation database mDatabase 
    try  { 
        // Note that this is called getWritableDatabase () method 
        return  getWritableDatabase (); 
    }  catch  (SQLiteException e) { 
        if  (mName ==  null ) 
            throw  e;  // Can not Open a temp database Read-only! 
        Log.e (TAG,  "Could not Open"  + mName +  "for Writing (Will try Read-only):" , e); 
    // If you can not open the database in read-write mode places open read-only 
    SQLiteDatabase db =  null ; 
    try  { 
        mIsInitializing =  true ; 
        String path = mContext.getDatabasePath (mName) .getPath (); // Get the database path 
        // Open the database in read-only mode 
        db = SQLiteDatabase.openDatabase (path, mFactory, SQLiteDatabase.OPEN_READONLY); 
        if  (db.getVersion ()! = mNewVersion) { 
            throw  new  SQLiteException ( "Can not upgrade from Version Read-only database"  + db.getVersion () +  "to" 
                    + MNewVersion +  ":"  + path); 
        onOpen (db); 
        Log.w (TAG,  "Opened"  + mName +  "in Read-only mode" ); 
        mDatabase = db; // specify the new open database mDatabase 
        return  mDatabase; // Returns the open database 
    }  finally  { 
        mIsInitializing =  false ; 
        if  (db! =  null  && db! = mDatabase) 
            db.close (); 

In getReadableDatabase () method, first determine whether the database instance already exists and is open, if it is, then the direct return that instance, or else trying to get a read-write mode database instance, if they get the disk space is full, etc. if it fails, then the database is opened in read-only mode, access to the database instance and return, and then assigned to mDatabase latest open database instance. Since there may be calls to getWritableDatabase () method, we will look at the:

public  synchronized  SQLiteDatabase getWritableDatabase () { 
    if  (mDatabase! =  null  && mDatabase.isOpen () &&! mDatabase.isReadOnly ()) { 
        // If mDatabase not empty and is not a read-only mode is turned on returns that instance 
        return  mDatabase; 
    if  (mIsInitializing) { 
        throw  new  IllegalStateException ( "getWritableDatabase Called recursively" ); 
    // If we have a read-only database open, someone could be using it 
    // (Though they should not), which would cause a lock to be held on 
    // The file, and our attempts to open the database read-write would 
    // Fail waiting for the file lock. To prevent that, we acquire the 
    // Lock on the read-only database, which shuts out other users. 
    boolean  success =  false ; 
    SQLiteDatabase db =  null ; 
    // If mDatabase not empty then locked to prevent other operations 
    if  (mDatabase! =  null ) 
        mDatabase.lock (); 
    try  { 
        mIsInitializing =  true ; 
        if  (mName ==  null ) { 
            db = SQLiteDatabase.create ( null ); 
        }  else  { 
            // Open or create a database 
            db = mContext.openOrCreateDatabase (mName,  0 , mFactory); 
        // Get the database version (database, if you just created, version 0) 
        int  Version = db.getVersion (); 
        // Compare versions (our code version mNewVersion 1) 
        if  (Version! = mNewVersion) { 
            db.beginTransaction (); // start transaction 
            try  { 
                if  (Version ==  0 ) { 
                    // Execute our onCreate method 
                    onCreate (db); 
                }  else  { 
                    // If we apply the upgrade mNewVersion 2, and the original version of a method of execution onUpgrade 
                    onUpgrade (db, version, mNewVersion); 
                db.setVersion (mNewVersion); // set up the latest version 
                db.setTransactionSuccessful (); // Set the transaction success 
            }  finally  { 
                db.endTransaction (); // end the transaction 
        onOpen (db); 
        success =  true ; 
        return  ; db // Returns the read-write mode database instance 
    }  finally  { 
        mIsInitializing =  false ; 
        if  (success) { 
            // Open success 
            if  (mDatabase! =  null ) { 
                // If it has a value mDatabase to close 
                try  { 
                    mDatabase.close (); 
                }  catch  (Exception e) { 
                mDatabase.unlock (); // Unlock 
            mDatabase = db; // assignment to mDatabase 
        }  else  { 
            // Open the case of failure: unlocked, closed 
            if  (mDatabase! =  null ) 
                mDatabase.unlock (); 
            if  (db! =  null ) 
                db.close (); 

As you can see, a few key steps, first determine mDatabase if not empty open read-only mode is not directly returned, otherwise if mDatabase not empty the lock, and then begin to open or create a database, compare versions, according to The version number to call the appropriate method to set a new version number for the database, and finally release the old is not empty of mDatabase and unlock, open the new database instance confers mDatabase, and returns the latest instance.
After reading the above process, you perhaps know a lot, if not in the face of disk space, etc., getReadableDatabase () will generally return and getWritableDatabase () the same database instance, so we use getWritableDatabase in DBManager constructor () Gets the database instance is used throughout the application is feasible. Of course, if you're really worried about that happening, then you can start () Gets the data instance with the getWritableDatabase, if an exception is encountered, then trying () Gets the instance with the getReadableDatabase, of course, this time instance you can read but not write access to a.
Finally, let's look at how to use these data manipulation methods to display data, the following is layout files and code:

<? XML  Version = "1.0"  encoding = "UTF-8" ?> 
< LinearLayout  xmlns: Android = "" 
    Android: Orientation = "Vertical" 
    Android: layout_width = "fill_parent" 
    Android: layout_height = "fill_parent" > 
    < Button 
        Android: layout_width = "fill_parent" 
        Android: layout_height = "wrap_content" 
        Android: text = "add" 
        Android: onClick = "add" /> 
    < Button 
        Android: layout_width = "fill_parent" 
        Android: layout_height = "wrap_content" 
        Android: text = "Update" 
        Android: onClick = "Update" /> 
    < Button 
        Android: layout_width = "fill_parent" 
        Android: layout_height = "wrap_content" 
        Android: text = "delete" 
        Android: onClick = "delete" /> 
    < Button 
        Android: layout_width = "fill_parent" 
        Android: layout_height = "wrap_content" 
        Android: text = "Query" 
        Android: onClick = "Query" /> 
    < Button 
        Android: layout_width = "fill_parent" 
        Android: layout_height = "wrap_content" 
        Android: text = "queryTheCursor" 
        Android: onClick = "queryTheCursor" /> 
    < ListView 
        Android: ID = "@ + ID / listView" 
        Android: layout_width = "fill_parent" 
        Android: layout_height = "wrap_content" /> 
</ LinearLayout > 

Package  com.scott.db; 
Import  java.util.ArrayList; 
Import  java.util.HashMap; 
Import  java.util.List; 
Import  java.util.Map; 
Import  android.database.Cursor; 
Import  android.database.CursorWrapper; 
Import  android.os.Bundle; 
Import  android.view.View; 
Import  android.widget.ListView; 
Import  android.widget.SimpleAdapter; 
Import  android.widget.SimpleCursorAdapter; 
public  class  MainActivity  extends  Activity { 
    Private  DBManager MGR; 
    Private  ListView listView; 
    public  void  onCreate (Bundle savedInstanceState) { 
        Super .onCreate (savedInstanceState); 
        setContentView (R.layout.main); 
        listView = (ListView) findViewById (; 
        // Initialize DBManager 
        MGR =  new  DBManager ( this ); 
    protected  void  onDestroy () { 
        Super .onDestroy (); 
        Activity close the last application should release DB // 
        mgr.closeDB (); 
    public  void  add (View View) { 
        ArrayList <Person> persons =  new  ArrayList <Person> (); 
        Person1 person =  new  Person ( "Ella" ,  22 ,  "Lively Girl" ); 
        Person2 person =  new  Person ( "Jenny" ,  22 ,  "beautiful Girl" ); 
        Person3 person =  new  Person ( "Jessica" ,  23 ,  "Sexy Girl" ); 
        Person4 person =  new  Person ( "Kelly" ,  23 ,  "Hot Baby" ); 
        Person5 person =  new  Person ( "Jane" ,  25 ,  "a Pretty woman" ); 
        persons.add (person1); 
        persons.add (person2); 
        persons.add (person3); 
        persons.add (person4); 
        persons.add (person5); 
        mgr.add (persons); 
    public  void  Update (View View) { 
        Person person =  new  Person (); =  "Jane" ; 
        person.age =  30 ; 
        mgr.updateAge (person); 
    public  void  delete (View View) { 
        Person person =  new  Person (); 
        person.age =  30 ; 
        mgr.deleteOldPerson (person); 
    public  void  Query (View View) { 
        List <Person> persons = mgr.query (); 
        ArrayList <Map <String, String >> List =  new  ArrayList <Map <String, String >> (); 
        for  (Person person: persons) { 
            HashMap <String, String> map =  new  HashMap <String, String> (); 
            map.put ( "name" ,; 
            map.put ( "info" , person.age +  "years old,"  +; 
            list.add (map); 
        SimpleAdapter Adapter =  new  SimpleAdapter ( this , List, android.R.layout.simple_list_item_2, 
                    new  String [] { "name" ,  "info" },  new  int [] {,}); 
        listView.setAdapter (adapter); 
    public  void  queryTheCursor (View View) { 
        Cursor c = mgr.queryTheCursor (); 
        startManagingCursor (c);  // entrusted to activity according to their life cycle to manage the life cycle of Cursor 
        CursorWrapper cursorWrapper =  new  CursorWrapper (c) { 
            public  String getString ( int  columnIndex) { 
                // Will be added before the age Introduction 
                if  (getColumnName (columnIndex) .equals ( "info" )) { 
                    int  age = getInt (getColumnIndex ( "age" )); 
                    return  age +  "years old,"  +  Super .getString (columnIndex); 
                return  Super .getString (columnIndex); 
        // Make sure that the query results in "_id" column 
        SimpleCursorAdapter Adapter =  new  SimpleCursorAdapter ( this , android.R.layout.simple_list_item_2,  
                cursorWrapper,  new  String [] { "name" ,  "info" },  new  int [] {,}); 
        ListView listView = (ListView) findViewById (; 
        listView.setAdapter (adapter); 

It should be noted that the application of SimpleCursorAdapter, when we use this adapter, we must first get a Cursor object, and there are several issues: how to manage the life cycle of Cursor, if the packaging Cursor, Cursor result set is what needs attention.
If you manually to manage Cursor words will be very troublesome, there is a certain risk, there will be mishandled during operation if abnormal, fortunately Activity provides us startManagingCursor (Cursor cursor) method, it will be based Activity lifecycle to manage current The Cursor object, Below is a description of the method:

The article mentioned, startManagingCursor method to manage the life cycle of the current Cursor object based Activity lifecycle, that when he stopped Activity Cursor will automatically call the deactivate method, disable the cursor back to the screen when the Activity it calls Cursor The requery method again query, when Activity destroyed managed Cursor will automatically shut down the release.
How to package Cursor: we will use to CursorWrapper objects to package our Cursor object, we need to achieve data conversion work, this CursorWrapper actually implements the Cursor interface. We inquire acquired Cursor Cursor fact is quoted, but the system actually returned to us must be an object instance of a class that implements the Cursor interface, we use CursorWrapper wrap this instance, and then use the results SimpleCursorAdapter displayed on the list.
Cursor result set needs to pay attention to what: One of the most important to note is that in our result set must contain an "_id" column, otherwise SimpleCursorAdapter will betray you, why should I? This stems from the norm because SQLite, the main key to "_id" as the standard. Solution three: first, when construction of the table according to the specification do; the second, when a query with an alias, for example: SELECT id AS _id FROM person; third, in CursorWrapper make a fuss:

CursorWrapper cursorWrapper =  new  CursorWrapper (c) { 
    public  int  getColumnIndexOrThrow (String columnName)  throws  IllegalArgumentException { 
        if  (columnName.equals ( "_id" )) { 
            return  Super .getColumnIndex ( "ID" ); 
        return  Super .getColumnIndexOrThrow (columnName); 

If you try to get the "_id" column corresponding index from CursorWrapper, we will return query results in "id" column corresponding to the index.
Finally, we look at the outcome:

Wednesday, August 26, 2015

Android Create paging with ListView

Create paging with ListView

Android familiar friends all know, whether it is microblogging client or news client, are inseparable from the list of components, it can be said the list of components is the most important aspect of Android data presentation components, we have to talk about the list of components ListView today Load related content data. Generally speaking, when an application to show large amounts of data, not all of the available data are presented to the user, because it regardless of the server or the client for it is no small pressure, therefore, many applications are based on points in the form of batch loaded to get the data you need. For example: microblogging client may automatically loads next data when the user slides to the bottom of the list, it may be placed at the bottom of a "load more" button, the user clicked, loads next data.
Today, we combine the use of examples to demonstrate what ListView data acquisition process.
Create a loadmore project, we look at the charts and the final image:

The left contains three layout file, a Adapter and a Activity, right after we run the main interface.
Wherein, main.xml is the main interface layout file that contains a ListView components, as follows:
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="" 
</ LinearLayout> 

Here we refer to the Android built-in named list of id, because we want to use later to ListActivity, our MainActivity inherit it.
Then is list_item.xml, it is the ListView layout file a single list item, from renderings can be seen here only to use a TextView component, list_item.xml code is as follows:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="" 
</ LinearLayout> 

We note that the list in the bottom right there is a button Unlike other list items, this is what happens? In fact, this button is a view that we added in the bottom of the ListView. ListView component provides two very useful features that you can add custom views at the top and bottom. We here at the bottom of the ListView adds a view to load more data, this view corresponds load_more.xml layout files, as follows:

<?xml version="1.0" encoding="utf-8"?> 
        android:text="load more" 
</ LinearLayout> 

Next we look at our Adapter, ListViewAdapter code is as follows:

package com.scott.loadmore; 
import java.util.List; 
import android.content.Context; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.BaseAdapter; 
import android.widget.TextView; 
public class ListViewAdapter extends BaseAdapter { 
    private List<String> items; 
    private LayoutInflater inflater; 
    public ListViewAdapter(Context context, List<String> items) { 
        this.items = items; 
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);       
    public  int  getCount () { 
        return items.size(); 
    public Object getItem(int position) { 
        return items.get(position); 
    public long getItemId(int position) { 
        return position; 
    public View getView(int position, View view, ViewGroup parent) { 
        if (view == null) { 
            view = inflater.inflate(R.layout.list_item, null); 
        TextView text = (TextView) view.findViewById(; 
        return view; 
     * Add a list item
     * @param item
     * / 
    public void addItem(String item) { 

This ListViewAdapter is our custom adapter, it inherits from BaseAdapter, instantiate this adapter requires a Context object to get LayoutInflater instance and to act as a collection object adapter data sets; we filled in getView method list_item.xml layout files, complete list Each data display; addItem method is used when loading data set to add new data to the data.
Finally, we look at MainActivity:

package com.scott.loadmore; 
import java.util.ArrayList; 
import android.os.Bundle; 
import android.os.Handler; 
import android.util.Log; 
import android.view.View; 
import android.widget.AbsListView; 
import android.widget.AbsListView.OnScrollListener; 
import android.widget.Button; 
import android.widget.ListView; 
public class MainActivity extends ListActivity implements OnScrollListener { 
    private ListView listView; 
    Private  int  visibleLastIndex =  0 ;    // last visible item index 
    Private  int  visibleItemCount;        // total number of items in the current window visible 
    private ListViewAdapter adapter; 
    private View loadMoreView; 
    private Button loadMoreButton; 
    private Handler handler = new Handler(); 
    public void onCreate(Bundle savedInstanceState) { 
        loadMoreView = getLayoutInflater().inflate(R.layout.load_more, null); 
        loadMoreButton = (Button) loadMoreView.findViewById(; 
        listView = getListView();              
        listView.addFooterView (loadMoreView);    // set the bottom of the list view 
        initAdapter (); 
        setListAdapter (Adapter);                 // id is automatically set the adapter list of ListView 
        listView.setOnScrollListener ( this );      // add listener slide 
     * Initialize adapter
     * / 
    private  void  initAdapter () { 
        ArrayList<String> items = new ArrayList<String>(); 
        for (int i = 0; i < 10; i++) { 
            items.add(String.valueOf(i + 1)); 
        adapter = new ListViewAdapter(this, items); 
     * When the slide is called
     * / 
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 
        this.visibleItemCount = visibleItemCount; 
        visibleLastIndex = firstVisibleItem visibleItemCount + -  1 ; 
     * When the slide is called the status change
     * / 
    public void onScrollStateChanged(AbsListView view, int scrollState) { 
        int  itemsLastIndex = adapter.getCount () -  1 ;     // index of the last item of the data set 
        int  lastIndex = itemsLastIndex +  1 ;              // add loadMoreView item at the bottom 
        if (scrollState == OnScrollListener.SCROLL_STATE_IDLE && visibleLastIndex == lastIndex) { 
            // If it is automatically loaded, you can place the code to load data asynchronously here 
            Log.i("LOADMORE", "loading..."); 
     * Click the button event
     * @param view
     * / 
    public void loadMore(View view) { 
        handler.postDelayed(new Runnable() { 
            public void run() { 
                loadData (); 
                adapter.notifyDataSetChanged ();  // data set after the change, notify adapter 
                listView.setSelection (visibleLastIndex - visibleItemCount +  1 );  // Set the selected item 
                loadMoreButton.setText("load more");  
        },    2000);
     * Analog load data
     * / 
    private  void  loadData () { 
        int count = adapter.getCount(); 
        for (int i = count; i < count + 10; i++) { 
            adapter.addItem(String.valueOf(i + 1)); 

As shown in the code, when we get onCreate method is invoked listView assembly, set the bottom view of the loadMoreView, which includes a button that will trigger clicked loadMore method call, the other at the end of listView set the adapter, but also to set up Sliding event listener, when the slide list onScroll will be called when the slide status change onScrollStateChanged will be called.
Let's show you the loading process:

As shown, when finished click on the button, there loading operation, the new data immediately after the original data is loaded after completion as shown at right. Then we slide in the end portion, the Load button still work: