Sunday, January 24, 2010

Creating multiple sqlite database tables in Android



Most of the Android database examples you will find on the web will usually contain only one table to demonstrate the basic database concepts.

That's great, the only problem with this is that most non-trivial database implementations will contain more than one table.


The standard database creation string for a single table will probably look a lot like the below:


private static final String CREATE_TABLE_1 =
" create table " + table1 +
" (_id integer primary key autoincrement," +
" title text not null, body text not null);";



Which is called in your DB Adapter class like this:


@Override
public void onCreate(SQLiteDatabase db) {

db.execSQL(CREATE_TABLE_1);
}


So what to do if you want to create more than one table?
You may do the below.. but will it work?
Note that this is one big string containing three separate create statements...


private static final String DATABASE_CREATE_MULTIPLE_TABLES =
" create table " + ITEMS_TABLE +
" (_id integer primary key autoincrement," +
" title text not null)" +

" create table " + TAGS_TABLE +
" (_id integer primary key autoincrement," +
" tagName text not null)" +

" create table " + LOCATIONS_TABLE +
" (_id integer primary key autoincrement," +
" locationName text not null, gpsCoOrds text);"
;


And then you try calling them in your DB Adapter class like so:


@Override
public void onCreate(SQLiteDatabase db) {

db.execSQL(DATABASE_CREATE_MULTIPLE_TABLES);
}


So you're trying to create the three tables in one call to db.execSQL.


This appears to compile and run successfully, you can even read and write to the FIRST table that is created, but..
..when you try to read or write to any other table you will see the dreaded

'Sorry! The application Kdkddfblah (process test.Kdkddfblah) has stopped unexpectedly. Please try again'

..error message.


Uh-oh.

If you debug your application, you might see references to syntax errors 'near create', and possible a reference that the table doesn't exist.

Hmm... What went wrong?

The answer is that sqlite, and therefore the db.exec method, only lets you execute one sql command at a time. We were trying to run three sql statements in one go in db.execSQL(DATABASE_CREATE_MULTIPLE_TABLES);.

So what you need to do to fix this is move each of the above table create statements into their own strings, like this (Note that this now creates three seperate strings, unlike above):


private static final String CREATE_TABLE_1 =
" create table " + table1 +
" (_id integer primary key autoincrement," +
" title text not null, body text not null);";

private static final String CREATE_TABLE_2 =
" create table " + TAGS_TABLE +
" (_id integer primary key autoincrement," +
" tagName text not null)";

private static final String CREATE_TABLE_3 =
" create table " + LOCATIONS_TABLE +
" (_id integer primary key autoincrement," +
" locationName text not null, gpsCoOrds text);";


.. and then use these strings in your onCreate method like below, this then works.


@Override
public void onCreate(SQLiteDatabase db) {

db.execSQL(CREATE_TABLE_1);
db.execSQL(CREATE_TABLE_2);
db.execSQL(CREATE_TABLE_3);
}


If you find this doesn't work for you, try dropping all the tables in your database and try again, or give the database a different name, or different version number. The SQLiteOpenHelper seems to have some troubles registering that the database is to be changed. It finds a db with the same name and version number, goes 'meh' and doesn't look to see if the structure is different at all.

You can also pull the sqlite db file right off your device (or emulator) by going into the DDMS perspective in Eclipse (Window menu\ Open perspective \ Other \ DDMS), navigating to the database file which will probably be at \data\data\*Your Application Name*\databases.

There's a 'pull file' button on the top left as seen highlighted below:



.. You can then open the DB in your favourite sqlite manager (I like Sqliteman) and play around. Can can also of course, push the file back to the device if you wish.




Monday, January 18, 2010

Psst..Remember that your layout files must have lowercase names..

Remember that your layout files must have lowercase names, or they won't show up in your autoComplete list of options after 'R.layout' in Eclipse when you try this :

setContentView(R.layout.test_db);


The file won't actually show up as having any errors in your package explorer (on the right by default in the IDE), but if you look down in the console (by default down the bottom), you'll see this:

Invalid file name: must contain only [a-z0-9_.]


You might see an error on your project name, but with all the folders and files it can be hard to track down the cause.

When you try to run your application you will see:

'Your project contains error(s) please fix them before running your application'


.. and it won't be happy until you delete the offending file, even if you're not actively referencing it in your code.



Hope that helps.. Happy coding!

Sunday, January 17, 2010

Errors trying to run ADB (Android Debug Bridge)?




The Android Debug Bridge (adb) is a tool lets you manage the state of an emulator instance or Android-powered device.

I was trying to run it for the first time on my ubuntu 9.10 box and I kept getting this error:


