Sunday, November 3, 2013

Getting Specific Data From An SQLite Database: Android

Previously, we looked at storing and getting information using the SQLiteDatabase class in Android. In that example (see links below) we retrieved all objects contained in the database; this time we're concerned with getting specific objects. The objects in question? Books. We'll create our own mini library and limit our searches to all available books by a certain author. 'Available' meaning the ones we add to our database.

If you're not familiar with simple methods of creating and displaying lists, as well as storing and retrieving information with an SQLite database, please have a look at the following 2-part tutorial:

Populating A ListView Using SQLite: Android

We'll go with a simple layout for entering and retrieving data...

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    android:gravity="center_horizontal"
    tools:context=".MainActivity" >

    <EditText
        android:id="@+id/enterTitle"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="Enter Book Title"/>
    <EditText
        android:id="@+id/enterAuthor"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="Enter Book Author"/>
    <Button
        android:id="@+id/setBook"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Save Book"
        android:onClick="onClick"/>
   
    <EditText
        android:id="@+id/retrieveBooks"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="Author of books to retrieve"/>
   <Button
        android:id="@+id/getBooks"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Get Books"
        android:onClick="onClick"/>
  
   <ListView
        android:id="@+id/theList"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>
  
   <Button
        android:id="@+id/exit"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Exit Application"
        android:onClick="onClick"/>

</LinearLayout>

For our book objects, we create the Book class...

Book.java
public class Book {
                  private long id;
                  private String title;
                  private String author;
                 
                  public Book(String title, String author) {
                                  this.title = title;
                                  this.author = author;
                  }
                 
                  public long getId() {
                    return id;
                  }

                  public void setId(long id) {
                    this.id = id;
                  }

                  public String getTitle() {
                    return title;
                  }

                  public void setTitle(String bookTitle) {
                    title = bookTitle;
                  }
                 
                  public String getAuthor() {
                                  return author;
                  }
                 
                  public void setAuthor(String bookAuthor) {
                                  author = bookAuthor;
                  }

                  
                  @Override
                  public String toString() {
                                  return "Book [id=" + id + ", title=" + title + ", author=" + author
                                + "]";
                  }

}

So far, the code should seem pretty straightforward to you. If it doesn't, I encourage you to check out Stack Overflow and search the site for some simple tutorials on Android application development.

We're going to build a custom adapter to display our books-by-author list where each row of the list has the following layout...

row_view.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:orientation="horizontal"
        android:gravity="center">
    <TextView
        android:id="@+id/bookTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"/>
    <TextView
        android:id="@+id/bookAuthor"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"/>
    </LinearLayout>
   

</LinearLayout>

This brings us to our custom adapter...

BookListAdapter.java
import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;

public class BookListAdapter extends ArrayAdapter<Book> {
                private int  resource;
    private LayoutInflater inflater;
    private Context context;
   
    // our constructor takes 3 parameters; keep this in mind when you set the adapter.
    //  resourceId will be our row_layout resource. Context and List should be clear.
    public BookListAdapter(Context ctx, int resourceId, List<Book> objects) {
            super( ctx, resourceId, objects );
            resource = resourceId;
            inflater = LayoutInflater.from( ctx );
            context = ctx;
    }
   
    @Override
    public View getView ( int position, View convertView, ViewGroup parent ) {
                             
            /* create a new layout view and inflate it in the row */
            convertView = ( LinearLayout ) inflater.inflate( resource, null );

            /* Extract a book object to show. getItem() comes from ArrayAdapter class */
            Book book = getItem(position);
                             
            /* set book title in title TextView. getMovie is from our Book class */
            TextView txtTitle = (TextView) convertView.findViewById(R.id.bookTitle);
            txtTitle.setText(book.getTitle() + "  /  ");
                             
           /* set book author in author TextView */
           TextView txtAuthor = (TextView) convertView.findViewById(R.id.bookAuthor);
           txtAuthor.setText(book.getAuthor());
                             
           return convertView;
   }

}

Now we'll make our class MySQLiteHelper, which extends SQLiteOpenHelper. You'll notice that DATABASE_VERSION = 6. I left it that way as a way of admitting that it took me six times to get this simple application right. Don't laugh...

MySQLiteHelper.java
public class MySQLiteHelper extends SQLiteOpenHelper {
                public static final String TABLE_BOOKS = "books";
                public static final String COLUMN_ID = "_id";
                public static final String COLUMN_TITLE = "title";
                public static final String COLUMN_AUTHOR = "author";
               
                private static final String DATABASE_NAME = "books.db";
                private static final int DATABASE_VERSION = 6;
               
                // Database creation sql statement
                private static final String DATABASE_CREATE = "create table "
                        + TABLE_BOOKS + " (" + COLUMN_ID
                        + " integer primary key autoincrement, " + COLUMN_TITLE
                        + " text not null, " + COLUMN_AUTHOR + " text not null);";
                                 
                public MySQLiteHelper(Context context) {
                          super(context, DATABASE_NAME, null, DATABASE_VERSION);
                }

                @Override
                public void onCreate(SQLiteDatabase database) {
                          database.execSQL(DATABASE_CREATE);

                }

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

                }

}

Our Data Access Object (DAO) is as follows (pay attention to the getAllBooksByAuthor method; it holds the key to retrieving specific objects)...

