-
Notifications
You must be signed in to change notification settings - Fork 2
Tip Calc A Xamarin.Android UI project
We started with the goal of creating an app to help calculate what tip to leave in a restaurant
We had a plan to produce a UI based on this concept:
To satisfy this we built a 'Core' Portable Class Library project which contained:
- our 'business logic' -
ICalculation
- our ViewModel -
TipViewModel
- our
App
which contains the application wiring, including the start instructions.
We're now ready to add out first User Interface.
So... let's start with Android.
To create an Android MvvmCross UI, you can use the Visual Studio project template wizards, but here we'll instead build up a new project 'from empty', just as we did for the Core project.
Add a new project to your solution - a 'Blank App (Android)' application with name TipCalc.UI.Droid
Within this, you'll find the normal Android application constructs:
- the Assets folder
- the Resources folder
- the MainActivity.cs
No-one really needs an MainActivity
:)
Also, delete Main.axml
in the /resources/Layout folder.
In the Package Manager Console, enter...
Install-Package MvvmCross.HotTuna.MvvmCrossLibraries
Add a reference to your TipCalc.Core
project - the project we created in the last step which included:
- your
Calculation
service, - your
TipViewModel
- your
App
wiring.
Every MvvmCross UI project requires a Setup
class.
This class sits in the root namespace (folder) of our UI project and performs the initialisation of the MvvmCross framework and your application, including:
- the Inversion of Control (IoC) system
- the MvvmCross data-binding
- your
App
and its collection ofViewModel
s - your UI project and its collection of
View
s
Most of this functionality is provided for you automatically. Within your Droid UI project all you have to supply are:
- your
App
- your link to the business logic andViewModel
content
For TipCalc
here's all that is needed in Setup.cs:
using Android.Content;
using Cirrious.MvvmCross.Droid.Platform;
using Cirrious.MvvmCross.ViewModels;
using TipCalc.Core;
namespace TipCalc.UI.Droid
{
public class Setup : MvxAndroidSetup
{
public Setup(Context applicationContext)
: base(applicationContext)
{
}
protected override IMvxApplication CreateApp()
{
return new App();
}
}
}
This tutorial doesn't attempt to give an introduction to Android XML layout.
Instead all I'll say here is the bare minimum. If you are new to Android, then you can find out more about Android XML from lots of places including the official documentation at: http://developer.android.com/guide/topics/ui/declaring-layout.html. If you are coming from a XAML background - you are a XAMLite - then I'll include some simple XAML-AXML comparisons to help you out.
To achieve the basic layout:
-
we'll add a new AXML file -
View_Tip.axml
in the/resources/Layout
folder -
we'll edit this using either the Xamarin Android designer or the Visual Studio XML editor - the designer gives us a visual display, while the VS editor sometimes gives us XML Intellisense.
<?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"> </LinearLayout>
-
we'll add a local app namespace - http://schemas.android.com/apk/res/TipCalc.UI.Droid - this is just like adding a namespace in XAML.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:local="http://schemas.android.com/apk/res/TipCalc.UI.Droid" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> </LinearLayout>
-
notice that this 'layout' is already by default a vertical
LinearLayout
- for XAMLites, this layout is just like aStackPanel
except that it is very important to specify the vertical orientation -
within this layout we'll add some
TextView
s to provide some static text labels - for XAMLites, these are likeTextBlock
s<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="SubTotal" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Generosity" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Tip to leave" />
-
we'll also add a short, wide
View
with a yellow background to provide a small amount of chrome:<View android:layout_width="fill_parent" android:layout_height="1dp" android:background="#ffff00" />
-
we'll add some
View
s for data display and entry, and we'll databind theseView
s to properties in ourTipViewModel
-
an
EditText
for text data entry of theSubTotal
- for XAMLites, this is aTextBox
<EditText android:layout_width="fill_parent" android:layout_height="wrap_content" local:MvxBind="Text SubTotal" />
-
a
SeekBar
for touch/slide entry of theGenerosity
- for XAMLites, this is like aProgressBar
<SeekBar android:layout_width="fill_parent" android:layout_height="wrap_content" android:max="40" local:MvxBind="Progress Generosity" />
-
-
we'll add a
TextView
to display theTip
that results from the calculation:<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" local:MvxBind="Text Tip" />
Put together, this looks like:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res/TipCalc.UI.Droid"
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="SubTotal" />
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
local:MvxBind="Text SubTotal" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Generosity" />
<SeekBar
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:max="40"
local:MvxBind="Progress Generosity" />
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="#ffff00" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Tip to leave" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
local:MvxBind="Text Tip" />
</LinearLayout>
Each of the data-binding blocks within our first sample looks similar:
local:MvxBind="Text SubTotal"
What this line means is:
- data-bind:
- the property
Text
on theView
- to the property
SubTotal
on theDataContext
- which in this case will be aTipViewModel
- the property
- so:
- whenever the
TipViewModel
callsRaisePropertyChanged
onSubTotal
then theView
should update - and whenever the user enters text into the
View
then theSubTotal
value should beset
on theTipViewModel
- whenever the
Note that this TwoWay
binding is different to XAML where generally the default BindingMode
is only OneWay
.
In later topics, we'll return to show you more options for data-binding, including how to use ValueConverter
s, but for now all our binding uses this simple ViewProperty ViewModelProperty
syntax.
With our AXML layout complete, we can now add the C# Activity
which is used to display this content. For developers coming from XAML backgrounds, these Activity
classes are roughly equivalent to Page
objects in WindowsPhone on WindowsStore applications - they own the 'whole screen' and have a lifecycle which means that only one of them is shown at any one time.
To create our Activity
- which will also be our MVVM View
:
-
Create a Views folder within your TipCalc.UI.Droid project
-
Within this folder create a new C# class -
TipView
-
This class will:
-
inherit from
MvxActivity
public class TipCalcView : MvxActivity
-
be marked with the Xamarin.Android
Activity
attribute, marking it as theMainLauncher
for the project[Activity(Label = "Tip", MainLauncher=true)]
-
provide a
new ViewModel
Property to specify the type of ViewModel it expects - theTipViewModel
public new TipViewModel ViewModel { get { return (TipViewModel)base.ViewModel; } set { base.ViewModel = value; } }
-
use
OnViewModelSet
to inflate itsContentView
from the AXML - this will use a resource identifier generated by the Android and Xamarin tools.protected override void OnViewModelSet() { SetContentView(Resource.Layout.View_Tip); }
As a result this completed class is very simple:
using Android.App;
using Cirrious.MvvmCross.Droid.Views;
using TipCalc.Core;
namespace TipCalc.UI.Droid.Views
{
[Activity(Label = "Tip", MainLauncher = true)]
public class TipView : MvxActivity
{
public new TipViewModel ViewModel
{
get { return (TipViewModel) base.ViewModel; }
set { base.ViewModel = value; }
}
protected override void OnViewModelSet()
{
base.OnViewModelSet();
SetContentView(Resource.Layout.View_Tip);
}
}
}
At this point you should be able to run your application.
When it starts... you should see:
If you then want to make it 'more beautiful', then try adding a few attributes to some of your AXML - things like:
android:background="#00007f"
android:textColor="#ffffff"
android:textSize="24dp"
android:layout_margin="30dp"
android:padding="20dp"
android:layout_marginTop="10dp"
Within a very short time, you should hopefully be able to create something 'styled'...
... but actually making it look 'nice' might take some design skills!
There's more we could do to make this User Interface nicer and to make the app richer... but for this first application, we will leave it here for now.
Let's move on to Xamarin.iOS and to Windows!