Lecture Presentation

Goals

  • Store simple values persistantly
  • Know some Java structures for storing multiple items of data
  • Use a SQLite database for storing more data
  • Display lists of data using ListView and adapters

SharedPreferences to store simple values

  • Store a single primitive type or a String persistantly
  • A good time
    • To write the value is whenever the value changes
    • To read the value is in the onCreate method of the Activity
  • Write a value
    String name = "Mike";
    SharedPreferences.Editor storage = 
      getSharedPreferences("mydata", MODE_PRIVATE).edit();
    storage.putString("name", name);
    storage.commit();
    
  • Read a value
    SharedPreferences storage = getSharedPreferences("mydata");
    String name = storage.getString("name", "");
    

Static arrays in Java

  • Arrays are an important part of any programming language for handling multiple instances of data
  • Declare and initialize a static Array
    String[] names = new String[] { "Adam", "Betty", "Carl", "Donna" };
    int nameCount = names.length;
    
  • Iterate through all values in an Array
    for (int index = 0; index < names.length; index++) {
      Log.i("myapp", "Welcome " + names[index]);
    }
    
  • Static arrays can change value at existing index, but can't add or remove values

Dynamic arrays in Java

  • Can add and remove values during the array's lifetime
  • Declare and add values
    ArrayList<String> names = new ArrayList<String>();
    names.add("Adam");
    names.add("Betty");
    names.addAll(Arrays.asList("Carl", "Donna"));
    
  • Iterate through all values in an ArrayList
    for (int index = 0; index < names.size(); index++) {
      Log.i("myapp", "Welcome " + names.get(i));
    }
    
  • Find and remove a value
    int found = names.indexOf("Carl");
    if (found>=0)
      names.remove(found);
    

Lists on mobile devices

  • Lists of items displayed in the user interface are of high importance and use on mobile devices
  • Both Android and iOS have specific feature for dealing with long lists
  • Virtual lists
    • Not efficient on mobile to have all data items in a list/table transformed into visual elements in the user interface
    • A virtual list is a feature where the system handles scrolling and knowing exactly which data items is enough to be visible at any particular point in time
    • Items not visible any more (from scrolling etc) are automatically freed from memory
    • Visual elements can be reused when scrolling to new data items so they don't have to be recreated repeatedly

Objects for virtual lists

  • ListView
    • The visual element in the user interface that handles display, scrolling etc
    • Knows which items in the list are currently visible on screen
    • Never extended or customized by you
  • Data source - for example an ArrayList<String>
    • Some sort of data storage mechanism that is capable of storing multiple items of data
    • Never handled directly by the ListView
    • Use a built in data storage class/concept
  • Adapter - connects the ListView to the data
    • Abstraction of the data source so that the same ListView can handle many different types of data storage
    • ListAdapter base class is extended into specific adapters for some types of data storage
    • Often extended and customized by you to give you full control of how your data items are displayed

Built-in resources for ListAdapters

  • Specify a layout xml file
    • android.R.layout.simple_list_item_1
      • Built-in layout with a single TextView
    • android.R.layout.simple_list_item_2
      • Built-in layout with one larger and one smaller TextView below each other
  • Specify id of TextView inside of layout
    • android.R.id.text1
      • Single TextView in android.R.layout.simple_list_item_1 or the larger first TextView in android.R.layout.simple_list_item2
    • android.R.id.text2
      • Second smaller TextView in android.R.layout.simple_list_item_2

Adapter choices

  • Data in an array, like ArrayList<String>
    • Use an ArrayAdapter<String>. Can also handle the storage, if you don't want to have a separate object for storing the individual items in the array.
  • Data in an ArrayList of HashMaps, like ArrayList<HashMap<String, String>>
    • Use a SimpleAdapter. Can map values from key of String in HashMap to R.id in layout.
  • Data in a SQLite database
    • Use a SimpleCursorAdapter.

Show a single string as ListView item

  • ArrayAdapter
    • Handle an array of strings and show the string as the ListView item
    • Choice of letting the adapter store the strings too, or keep them in a separate ArrayList
    • Actually can contain any object type and will use toString to get the text to display for a single item
  • Example
    ArrayAdapter<String> data = new ArrayAdapter<String>(this, 
      android.R.layout.simple_list_item_1);
    

Hashes in Java

  • Hashes are also a common storage object
    • Values are accessed with a key and not just a sequential index
    • Keys are stored in such a way that finding a specific key's value is very fast
    • Can also be called maps, hashtables, dictionaries etc
  • Declare and add values
    HashMap<String, String> capitals = new HashMap<String, String>();
    capitals.put("Sweden", "Stockholm");
    capitals.put("Norway", "Oslo");
    
  • Iterate through all values
    for (String country : capitals.keySet()) {
      Log.i("myapp", "The capital of " + country + " is " + capitals.get(country));
    }
    
  • Remove a value by key
    capitals.remove("Denmark");
    

SQLite databases

  • For storing larger amounts of data, a database is your best option
    • Not all data has to be in memory at any one point in time
    • Easy to sort and filter the data
  • Knowledge needed
    • Basic understanding of tables, columns, rows, relations/joins
    • You should know some about the SQL language (SQL92 to be specific)
    • SELECT * FROM Contacts WHERE City='London' ORDER BY LastName
  • Compatability
    • SQLite database files / file format is the same on many platforms
    • You can create a SQLite database on desktop and read it inside of an Android app
    • You can export your Android SQLite database to desktop and read/modify it
    • iOS also uses SQLite as its standard format for databases