BooksDataSource.java
public class BooksDataSource {
          // Database fields
          private SQLiteDatabase database;
          private MySQLiteHelper dbHelper;
          private String[] allColumns = { MySQLiteHelper.COLUMN_ID,
                        MySQLiteHelper.COLUMN_TITLE, MySQLiteHelper.COLUMN_AUTHOR };
                                 
          public BooksDataSource(Context context) {
                   dbHelper = new MySQLiteHelper(context);
          }
                                 
           public void open() throws SQLException {
                    database = dbHelper.getWritableDatabase();
           }
                                 
           public void close() {
                   dbHelper.close();
           }
                                 
           public Book createBook(Book book) {
                   ContentValues values = new ContentValues();
                   values.put(MySQLiteHelper.COLUMN_TITLE, book.getTitle());
                   values.put(MySQLiteHelper.COLUMN_AUTHOR, book.getAuthor());
                   long insertId = database.insert(MySQLiteHelper.TABLE_BOOKS, null,
                                                        values);
                  Cursor cursor = database.query(MySQLiteHelper.TABLE_BOOKS,
                                     allColumns, MySQLiteHelper.COLUMN_ID + " = " + insertId, null,
                                                        null, null, null);
                  cursor.moveToFirst();
                  Book newBook = cursorToBook(cursor);
                  cursor.close();
                  return newBook;
           }
                                 
           public void deleteBook(Book book) {
                  long id = book.getId();
                  System.out.println("Book deleted with id: " + id);
                 database.delete(MySQLiteHelper.TABLE_BOOKS, MySQLiteHelper.COLUMN_ID
                                                + " = " + id, null);
           }
                                 
           // THIS is the method that queries the database for books by a specific author.
           public List<Book> getAllBooksByAuthor(String bookAuthor) {
                   List<Book> books = new ArrayList<Book>();
                   
                  // This query gets the books by a specific author                                 
                  Cursor cursor = database.query(true, MySQLiteHelper.TABLE_BOOKS, allColumns, MySQLiteHelper.COLUMN_AUTHOR + "=?" , new String[]{bookAuthor},
                                                              null, null, null, null);

                  cursor.moveToFirst();
                  while (!cursor.isAfterLast()) {
                     Book book = cursorToBook(cursor);
                     books.add(book);
                     cursor.moveToNext();
                  }
                  // make sure to close the cursor
                  cursor.close();
                  return books;
          }
                                 
          private Book cursorToBook(Cursor cursor) {
                    Book book = new Book(cursor.getString(1), cursor.getString(2));
                    book.setId(cursor.getLong(0));
                    book.setTitle(cursor.getString(1));
                    book.setAuthor(cursor.getString(2));
                    return book;
         }


}

I'll leave it as an exercise for you to add a method that returns all the books in the database.

And now for the MainActivity...

MainActivity.java
public class MainActivity extends Activity {
               
         private BooksDataSource datasource;

         @Override
          protected void onCreate(Bundle savedInstanceState) {
                   super.onCreate(savedInstanceState);
                   setContentView(R.layout.activity_main);
                               
                               
                   List myList = new ArrayList();
                   ListView bookList = (ListView) findViewById(R.id.theList);
                   bookList.setAdapter(new BookListAdapter(this, R.layout.row_view, myList));
                   
                   
         }
               
         public void onClick(View view) {
         @SuppressWarnings("unchecked")
              EditText title = (EditText) findViewById(R.id.enterTitle);
              EditText author = (EditText) findViewById(R.id.enterAuthor);
              EditText booksView = (EditText) findViewById(R.id.retrieveBooks);
                   
              Button saveBook = (Button) findViewById(R.id.setBook);
              Button showBooks = (Button) findViewById(R.id.getBooks);
                   
              ListView bookList = (ListView) findViewById(R.id.theList);
              BookListAdapter bookadapter = (BookListAdapter) bookList.getAdapter();
                   
              datasource = new BooksDataSource(this);
              datasource.open();
                   
              Book newBook = null;
              switch (view.getId()) {
              case R.id.setBook:
                    List myList = new ArrayList();
                    String bookTitle = title.getText().toString();
                    String bookAuthor = author.getText().toString();
                    Book book = new Book(bookTitle, bookAuthor);
                    myList.add(book);
                    // save the new book to the database
                    newBook = datasource.createBook(book);
                    bookadapter.add(newBook);
                    title.setText("");
                    author.setText("");
                    break;
              case R.id.getBooks:
                    booksView.getText().toString();
                    List<Book> books = datasource.getAllBooksByAuthor(booksView.getText().toString());
                                bookList.setAdapter(new BookListAdapter(this, R.layout.row_view, books));
                    booksView.setText("");
                    break;
              case R.id.exit:
                    finish();
                    break;
              }
                    bookadapter.notifyDataSetChanged();
       }
               
               

      @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.main, menu);
              return true;
       }

}
Now, we're ready to run the application. Upon start-up, we have...


Enter some books (Enter title and author, then click "Save Book") to get...


Exit the application and restart it. Once it starts up, enter a books-by-author search query...



So, now we have a list of all books in our library by the author in question (Two of which we entered, and two of which were entered previously). 

So, what we set out to do has been done; we've saved books by several authors and retrieved all books by a specific author. The key to this exercise is how we coded our database query to get the specific books. I encourage you to investigate SQL queries if this new to you and, as always, please let me know if you have any questions or concerns about this tutorial. Cheers, and thanks for following!

Note:
This tutorial is based on a tutorial at Vogella.com.

1 comment:

  1. how can i edit book title or author on clicking item from listview?

    ReplyDelete