Skip to content

Commit

Permalink
Update TipCalc Android and Core tutorial (#4616)
Browse files Browse the repository at this point in the history
* Update TipCalc Android and Core tutorial

* TipView.axml to TipView.xml

Co-authored-by: Tomasz Cielecki <tomasz@ostebaronen.dk>

---------

Co-authored-by: Artem Levin <alevin@arbus.biz>
Co-authored-by: Tomasz Cielecki <tomasz@ostebaronen.dk>
  • Loading branch information
3 people committed Jun 9, 2023
1 parent 24b2c59 commit 0efe17d
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 111 deletions.
Expand Up @@ -25,7 +25,7 @@ Same as we did with the _Core_ project, we will use a standard template to creat

## Create a new Android project

Add a new project to your solution - a 'Blank App (Android)' application with name `TipCalc.Droid`
Add a new project to your solution - a '.Net Android Application' application with name `TipCalc.Droid`

Within this, you'll find the normal Android application constructs:

Expand All @@ -37,7 +37,7 @@ Within this, you'll find the normal Android application constructs:

No-one really needs that `MainActivity` :)

Also, make sure you delete `Main.axml` in the /resources/Layout folder.
Also, make sure you delete `activity_main.xml` in the /resources/Layout folder.

## Install MvvmCross

Expand All @@ -51,53 +51,60 @@ If you don't really enjoy the NuGet UI experience, then you can alternatively op

Add a reference to your `TipCalc.Core` project - the project we created in the first step.

## Creating Setup class

Every MvvmCross UI project requires a `Setup` class. The `Setup` class is responsible for performing the initialization of the MvvmCross framework, including:

- The IoC Container and DI engine
- The Data-Binding engine
- The ViewModel / View lookups
- The whole navigation system
- Plugins

The `Setup` class is also responsible for initializing your `App` class.

Finally, let's add `Setup` class:
```c#
using Microsoft.Extensions.Logging;
using MvvmCross.Platforms.Android.Core;
using TipCalc.Core;

namespace TipCalc.Droid;

public class Setup : MvxAndroidSetup<App>
{
protected override ILoggerFactory? CreateLogFactory() => default!;

protected override ILoggerProvider? CreateLogProvider() => default!;
}
```

## Add an Android Application class

The Android Application class will allow us to specify the MvvmCross framework some key classes to be used for initialization:

```c#
using System;
using Android.App;
using Android.Runtime;
using MvvmCross.Platforms.Android.Core;
using MvvmCross.Platforms.Android.Views;
using TipCalc.Core;

namespace TipCalc.Droid
namespace TipCalc.Droid;

[Application]
public class MainApplication : MvxAndroidApplication<Setup, App>
{
[Application]
public class MainApplication : MvxAndroidApplication<MvxAndroidSetup<App>, App>
public MainApplication(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
public MainApplication(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
}
}
```

We won't go deeper into what role does MainApplication have on the Android platform, but let's talk a bit about the MvvmCross bits:

- `MvxAndroidApplication` provides some behavior for initializing the framework in runtime - although it isn't really the only way to configure the Android project.
- `MvxAndroidSetup` is the default setup MvvmCross contains. But you can use your own customized setup - sometimes it's necessary and it's a good place to initialize some 3rd party libraries on it.
- `App` here is a reference to our `TipCalc.Core.App` class.

### Some more details about the Setup class

Every MvvmCross UI project requires a `Setup` class, but if your app is fairly simple, like the TipCalc is, then you can safely use the default one, provided by the framework.

The `Setup` class is responsible for performing the initialization of the MvvmCross framework, including:

- The IoC Container and DI engine
- The Data-Binding engine
- The ViewModel / View lookups
- The whole navigation system
- Plugins

Finally, the `Setup` class is also responsible for initializing your `App` class.

Luckily for us, all this functionality is provided for you automatically, unless you want / need to use a custom `Setup` class (since it is an excellent place to register your own services / plugins, it is often the case).

## Add your View

### Add the Android Layout XML (AXML)
Expand All @@ -106,7 +113,7 @@ This tutorial doesn't attempt to give an introduction to Android XML layout, but

To achieve the basic layout that we need:

- We will add a new .axml file - called `TipView.axml` into the `/Resources/Layout` folder.
- We will add a new .xml file - called `TipView.xml` into the `/Resources/Layout` folder.

- We will edit this file using the XML editor - the designer gives us a visual display, while the VS editor **sometimes** gives us XML Intellisense. Open the file, go to the "Source" tab and replace the file content with the following code:

Expand Down Expand Up @@ -267,7 +274,7 @@ public class TipView : MvxActivity<TipViewModel>
- Add an `Activity` attribute over the class and set the `MainLauncher` property to `true`. This attribute lets Xamarin.Android add it automatically to your AndroidManifest file:

```c#
[Activity(Label = "Tip Calculator", MainLauncher = true)]
[Activity(Label = "Tip Calculator", MainLauncher = true, Theme = "@style/Theme.AppCompat")]
```

- Override the method `OnCreate` and call `SetContentView()` right after the call to base:
Expand All @@ -283,21 +290,18 @@ protected override void OnCreate(Bundle bundle)
As a result this completed class is very simple:

```c#
using Android.App;
using Android.OS;
using MvvmCross.Platforms.Android.Views;
using TipCalc.Core.ViewModels;

namespace TipCalc.Droid.Views
namespace TipCalc.Droid;

[Activity(Label = "Tip Calculator", MainLauncher = true, Theme = "@style/Theme.AppCompat")]
public class TipView : MvxActivity<TipViewModel>
{
[Activity(Label = "Tip Calculator", MainLauncher = true)]
public class TipView : MvxActivity<TipViewModel>
protected override void OnCreate(Bundle bundle)
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.TipView);
}
base.OnCreate(bundle);
SetContentView(Resource.Layout.TipView);
}
}
```
Expand All @@ -310,7 +314,7 @@ When it starts... you should see something like this:

