In Part 1 I outlined how a MonoCross application’s models, controllers, and views work together to provide a cross-platform smartphone solution (it also supports Windows and the web but smartphones reuse is what I’m currently interested in).
The goal now is to create an Android application using Mono for Android and MonoCross that aligns with the meta-functions of navigation and data collection. Using other data collection and navigation prototypes that I’ve worked on as user interface guides, this Android application will consist of screens and a minimal workflow to support transition from screen-to-screen (barebones models and controllers will be created to support the various views).
Designing The Application
This Android application consists of seven screens:
- MainList – A list of high level functions (Navigate, Collect, Browse, Map, and Status)
- NavigateList – A list of navigation selections (Map, Waypoint, Coordinate)
- CollectList – A list of data collection selections (Point, Line, Area)
- Point – A data collection screen offering sub-screens via a tabbed interface
- Coordinate – A navigation data entry screen for inputting latitude and longitude
- Navigate – A navigation screen displaying a custom control
- Map – A Google Maps display screen
The workflow below shows specific screen-to-screen transitions (tapping “Navigate” will take you to the Navigate screen, tapping Coordinate on that screen will take you to the Coordinate data entry screen, etc.).
Creating The Solution
The development tools I used to create the Android application were:
- Visual Studio 2010 Ultimate
- Mono for Android (includes associated Android and Java SDKs)
- MonoCross Project Templates
The first step is to create the application solution. In the New Project dialog (File-New-Project), select MonoCross in the Installed Templates list and then select the MonoCross Android Application template to the right. Clicking OK creates the new solution containing the HB1.MD project. The HB1.MD project will contain the application’s models and controllers.
The next step is to add a container to the solution. In the Add New Project dialog (right-click Solution, Add-New Project), select MonoCross in the Installed Templates list and then select the MonoCross Android Container to the right. Clicking OK creates a new container project called HB1.Droid in the solution. The HB1.Droid project will contain the application’s views.
A HB1.MD project reference (green) is added to the HB1.Droid container project (yellow) and HB1.Droid is set as the startup project.
Next run the application (Debug-Start Debugging) to start the Android emulator and deploy the application to the emulator to test your work so far.
The next step is to create the models, controllers, and views that make up the application.
I’ll build the models, controllers, and views in the priority order listed – 1a, 1b, 1c, 2, 3, etcetera.
Building The List Screens
The three list screens – MainList, NavigateList, and CollectList – are simple screens that list the navigational commands that allow the user to perform various application functions. The command items in each list a contained in a list model (one for each screen) and displayed by each list screens controller.
Creating List Models
The Main, Navigate, and Collect list models are simple POCOs (Plain Old CLR Objects) that inherit from generic List<T>.
The ListItem POCO is the building block of the other three lists.
MainList is a generic List<T> of ListItems.
NavigateList is also a generic List<T> of ListItems.
And CollectList is also a generic List<T> of ListItems.
Creating List Controllers
The MainList, NavigateList, and CollectList controllers initialize and populate the respective models with data.
Registering the List Controllers
The three controllers are added to the NavigationMap along with their URI-based navigation templates.
Creating List Views
The MainList, NavigateList, and CollectList views are based on the MXListActivityView class. In each case, a string array of items is created by iterating through the specific model and passed to the listview’s adapter for screen display.
Binding List Views to Application
The three list views must be added to the Android container (lines 23-25) so they can be rendered and displayed onscreen.
Here are the three list views rendered independently on application startup (this is done by changing the controller assocatioed with NavigationMap.Add(“”, …) in App.cs).
Implementing List Navigation
Navigation from the MainList screen to the NavigateList screen or the CollectList screen is done by overriding the OnListItemClick() method in MainListView.
In this method (which is called when a list item is tapped), we get the DisplayName of the individual list item from the model (line 39) and pass it into the navigation engine (line 40).
The navigation engine uses the NavigationMap to determine which controller to call. If the Navigate item is tapped, the NavigationMap is searched for “Navigate” and, when found, the NavigateListController is instantiated, populating the NavigateList model and displaying the NavigateList view.
Building The Point Screen
Creating Point Model
The model for the Point screen is a simple POCO with a few test properties that we’ll display onscreen.
Creating Point Controller
The Point controller populates the Point model with test data when loaded.
Creating Point View
The design for the Point screen calls for a tabbed user interface. To create a tabbed UI you need to create a tab layout file, a view for each of the tabs, and a view to tie the previous three together.
The tab layout file is an XML that contains the declarative elements that specify the layout for the tabs and tab content. A tab layout file has both a TabHost and a TabWidget. The TabHost is the root node for the layout and contains a TabWidget for displaying the tabs and a FrameLayout for displaying the tab content.
This tab layout field above is referenced in line 27 of the PointView where it is set as the view’s content. We also reference two other views, one for each of the two tabs, when we create new intents and bind them to the TabHost (lines 32-39; lines 41-47).
When rendered onscreen the view will look like this.
In order to display point data onscreen, we need to create a layout file for each tab view and, inside the tab view, populate the layout fields.
Here’s the two files for Tab 1.
And what it looks like when rendered onscreen.
And here’s the two files for Tab 2.
And what it looks like when it’s rendered onscreen.
Additional information about created tabbed screens in Android can be found here.
“Wiring” Point into Application
In line 23 of App.cs, the “Point” URI is added to the NavigationMap along with its relationship to the PointController.
In line 27 of MainReceiver.cs, PointView is added to the Android container.
And in the CollectList view, the OnListItemClick() method is overriden so the selected line item (in this case “Point”) can be extracted and passed to the navigation engine.
Building The Coordinate Screen
I decided not to create a Coordinate model because it wasn’t necessary to show the transition between the Coordinate and Navigate screens (though one could be created if it was necessary to create coordinates to pass between screens).
Creating Coordinate Controller
Creating Coordinate View
Because we’re going to display some controls on the screen we need to set the content view of the view to a layout file (line 27).
Here’s the layout file that we referencing in line 27.
And here’s what the view looks like when it’s rendered on the emulator.
And on an actual device (the difference in appearance is because device-independent coordinates were not used).
“Wiring” Coordinate into Application
In line 25 of App.cs, the “Coordinate” URI is added to the NavigationMap along with its relationship to the CoordinateController.
In line 29 of MainReceiver.cs, CoordinateView is added to the Android container.
And in the NavigateList view, the OnListItemClick() method is overriden so the selected line item (in this case “Coordinate”) can be extracted and passed to the navigation engine.
Building The Navigate Screen
To create a navigation compass on the Navigate screen we first need to add a reference to OpenTK (yellow), the Android assembly that implements OpenGLES 2.0.
Creating Navigate Controller
Creating Navigate View
The content for the NavigateView is set to a Navigate layout (line 33).
In which we define a PaintingView element (lines 7-11).
Which is an class that inherits from AndroidGameView. In this view we do all of the drawing required.
To render a custom control by drawing directly onto the screen.
Obviously this is just a simple triangle rendered on the screen that doesn’t do anything, but with more time and effort it could be turned into a compass pointer that could give “as the crow flies” directions to a specific coordinate.
“Wiring” Navigate into Application
In line 27 of App.cs, the “StartNavigation” URI is added to the NavigationMap along with its relationship to the NavigateController.
In CoordinateView, a reference to the Go buttion is obtained by using FindViewById<T>. The click event of the Go button is wired to the StartNavigation() method which creates a “StartNavigation” URI and passes it to the navigation engine.
Finally, in line 31 of MainReceiver.cs, NavigateView is added to the Android container.
Building The Map Screen
To display a Google Map onscreen we need to add a reference to Mono.Android.GoogleMaps (yellow) to the container project.
Creating Map Controller
Creating Map View
The MappingView inherits from both Android’s MapActivity and MonoCross’ IMXView. Its content view is set to a MapLayout (line 32) and a reference to the MapView in the layout is obtained by using FindViewById<T> (line 34).
The MapView component in the MapLayout file contains an API key obtained from Google that allows you to access Google Maps.
And display the maps onscreen.
“Wiring” Map into Application
In line 29 of App.cs, the “Map” URI is added to the NavigationMap along with its relationship to the MapController.
In line 33 of MainReceiver.cs, MappingView is added to the Android container.
In 31 hours it was possible to create an “skeleton” Android application using the MonoCross framework and just 536 lines of custom code.