In this version of MyRent, we introduce a range of UI Widgits to evolve the UX into something more useful. These widgits will be 'active', meaning that the host Activity will be intercepting and responding to events the user may generate when interacting with the application.
At the end of this topic our goal is to have a screen looking something like this:
We will have added:
In the previous step we introduced a listener for the geolocation input (the latitude-longitude string).
In this step we shall:
Before we start to expand the project, we need to perform some rearranging so it can be extended in an orderly manner.
This is our current application workspace (ignore the 'V00' in the project name):
.. and this is a version we would like to move to (ignore the 'V01' in the project name):
This will require you to rename the two classes + the layout (use the Refactor->Rename menu). You will also need to create e new packages called:
and make sure the classes are relocated (using drag/drop) to these packages.
The previous iteration has one UI control, an EditText.
This iteration shall introduce:
Button
Open factivity_residence.xml.
A number of different approaches are available to change the layout.
In graphical display mode drag a Button onto the MyRent canvas as shown in Figure 1.
Observe the red rectangle with the number 1 inside located at the top right of the MyRent screen.
Double click on the rectangle.
A window Lint Warnings in Layout opens with a description of the warning.
<Button
android:id="@+id/registration_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
Observe the auto-generated line:
<?xml version="1.0" encoding="utf-8"?>
This is not required in the Android development target range we have chosen (API levels 16 - 19) and may, optionally, be omitted in all xml files (despite being auto-generated).
Notice also that a button id has now been generated in the R.java file:
We shall now make some changes to size and position the button:
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_width="match_parent"
Here is the refactored Button code:
<Button android:id="@+id/registration_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
/>
When you have made these changes, switch to Graphical Layout. You should be presented with that shown in Figure 4:
Next we shall add a section label and divider immediately before the geolocation node.
<string name="location">Location</string>
Here is the completed xml node for the location label:
<!-- LOCATION -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/location"
style="?android:listSeparatorTextViewStyle"/>
Implement these modifications and inspect the result in the Graphical Layout. You should be presented with that shown in Figure 5:
Finally, in this step, add a section label for status.
Here is the xml:
<!-- STATUS -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/status"
style="?android:listSeparatorTextViewStyle"
/>
Add the referenced string resource status in res/values/strings.xml:
<string name="status">Status</string>
The result is shown in Figure 6.
We shall continue with the development of the layout in the following steps.
We shall now complete remaining work on the layout using the Graphical Layout and the Outline panel.
Here is the the layout at this stage of development.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<!-- LOCATION -->
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/location" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal" >
<!-- Geolocation (GPS Coords) -->
<EditText
android:id="@+id/geolocation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/geolocation_hint" >
<requestFocus />
</EditText>
</LinearLayout>
<!-- STATUS -->
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/status" />
<Button android:id="@+id/registration_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
/>
</LinearLayout>
It remains only to add the checkbox.
With the Graphical Layout open, drag a CheckBox from the Form Widgets folder and drop directly underneath the registration_date button in the Outline panel.
Observe that the CheckBox has been assigned a default id, checkBox1. We shall change this now.
This concludes the layout design and implementation in this step.
The hierarchical arrangement of the layout is shown here in Figure 5.
Replace your Residence class with the following:
package org.wit.myrent.models;
import java.text.DateFormat;
import java.util.Date;
import java.util.UUID;
public class Residence
{
public UUID id;
public String geolocation;
public Date date;
public boolean rented;
public Residence()
{
id = UUID.randomUUID();
this.date = new Date();
}
public String getDateString()
{
return "Registered: " + DateFormat.getDateTimeInstance().format(date);
}
}
Note that we have made the fields public for convenience. Also, we have introduced a new date and rented fields into the model.
Introduce 2 new fields to access the new widgets we have just introduced:
private CheckBox rented;
private Button dateButton;
and on OnCreate, we need to initialize these:
dateButton = (Button) findViewById(R.id.registration_date);
rented = (CheckBox) findViewById(R.id.isrented);
Furthmore, we are going to disable the date button when the activity is created:
dateButton .setEnabled(false);
Run the app now, and verify that the activity launches without incident.
We would now like to engage the checkbox rented. First, implement the OnCheckedChangeListener interface:
public class ResidenceActivity extends Activity implements TextWatcher, OnCheckedChangeListener
{
This will require the following import:
import android.widget.CompoundButton.OnCheckedChangeListener;
and this is the implementation:
@Override
public void onCheckedChanged(CompoundButton arg0, boolean isChecked)
{
Log.i(this.getClass().getSimpleName(), "rented Checked");
residence.rented = isChecked;
}
This completes the class. Here is the complete code to this stage:
package org.wit.myrent.activities;
import org.wit.myrent.R;
import org.wit.myrent.models.Residence;
import android.app.Activity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.CompoundButton.OnCheckedChangeListener;
public class ResidenceActivity extends Activity implements TextWatcher, OnCheckedChangeListener
{
private EditText geolocation;
private CheckBox rented;
private Button dateButton;
private Residence residence;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_residence);
geolocation = (EditText) findViewById(R.id.geolocation);
dateButton = (Button) findViewById(R.id.registration_date);
rented = (CheckBox) findViewById(R.id.isrented);
residence = new Residence();
geolocation.addTextChangedListener(this);
geolocation.setText(residence.geolocation);
dateButton .setEnabled(false);
rented .setChecked(residence.rented);
rented .setOnCheckedChangeListener(this);
}
@Override
public void onCheckedChanged(CompoundButton arg0, boolean isChecked)
{
Log.i(this.getClass().getSimpleName(), "rented Checked");
residence.rented = isChecked;
}
@Override
public void afterTextChanged(Editable c)
{
Log.i(this.getClass().getSimpleName(), "geolocation " + c.toString());
residence.geolocation = c.toString();
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3)
{
}
@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3)
{
}
}
Run the app and check that the date string is correctly represented on the date button: see Figure 1.
Run the app and use the debugger to ensure data is being transmitted to and from the Residence object.
Repeat for the checkbox as illustrated in Figure 3.
Here is what we have achieved in this topic:
Added widgets to the layout
Added a listener in the controller to detected changes in the UI checkbox state and transmit any state changes to the model Residence object
Added a date field to the model and intialized this at the time a residence object created which represents the registration date of the property with the MyRent app.
Described how to conduct a simple test using the debugger to verify that the listeners operate correctly and that UI data transmission takes place successfully in both directions between model and UI.