![Android TipCalc]({{ site.url }}/assets/img/tutorials/tipcalc/TipCalc_Android.png)

If you then want to make it 'more beautiful', then try adding a few attributes to some of your .axml - things like:
If you then want to make it 'more beautiful', then try adding a few attributes to some of your .xml - things like:

```xml
android:background="#00007f"
Expand Down
131 changes: 62 additions & 69 deletions docs/_documentation/tutorials/tipcalc/the-core-project.md
Expand Up @@ -6,16 +6,16 @@ order: 2
---
MvvmCross applications normally consist on:

- A 'Core' project in the form of a .NET Standard library, which will contain all the shared code (so you want to maximize the amount of code placed in this project). The _Core_ will contain Models, ViewModels, Services, Converters, ...
- A 'Core' project in the form of a .NET Core library, which will contain all the shared code (so you want to maximize the amount of code placed in this project). The _Core_ will contain Models, ViewModels, Services, Converters, ...
- One 'Platform' project per targeted platform. These projects will contain some framework initialization code, Views and SDK dependant code.

Normally, you start development from the _Core_ project - and that's exactly what we'll do here.

Although it is recommended that you install any of the community made solution templates, we'll use a blank solution on this tutorial.

## Create the new .NET Standard library
## Create the new .NET Core library

Using Visual Studio, create your new `.NET Standard 2 Library` project using the File|New Project wizard.
Using Visual Studio, create your new `.NET Class Library` project using the File|New Project wizard. Choose `.Net 7` as project framework.

Call it something like `TipCalc.Core` and name the solution `TipCalc`.

Expand All @@ -38,26 +38,22 @@ Create a folder called `Services`.
Within this folder create a new interface, which will be used for calculating tips:

```c#
namespace TipCalc.Core.Services
namespace TipCalc.Core.Services;
public interface ICalculationService
{
public interface ICalculationService
{
double TipAmount(double subTotal, int generosity);
}
double TipAmount(double subTotal, int generosity);
}
```

Within the `Services` folder now create an implementation for the interface:

```c#
namespace TipCalc.Core.Services
namespace TipCalc.Core.Services;
public class CalculationService : ICalculationService
{
public class CalculationService : ICalculationService
public double TipAmount(double subTotal, int generosity)
{
public double TipAmount(double subTotal, int generosity)
{
return subTotal * ((double)generosity)/100.0;
}
return subTotal * ((double)generosity)/100.0;
}
}
```
Expand All @@ -83,70 +79,68 @@ So now let's create a folder called `ViewModels`, and inside of it a new class n
```c#
using MvvmCross.ViewModels;
using TipCalc.Core.Services;
using System.Threading.Tasks;

namespace TipCalc.Core.ViewModels
namespace TipCalc.Core.ViewModels;

public class TipViewModel : MvxViewModel
{
public class TipViewModel : MvxViewModel
readonly ICalculationService _calculationService;

public TipViewModel(ICalculationService calculationService)
{
readonly ICalculationService _calculationService;
_calculationService = calculationService;
}

public TipViewModel(ICalculationService calculationService)
{
_calculationService = calculationService;
}
public override async Task Initialize()
{
await base.Initialize();

public override async Task Initialize()
{
await base.Initialize();
_subTotal = 100;
_generosity = 10;

Recalculate();
}

_subTotal = 100;
_generosity = 10;
private double _subTotal;
public double SubTotal
{
get => _subTotal;
set
{
_subTotal = value;
RaisePropertyChanged(() => SubTotal);

Recalculate();
}
}

private double _subTotal;
public double SubTotal
private int _generosity;
public int Generosity
{
get => _generosity;
set
{
get => _subTotal;
set
{
_subTotal = value;
RaisePropertyChanged(() => SubTotal);

Recalculate();
}
}
_generosity = value;
RaisePropertyChanged(() => Generosity);

private int _generosity;
public int Generosity
{
get => _generosity;
set
{
_generosity = value;
RaisePropertyChanged(() => Generosity);

Recalculate();
}
Recalculate();
}
}

private double _tip;
public double Tip
private double _tip;
public double Tip
{
get => _tip;
set
{
get => _tip;
set
{
_tip = value;
RaisePropertyChanged(() => Tip);
}
_tip = value;
RaisePropertyChanged(() => Tip);
}
}

private void Recalculate()
{
Tip = _calculationService.TipAmount(SubTotal, Generosity);
}
private void Recalculate()
{
Tip = _calculationService.TipAmount(SubTotal, Generosity);
}
}
```
Expand Down Expand Up @@ -266,21 +260,20 @@ The previous line tells the MvvmCross framework that `TipViewModel` should be th
In summary, this is what App.cs should look like:

```c#
using MvvmCross;
using MvvmCross.ViewModels;
using MvvmCross;
using TipCalc.Core.Services;
using TipCalc.Core.ViewModels;

namespace TipCalc.Core
namespace TipCalc.Core;

public class App : MvxApplication
{
public class App : MvxApplication
public override void Initialize()
{
public override void Initialize()
{
Mvx.IoCProvider.RegisterType<ICalculationService, CalculationService>();
Mvx.IoCProvider.RegisterType<ICalculationService, CalculationService>();

RegisterAppStart<TipViewModel>();
}
RegisterAppStart<TipViewModel>();
}
}
```
Expand Down

0 comments on commit 0efe17d

Please sign in to comment.