About SQLite

  • Open source library for single file database
    • Is threadsafe, meaning multiple applications can access the database file simultaneously
  • All column values are stored as text
    • SQLite will convert when you specify the type
    • No size of column string values either
    • Integer, Text
  • Identifier columns

Use SQLite on Android

  • Create your own class extended from SQLiteOpenHelper
  • Override onCreate to create the tables of the database
  • Create object of this class in your Activity
  • Get database access by calling getWritableDatabase
  • Now you can
    • Read data using rawQuery or query that returns a Cursor
    • Add data using insert and a collection of values in a ContentValues object
    • Update data using update and a ContentValues object
    • Delete data using delete
    • Execute other SQLite statements using execute

Example

  • All tables should have a column named "_id" as a unique numeric identifier
    • There are a few places in the Android platform where this is important, or at least helpful.
  • Create your own class extended from SQLiteOpenHelper
    public class MyDatabase extends SQLiteOpenHelper {
      public MyDatabase(Context context) {
        super(context, "mydb", null, 1);
      }
      @Override
      public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE Items (_id integer primary key autoincrement,
          name text, description text, level integer)");
      }
    }
    
  • Use your class in the Activity
    MyDatabase _mydata;
    _mydata = new MyDatabase(this);
    SQLiteDatabase db = _mydata.getWritableDatabase();
    

When you have the database object

  • Read all rows from a table
    Cursor table = db.rawQuery("SELECT * FROM Items ORDER BY name");
    while (table.moveNext()) {
      String name = table.getString("name");
      //...
    }
    table.close();
    db.close();
    
  • Read a single row from a table
    Cursor row = db.rawQuery("SELECT * FROM Items WHERE name='Apple'");
    // Must always call moveNext to enter the row, even if just one
    if (row.moveNext()) {
      int columnIndex = row.getColumnIndex("price");
      int price = row.getInt(columnIndex);
      Toast.makeText(this, "Price of Apple is "+Integer.toString(price), Toast.LENGTH_SHORT).show();
    }
    else {
      Toast.makeText(this, "Sorry, no Apples here!", Toast.LENGTH_SHORT).show();
    }
    

Update and delete in a database table

  • Add a row to the table
    ContentValues row = new ContentValues();
    row.put("name", "Adam");
    db.insert("Items", null, row);
    
  • Update a row in the table
    int id = 5; // Assume we know the id of the row to update
    ContentValues row = new ContentValues();
    row.put("name", "Alice");
    db.update("Items", row, String.format("_id=%d", id), null);
    
  • Delete a row in the table
    int id = 5; // Assume we know the id of the row to delete
    db.delete("Items", String.format("_id=%d", id), null);
    

What if you need to add a column to a table?

  • Adding a new column to a SQLite database table without destroying existing data
    • Increase the version number in your SQLiteOpenHelper extended class
    • Implement the onUpdate method in your SQLiteOpenHelper extended class
    • Use the ALTER TABLE ADD COLUMN statement to add the column inside the onUpdate method
  • Example
    @Override
      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (oldVersion<=1)
          db.execSQL("ALTER TABLE Items ADD COLUMN level integer");
    }
    

Show SQLite data in ListView

  • Retrieve a Cursor with the data from the SQLite database
  • Use a SimpleCursorAdapter for the ListView
    • Specify the open Cursor from a rawQuery or query call on the database
    • Specify an xml layout to use for the individual items
    • Specify an array of column names to match to the int indentifiers
    • Specify an array of int identifiers inside the xml layout for the column names
  • Example
    SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, 
      cursor, android.R.layout.simple_list_item_2,
      new String[] { "name", "desc" }, 
      new int[] { android.R.id.text1, android.R.id.text2 });
    

Working with SimpleCursorAdapter

  • When the data in the SQLite database has changed
    • Requery the Cursor and the ListView will be updated via the SimpleCursorAdapter
  • Either
    • Save the Cursor in a class member so you can call requery on it
    • Get the Cursor from the SimpleCursorAdapter of the ListView
  • Example
    ((SimpleCursorAdapter)_list.getAdapter()).getCursor().requery();
    
  • Note that these API calls are marked deprecated
    • Reason is that in a performant app, you should not to lengthy database operations (like query/requiery) on the main UI thread

Show customized ListView item

  • Create your own class that extends SimpleCursorAdapter
  • Override the bindView method
    • Called by framework when an item view should be filled with data
    • The view at hand might have been used previously with other data
    • Don't assume empty values, reset/empty each time
  • Example
    @Override
    public void bindView(View view, Context context, Cursor cursor) {
      super.bindView(view, context, cursor);
      TextView text = (TextView)view.findViewById(android.R.id.text2);
      int columnIndex = cursor.getColumnIndex("price");
      text.setText(String.format("%d SEK", cursor.getInt(columnIndex)));
    }
    

ListView events

  • Clicking and long-clicking on individual items
  • OnItemClickListener
    • When user touches on an item in the list
  • OnItemLongClickListener
    • When user touches and holds on for a period of time, on an item in the list
  • Implement event handler
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    }
    
  • Arguments
    • AdapterView<?> is the adapter for the ListView
    • View is the visual element container for the particular item that was clicked
    • int is the position of the item that was clicked (first is zero)
    • long is the identifier of a SQLite database table row, if a CursorAdapter was used

More information