Wednesday, October 23, 2013

Using SQLiteDatabase To Preserve Data In A ListView: Android

If you haven't yet, Go to Part 1 of this two-part tutorial

In the previous post, we created a basic application that gives us a list showing the order in which the SW movies were released. We created a custom adapter that allowed us to insert object data into each row of the list using a custom row layout. What we still need to do, however, is find a way to save the data so that when we close the application and restart it, the data will persist.

As it stands now, however, we have no way to show that our application isn't actually saving the data because it gets automatically displayed each time we start it up. Just look at the onCreate method in ListFiller.java to see why; we need to add a feature that allows us to control when the movie list gets displayed. This is easily done with a button, so let's add one. Also, let's add an 'exit' button as well.

activity_list_filler.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
   
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">
        <Button
            android:id="@+id/showList"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/showList"
            android:onClick="onClick"/>
        <Button
            android:id="@+id/exit"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/exit"
            android:onClick="onClick"/>
    </LinearLayout>

    <ListView
        android:id="@+id/theList"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10sp" />

</LinearLayout>

Graphical Layout

So, above we have our new activity_list_filler.xml file and it's associated Graphical Layout. Now, let's add some code to ListFiller.java to get the buttons working.

ListFiller.java
import java.util.ArrayList;
import java.util.List;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.ListView;

public class ListFiller extends Activity {
         ListView movieList;
                  

        @Override
         protected void onCreate(Bundle savedInstanceState) {
                       super.onCreate(savedInstanceState);
                       setContentView(R.layout.activity_list_filler);
                                    
                       List myList = new ArrayList();
                       movieList = (ListView) findViewById(R.id.theList);
                       movieList.setAdapter(new MovieListAdapter(this, R.layout.row_layout, myList));
         }
                  
         public void onClick(View view) {
                @SuppressWarnings("unchecked")
                 ListView movieList = (ListView) findViewById(R.id.theList);
                 MovieListAdapter swadapter = (MovieListAdapter) movieList.getAdapter();
                 SWMovie swmovie = null;
                 switch (view.getId()) {
                 case R.id.showList:
                        List myList = new ArrayList();
                        for (int i = 1; i < 4; i++) {
                            String g = Integer.toString(i);
                            int k = i + 3;
                            String j = Integer.toString(k);
                            myList.add(new SWMovie("Movie " + g, "Episode " + k));
                            swmovie = new SWMovie("Movie " + g, "Episode " + k);
                            swadapter.add(swmovie);
                        }
                        for (int i = 1; i < 4; i++) {
                            String g = Integer.toString(i);
                            int k = i + 3;
                            String j = Integer.toString(k);
                            myList.add(new SWMovie("Movie " + k, "Episode " + g));
                            swmovie = new SWMovie("Movie " + k, "Episode " + g);
                            swadapter.add(swmovie);
                        }
                                                       
                    break;
              case R.id.exit:
                     finish();
                     break;
              }
              swadapter.notifyDataSetChanged();
       }

      @Override
       public boolean onCreateOptionsMenu(Menu menu) {
               // Inflate the menu; this adds items to the action bar if it is present.
               getMenuInflater().inflate(R.menu.list_filler, menu);
               return true;
      }

}

So, if you run the application now, you see the following screen upon start-up...

Start-up screen

Click "Show Movie List" and you see...

Movie List screen

Now, click "Exit Application" and restart the application once it closes. You should once again see the start-up screen with no list. This is because we haven't saved the list. We'll do that with SQLite now...

Actually, before we begin, if you'd like to learn more about SQLite now, check out the following links:


We need to create a class that creates the database, so let's do that and call it MovieSQLiteHelper and make it extend SQLiteOpenHelper.

MovieSQLiteHelper.java
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class MovieSQLiteHelper extends SQLiteOpenHelper {
                   // name the Table
                   public static final String TABLE_MOVIES = "movies";
                   // name the Columns of the Table
                   public static final String COLUMN_ID = "_id";
                   public static final String COLUMN_MOVIE = "movie";
                   public static final String COLUMN_EPISODE = "episode";

                   private static final String DATABASE_NAME = "movies.db";
                   private static final int DATABASE_VERSION = 1;
                  
                   // Database creation declaration
                   private static final String DATABASE_CREATE = "create table "
                                           + TABLE_MOVIES + "(" + COLUMN_ID
                                           + " integer primary key autoincrement, " + COLUMN_MOVIE
                                           + " text not null, " + COLUMN_EPISODE + " text not null" + ")";
                  
                   // our object constructor
                   public MovieSQLiteHelper(Context context) {
                                     super(context, DATABASE_NAME, null, DATABASE_VERSION);
                   }
                    
                    
                   @Override
                   public void onCreate(SQLiteDatabase db) {
                                     db.execSQL(DATABASE_CREATE);

                   }

                   @Override
                   public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                                     Log.w(MovieSQLiteHelper.class.getName(),
                                             "Upgrading database from version " + oldVersion + " to "
                                                 + newVersion + ", which will destroy all old data");
                                         db.execSQL("DROP TABLE IF EXISTS " + TABLE_MOVIES);
                                         onCreate(db);

                   }

}

Again, if the above looks totally alien to you, or if you're having a hard time following the code, I encourage you to check out the links above.

Now, we're going to create a class which acts as a Data Access Object (DAO). The DAO is a database handler that manages the data and converts objects contained within the database into Java Objects. We'll call the class MoviesDataSource...

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

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;

public class MoviesDataSource {
                  