desktop:~/dev/Android/android-sdk-linux/tools$ adb
No command 'adb' found, did you mean:
Command 'cdb' from package 'tinycdb' (main)
Command 'gdb' from package 'gdb' (main)
Command 'aub' from package 'aub' (universe)
Command 'dab' from package 'bsdgames' (universe)
Command 'mdb' from package 'mono-debugger' (universe)
Command 'arb' from package 'arb' (multiverse)
Command 'tdb' from package 'tads2-dev' (multiverse)
Command 'pdb' from package 'python' (main)
Command 'jdb' from package 'openjdk-6-jdk' (main)
Command 'jdb' from package 'sun-java6-jdk' (multiverse)
Command 'ab' from package 'apache2-utils' (main)
adb: command not found


What was I doing wrong?

A quick google search shows me the error of my ways.. I haven't added my Android SDK tools directory to my system path!


It should go something like this...

open a terminal window and type:

$ echo $PATH
---(should return the directories associated with $PATH)

$ export PATH=$PATH:/home/YOUR-USERNAME/sdk/tools
---(replace with path to your tools directory, you may need to add 'sudo' to the beginning of this cmd)
Update: later versions of the SDK have ADB moved to the platform-tools directory, so adjust the above accordingly.


$ echo $PATH
---(you should now see your tools directory added to the end of the $PATH variable)

$ adb devices
---(now adb should do something, if nothing else at least error, no devices)


And now I get:

List of devices attached
emulator-5554 device


Sweet Success!

p.s. Adding to the system path in Windows is along the lines of :
  1. right-click '(My) Computer'
  2. Select 'Properties'
  3. Go to 'Advanced' or whatever tab you find 'Environment Variables'
  4. Select 'Path' then 'Edit' and add your new path in.
Update: if you are using 64-bit linux you may need to also install the ia32-libs package like so:

sudo apt-get install ia32-libs



Thursday, January 14, 2010

Including layouts: a working example

Here's a working example of including one layout inside another.

Let me know if you have any issues or questions.
This works with, and probably requires, a AVD version of 2.1 or thereabouts.



contents of droidTest1.java:

package androidforbeginners.droidTest1;

import android.app.Activity;
import android.os.Bundle;

public class droidTest1 extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}


contents of main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="combining layouts"
/>

<include android:id="@+id/cell1" layout="@layout/layout2" />
<include android:id="@+id/cell2" layout="@layout/layout3" />


</LinearLayout>



Contents of layout2.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="100px"
android:background="#0033cc"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="40px"
android:text="layout2"
/>
<CheckBox
android:layout_width="fill_parent"
android:layout_height="40px"
/>
</LinearLayout>


Contents of layout3.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="100px"
android:background="#0066cc"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="layout3"
/>
<CheckBox
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>


Output:



You could also include multiple occurrences of the one layout in your main.xml like this if you wanted:

contents of main.xml (revised):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="combining layouts"
/>

<include android:id="@+id/cell1" layout="@layout/layout2" />
<include android:id="@+id/cell2" layout="@layout/layout2" />
<include android:id="@+id/cell3" layout="@layout/layout2" />
<include android:id="@+id/cell4" layout="@layout/layout2" />


</LinearLayout>


Although if you do this, I can't see a way to reference individual repeating items.
I think include is more including a single layout across multiple Activities.

Let me know in the comments if you know a way.

Till next time: Enjoy!

Tuesday, January 12, 2010

Layout Tricks: Creating Reusable UI Components

The Android platform offers a wide variety of UI widgets, small visual construction blocks that you can glue together to present users with complex and useful interfaces. However applications often need higher-level visual components. To meet that need, and to do so efficiently, you can combine multiple standard widgets into a single, reusable component.

For example, you could create a reusable component that contains a progress bar and a cancel button, a panel containing two buttons (positive and negative actions), a panel with an icon, a title and a description, and so on. You can create UI components easily by writing a custom View, but you can do it even more easily using only XML.

In Android XML layout files, each tag is mapped to an actual class instance (the class is always a subclass of View, The UI toolkit lets you also use three special tags that are not mapped to a View instance: <requestFocus />, <merge /> and <include />. This article shows how to use <include /> to create pure XML visual components.

The <include /> element does exactly what its name suggests; it includes another XML layout. Using this tag is straightforward as shown in the following example:


<com.android.launcher.Workspace
android:id="@+id/workspace"
android:layout_width="fill_parent"
android:layout_height="fill_parent"

launcher:defaultScreen="1">

<include android:id="@+id/cell1" layout="@layout/workspace_screen" />
<include android:id="@+id/cell2" layout="@layout/workspace_screen" />
<include android:id="@+id/cell3" layout="@layout/workspace_screen" />

</com.android.launcher.Workspace>


In the <include /> only the layout attribute is required. This attribute, without the android namespace prefix, is a reference to the layout file you wish to include. In this example, the same layout is included three times in a row. This tag also lets you override a few attributes of the included layout. The above example shows that you can use android:id to specify the id of the root view of the included layout; it will also override the id of the included layout if one is defined. Similarly, you can override all the layout parameters. This means that any android:layout_* attribute can be used with the <include /> tag. Here is an example:



<include android:layout_width="fill_parent" layout="@layout/image_holder" />
<include android:layout_width="256dip" layout="@layout/image_holder" />