Getting Ready
The first and I think most important goal of this tutorial is to work!
I was frustrated each time, I tried one of the tutorials on the android website because 9/10 times they did not work. Google might be very good with search engines, but when it comes to writing tutorials they suck. Sorry... The other thing I really did not like was, that they just let you copy and paste code and then add only a small description. I won't do it that way. We will build this app together. Let's go!
The complete source code of this tutorial can be found here.
Before we start hacking code, we shortly think about what we want to achieve. This should be about our own little book collection. A very, very simple book collection. So what are the basic specs? I would say a book should have a
public class Author extends Model { protected CharField mName; public Author() { super(); mName = new CharField(80); } }
This now might look somewhat different from what you would normally write in Java. Probably you would have chosen String
for the name field and set it's visibility to private
. Unfortunately androrm then wouldn't be able to
- see the field and
- know how to write it to the database.
If you use the field types provided by androrm we have a different situation. What I've done here is
- set the visibility to
protected
, so that theModel
class can access this field - created a zero-argument constructor, that can be called to initialize all fields
- called
super()
in order to also set-up everything in theModel
superclass and - initialized my field providing a parameter for the maximum length.
Next up: the book model.
public class Book extends Model { protected CharField mTitle; protected ForeignKeyField<Author> mAuthor; public Book() { super(); mTitle = new CharField(80); mAuthor = new ForeignKeyField<Author>(Author.class); } }
In the Book
class we're doing basically the same. But we also define a ForeignKeyField
, that links the books to authors. This field is everything, that androrm needs, to handle this relation for you.
Actually you don't have to add anything in order to get your models ready for queries. The Model
class already defines the objects()
method, but I would say it is handy and also good style, to implement this method in every class. Normally you would have to hand in a Context
instance and the Class
object of your model. You can easily get rid of the second parameter, by implementing objects()
method, that will automatically fill in the class for you. On the author class this looks like...
public class Author extends Model { public static final QuerySet<Author> objects(Context context) { return objects(context, Author.class); } protected CharField mName; public Author() { super(); mName = new CharField(80); } }
Not much work, but looks way better in the code. I think you can figure out yourself, how to do this for the Book
class. We'll get to the details of the objects()
method and QuerySets
later.

This is, how our activity should look like, when it's finished. So as you see, we have to create three tabs and add some content to them. Luckily android gives us the TabActivity
.
As you might also have noted I was too lazy, to add three different pictures for the tabs and just used the same one for all of them. If you want to be as lazy, as I, then go ahead and save these two images.


Put these two files into the res/drawable
folder of your android project. If there is no plain drawable
folder, create it, as we will need it anyway.
In the same folder now also create a file called tab_button.xml
. This will be the standard layout file, we use for tabs. The first time I created an xml file in the drawable folder I was like WTF? What the heck is that supposed to be. But actually it is a quite smart mechanism. You can define different items, that will be used under different conditions. For the tabs that means, that we only want to see the button_selected
image, if the tab is actually selected.
In android you do this by defining states. When looking up the file, it will compare the current state with the one given in the file and stop on the first match. You don't have to define all states at all times, but only these, you care about. In our case, we want to stop the lookup, if our state is selected
and the tab is also selected. This is why the item for the selected state will come first in the file.
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/button_selected" android:state_selected="true" /> <item android:drawable="@drawable/button_deselected" /> </selector>
So now, that you have created the drawable
for your tabs, you have to create the actual tab activity. I won't describe that here in detail, as this tutorial is actually not too bad (check here). Make sure, you create three activities, called
that we will use as our different views. For now all these activities can just inherit from Activity
. We'll change this later on. Also do not forget to add all of them to the AndroidManifest.xml
! I forget this all the time and am wondering about the exception I'm getting.
The code for the plain TabActivity
looks like this:
public class Tabs extends TabActivity { /** Called when the activity is first created. **/ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // always call this before you do something else // in order to have the correct database layout syncDB(); createTab(BrowseBooks.class, "Browse Books", R.drawable.tab_button); createTab(BrowseAuthors.class, "Browse Authors", R.drawable.tab_button); createTab(AddBook.class, "Add Book", R.drawable.tab_button); getTabHost().setCurrentTab(0); } /** Creates a new tab with the given activity class as content **/ private void createTab(Class<? extends Activity> activity, String tabAlias, int drawable) { Intent intent = new Intent().setClass(this, activity); Resources res = getResources(); TabHost tabHost = getTabHost(); TabHost.TabSpec spec = tabHost .newTabSpec(tabAlias.toLowerCase().replace(" ", "_")) // this is optional .setIndicator(tabAlias, res.getDrawable(drawable)) // sets the drawable .setContent(intent); // should be self-explaning tabHost.addTab(spec); } /** Will set up the database definition **/ private void syncDB() { List<Class<? extends Model>> models = new ArrayList<Class<? extends Model>>(); models.add(Author.class); models.add(Book.class); DatabaseAdapter.setDatabaseName("books_db"); DatabaseAdapter adapter = new DatabaseAdapter(getApplicationContext()); adapter.setModels(models); } }
The only thing, that's still missing is the correct layout in the main.xml
file. I think it should be very similar to the one in the google tutorial, but I'll post it here anyway.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <!-- each tab activity has to contain one tab host this is the overall container for the tabs bar and it's contents --> <TabHost android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1"> <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <!-- these are the actual tabs or at least their container --> <TabWidget android:id="@android:id/tabs" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <!-- last but not least the container for the activities or views of each tab --> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout> </TabHost> </LinearLayout>
It's really important, that you choose the exact same ids for the objects, as android searches exactly for these, when it handles a TabActivity
. Only that way methods like getTabHost()
that we are using in the activity class can work.
One side note If you encounter the problem, that you can't read the text, because android chooses white text, for a white background and grey text for the grey background, then this is not your fault. On some phone, there seems to be an error, that produces this exact behavior. To overcome the problem add a value below 5 as targetSdkVersion
in your AndroidManifest.xml
file.
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="4" />
I'm not to sure, what exactly happens. I think some compatibility features are being disabled, if you specify targetSdkVersion
, but at least, you got nice looking tabs afterwards.
That was the first part of our tutorial. I hope, you had no trouble following the steps. If you struggled at some point please let me know, so that I can change it to the better. You should now have you models set-up and a plain tab activity, with currently no contents, but nice tabs.
To create some content, continue to the next step of the tutorial, to learn how to create a form and validate its data.