Forms and Validation

In the second part of the tutorial we want to create the form to add new books to the library. We also need some mechanism to create new authors. In order to achieve all this, we need to utilize different form elements, that android provides and tweak them a little bit, to do exactly what we want.

The complete source code of this tutorial can be found here.


Creating the form

First of all we need the layout for the form itself. So what do we want to achieve? We want to enter the name of the book. Also the authors should be selectable from a list of all authors, that we know. And last but not least, we also need to buttons. One to show a dialog to create a new author and one to save the book to the database.

Entering text is easy. Android provides a class called EditText, that will do exactly, what we need. We can also create the item for the authors pretty easy, by using the Spinner class. All the other stuff in the following layout file is basically getting the items to their correct position. We use a TableLayout so that we can display a label in front of each editor and a LinearLayout to place a button at the bottom of the form.

<?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"
  android:orientation="vertical">
  
  <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1">
  
      <TableLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
      
        <TableRow
            android:layout_width="fill_parent"
            android:layout_height="wrap_content">
        
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="15dp"
                android:text="Title" />
                
            <EditText
                android:id="@+id/title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:hint="Title..." 
                android:layout_weight="1" />
        
        </TableRow>
      
        <TableRow
            android:layout_width="fill_parent"
            android:layout_height="wrap_content">
        
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:padding="15dp"
                android:text="Author" />
            
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:layout_weight="1">
                
                <Spinner
                    android:id="@+id/spinner"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content" />
                    
                <Button
                    android:id="@+id/add_author"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:text="New Author" />
            
            </LinearLayout>
        
        </TableRow>
      
      </TableLayout>
  
  </LinearLayout>

  <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="0">

      <Button
        android:id="@+id/save"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Save Book" />
        
  </LinearLayout>  
  
</LinearLayout>

Setting up the form elements

The Spinner

Now that we have created the layout, we need to implement the actual Activity. Create a plain activity and set its contentView to the layout file, we just created. For now, we don't need to change anything on the EditText widget. But we want our authors to be displayed in the Spinner. In order to achieve this, we have to first query the database for all authors. Do you think SQL right now? Forget it! This line does the job:

mAuthors = Author.objects(getApplicationContext()).all();

Unfortunately android does not support QuerySets natively ;) We need to provide the authors, as a list, that it can handle. calling toList() on the QuerySet does the job. Now we are able to set up the handler android needs in order to show the Spinner correctly.

Spinner authors = (Spinner) findViewById(R.id.spinner);
mSpinnerAdapter = new ArrayAdapter(getApplicationContext(), 
                                           R.layout.spinner_item, 
                                           mAuthors);

authors.setAdapter(mSpinnerAdapter);

Now the spinner will show all the authors, we currently have in the database. But what happens, if there are no authors in the database?

Adding authors

If we don't have an author in our database, we want to give the user the opportunity to add a new one on the fly. When a users clicks the "Add new author" button, a dialog should pop open, giving him the possibility, to enter the name of the new author. Achieving this is trickier, than you might think. We have to implement our one Dialog class. But luckily the implementation is not very hard.

First of all (as always), we need to define the layout for the dialog. As we only want a very simplistic one, it is not very large.

<?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"
    android:padding="15dp"
    android:orientation="vertical">

    <EditText
        android:id="@+id/name"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="Name..." /> 

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

    <Button
        android:id="@+id/button_ok"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Save"
        android:layout_weight="1" />

    <Button
        android:id="@+id/button_cancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Cancel"
        android:layout_weight="1" />

    </LinearLayout>
  
</LinearLayout>

Again, we define an EditText, so that the user is able to enter the name of the new author and we add two buttons, ok and cancel, giving him the opportunity to either save the new author or cancel.

public class AddAuthorDialog extends Dialog {

    public AddAuthorDialog(Context context) {
        super(context);
        setContentView(R.layout.new_author_dialog);
        
        setTitle("Add a new author");
        
        registerOkButton();
        registerCancelButton();
    }
    
    private void registerOkButton() {
        final EditText name = (EditText) findViewById(R.id.name);
        Button ok = (Button) findViewById(R.id.button_ok);
        
        ok.setOnClickListener(new View.OnClickListener() {
            
            @Override
            public void onClick(View v) {
                String value = name.getText().toString();
                
                if(value.trim().equals("")) {
                    return;
                }
                
                Author author = new Author();
                author.setName(value);
                author.save(getContext());
                    
                AddBook activity = (AddBook) getOwnerActivity();
                activity.addAuthor(author);
                
                name.setText("");
                dismiss();
            }
        });
    }
    
    private void registerCancelButton() {
        Button cancel = (Button) findViewById(R.id.button_cancel);
        cancel.setOnClickListener(new View.OnClickListener() {
            
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });
    }
    
}

In the Dialog class itself, note how the new author is being crated and saved using androrm. Also the owner activity, which is our form is notified, that a new author has been added, so it can update the Spinner.

Author author = new Author();
author.setName(value);
author.save(getContext());
    
AddBook activity = (AddBook) getOwnerActivity();
activity.addAuthor(author);

We also had to assure, that no "empty" authors are saved. You see, that we check for an empty string before we create and save the Author object.


Creating the new book

The last step is very simple. If a user clicks on the save button at the bottom of the form, a new book should be created and saved to the database. But before we blindly save books, we first have to check if an author has been selected and if the user has entered a title for the book. If the provided data is correct, we can go ahead and set-up and save the book.

Button saveBook = (Button) findViewById(R.id.save);
    saveBook.setOnClickListener(new OnClickListener() {
    
    @Override
    public void onClick(View v) {
        EditText title = (EditText) findViewById(R.id.title);
        String value = title.getText().toString();
        
        if(value.trim().equals("")) {
            showDialog(NO_TITLE);
        } else {
            if(mSelectedAuthor == null) {
                mSelectedAuthor = mSpinnerAdapter.getItem(0);
            }
            
            Book book = new Book();
            book.setTitle(value);
            book.setAuthor(mSelectedAuthor);
            book.save(getApplicationContext());
            
            resetForm();
        }
    }
});

That's it! You created the form to create new books.

androrm on github

Django is a registered trademark of the Django Foundation. Android is a registered trademark of Google Inc.