       private SQLiteDatabase database;
       private MovieSQLiteHelper dbHelper;
       private String[] allColumns = { MovieSQLiteHelper.COLUMN_ID,
                                                        MovieSQLiteHelper.COLUMN_MOVIE,     MovieSQLiteHelper.COLUMN_EPISODE };
                  
      public MoviesDataSource(Context context) {
              dbHelper = new MovieSQLiteHelper(context);
     }

     public void open() throws SQLException {
             database = dbHelper.getWritableDatabase();
    }

    public void close() {
             dbHelper.close();
   }

   public SWMovie createSWMovie(String movie, String episode) {
            ContentValues values = new ContentValues();
                      
          values.put(MovieSQLiteHelper.COLUMN_MOVIE, movie);
          values.put(MovieSQLiteHelper.COLUMN_EPISODE, episode);
          long insertId = database.insert(MovieSQLiteHelper.TABLE_MOVIES, null, values);
          Cursor cursor = database.query(MovieSQLiteHelper.TABLE_MOVIES,
                       allColumns, MovieSQLiteHelper.COLUMN_ID + " = " + insertId, null,
                        null, null, null);
         cursor.moveToFirst();
         SWMovie newSWMovie = cursorToSWMovie(cursor);
         cursor.close();
         return newSWMovie;
  }

  public void deleteSWMovie(SWMovie swmovie) {
           long id = swmovie.getId();
           System.out.println("SWMovie deleted with id: " + id);
           database.delete(MovieSQLiteHelper.TABLE_MOVIES,   MovieSQLiteHelper.COLUMN_ID
                           + " = " + id, null);
  }

  public List<SWMovie> getAllSWMovie() {
           List<SWMovie> swmovies = new ArrayList<SWMovie>();

          Cursor cursor = database.query(MovieSQLiteHelper.TABLE_MOVIES,
                       allColumns, null, null, null, null, null);
                      
         cursor.moveToFirst();
         while (!cursor.isAfterLast()) {
            SWMovie swmovie = cursorToSWMovie(cursor);
            swmovies.add(swmovie);
            cursor.moveToNext();
         }
                      
         cursor.close();
         return swmovies;
  }

  private SWMovie cursorToSWMovie(Cursor cursor) {
            SWMovie swmovie = new SWMovie();
            swmovie.setId(cursor.getLong(0));
            swmovie.setMovie(cursor.getString(1));
            swmovie.setEpisode(cursor.getString(2));
            return swmovie;
  }

}

I threw in some code so that movies can be deleted from the list, but I'll leave it as an extra exercise for you to work a 'delete movie' button into the application.

Now, we need to update the main activity to make sure the data gets saved...

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

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.ListView;

public class ListFiller extends Activity {
        ListView movieList;
        private MoviesDataSource datasource;
                  

       @Override
        protected void onCreate(Bundle savedInstanceState) {
                      super.onCreate(savedInstanceState);
                      setContentView(R.layout.activity_list_filler);
                                    
                      datasource = new MoviesDataSource(this);
                      datasource.open();
                                    
                      List myList = datasource.getAllSWMovie();
                      movieList = (ListView) findViewById(R.id.theList);
                      movieList.setAdapter(new MovieListAdapter(this, R.layout.row_layout, myList));
        }
                  
        public void onClick(View view) {
              @SuppressWarnings("unchecked")
               ListView movieList = (ListView) findViewById(R.id.theList);
               MovieListAdapter swadapter = (MovieListAdapter) movieList.getAdapter();
               SWMovie swmovie = null;
               switch (view.getId()) {
               case R.id.showList:
                     List myList = new ArrayList();
                     for (int i = 1; i < 4; i++) {
                        String g = Integer.toString(i);
                        int k = i + 3;
                        String j = Integer.toString(k);
                        myList.add(new SWMovie("Movie " + g, "Episode " + k));
                        swmovie = new SWMovie("Movie " + g, "Episode " + k);
                        swmovie = datasource.createSWMovie(swmovie.getMovie(),     
                                        swmovie.getEpisode());
                        swadapter.add(swmovie);
                    }
                    for (int i = 1; i < 4; i++) {
                        String g = Integer.toString(i);
                        int k = i + 3;
                        String j = Integer.toString(k);
                        myList.add(new SWMovie("Movie " + k, "Episode " + g));
                        swmovie = new SWMovie("Movie " + k, "Episode " + g);
                        swmovie = datasource.createSWMovie(swmovie.getMovie(),    
                                        swmovie.getEpisode());
                        swadapter.add(swmovie);
                    }
                                                       
                    break;
                    case R.id.exit:
                         finish();
                         break;
                    }
                       swadapter.notifyDataSetChanged();
                    }
                  
                   @Override
                    protected void onResume() {
                                 datasource.open();
                                 super.onResume();
                  }
                    
                 @Override
                  protected void onPause() {
                               datasource.close();
                               super.onPause();
                 }

                @Override
                public boolean onCreateOptionsMenu(Menu menu) {
                         // Inflate the menu; this adds items to the action bar if it is present.
                         getMenuInflater().inflate(R.menu.list_filler, menu);
                         return true;
               }

}

Make sure to read through all the code in ListFiller.java and understand how the database is made to store and display the data. Run the application, click "Show Movie List", click "Exit Application" after the list is displayed, and once the application is closed, restart the application. The movie list should display upon opening, which means the data has been correctly stored in the database.

If you have any questions or concerns about this tutorial, please drop a comment below. Thanks for following!

No comments:

Post a Comment