65.9K
CodeProject 正在变化。 阅读更多。
Home

MvvmCross TipCalc - 第 2 步:创建 Android UI

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (6投票s)

2013 年 3 月 23 日

Ms-PL

7分钟阅读

viewsIcon

68313

TipCalc 教程 MvvmCross v3 - Hot Tuna 的第 2 步。

介绍 

本文是 MvvmCross v3 - Hot Tuna! 的 `TipCalc` 教程的第 2 步。

迄今为止...

我们开始的目标是创建一个应用程序,以帮助计算在餐厅应该留下多少小费。

我们计划基于此概念创建一个 UI

Sketch

为了实现这一点,我们构建了一个“Core”可移植类库项目,其中包含

  • 我们的“业务逻辑” - `ICalculation`
  • 我们的 `ViewModel` - `TipViewModel`
  • 我们的 App,它包含了应用的连接逻辑,包括启动指令

所以我们现在准备好添加我们的第一个用户界面了。

那么……让我们从 Android 开始吧。

要创建一个 Android `MvvmCross` UI,你可以使用 Visual Studio 的项目模板向导,但在这里,我们将“从零开始”构建一个新项目,就像我们为核心项目所做的那样。

创建一个新的 Android UI 项目

向你的解决方案中添加一个新项目——一个名为 `TipCalc.UI.Droid` 的“`Xamarin.Android`”应用程序。

在这里面,你会找到常规的 Android 应用程序结构

  • Assets 文件夹
  • Resources 文件夹
  • Activity1.cs 文件

删除 Activity1.cs

没人真的需要一个 `Activity1`。笑脸 | <img src= " src="https://codeproject.org.cn/script/Forums/Images/smiley_smile.gif" />

另外,删除 /resources/Layout 文件夹中的 Main.axml

添加引用

添加对 CoreCross、Binding 和 MvvmCross 的引用 - PCL 版本

为新项目添加对可移植库的引用

  • Cirrious.CrossCore.dll
    • 核心接口和概念,包括跟踪、IoC 和插件管理
  • Cirrious.MvvmCross.Binding.dll
    • `DataBinding` 类 - 你将主要从 XML 中使用它
  • Cirrious.MvvmCross.dll
    • Mvvm 类 - 包括视图和 ViewModel 的基类
  • Cirrious.MvvmCross.Plugins.Json.dll
    • 添加一个 PCL `Newtonsoft.JSON.Net` 实现 - 我们的 Android UI 应用程序将使用它在 Activities (页面) 之间导航

通常,这些文件会位于类似 {SolutionRoot}/Libs/Mvx/Portable/ 的文件夹路径中。

添加对 CoreCross、Binding 和 MvvmCross 的引用 - Droid 特定版本

为新项目添加对 `Xamarin.Android` 特定库的引用

  • Cirrious.CrossCore.Droid.dll
  • Cirrious.MvvmCross.Binding.Droid.dll
  • Cirrious.MvvmCross.Droid.dll

这些库都通过 Android 特定的附加功能扩展了其 PCL 对应部分的功能。

通常,这些文件会位于类似 {SolutionRoot}/Libs/Mvx/Droid/ 的文件夹路径中。

另外,在同一个文件夹中,您还需要添加

  • System.Windows.dll - Android 版本
    • 这增加了一些核心的 PCL 适配 - 一些“类型转发”,允许 PCL 库访问像 `System.Windows.ICommand` 这样的东西,即使在 `Xamarin.Android` 上实际上并没有任何 `System.Windows`。

添加对 TipCalc.Core.csproj 的引用

添加对你的 `TipCalc.Core` 项目的引用 - 这是我们在上一步中创建的项目,它包含了

  • 你的 `Calculation` 服务
  • 您的 TipViewModel
  • 您的 App 连接

添加 MvvmCross Android 绑定资源文件

这个文件 - MvxBindingAttributes.xml - 可以在这里找到。

它需要被复制到你项目中的 /Resources/Values 文件夹。

这个文件的内容非常简单——但技术性很强。我们稍后会回来详细讨论这个文件中声明的节点和属性,以及它们如何启用声明式数据绑定。

现在,我们唯一会用到的行是核心的数据绑定属性:`MvxBind`。

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="MvxBinding">
    **<attr name="MvxBind" format="string"/>**
    <attr name="MvxLang" format="string"/>
  </declare-styleable>
  <declare-styleable name="MvxListView">
    <attr name="MvxItemTemplate" format="string"/>
    <attr name="MvxDropDownItemTemplate" format="string"/>
  </declare-styleable>
  <item type="id" name="MvxBindingTagUnique"/>
  <declare-styleable name="MvxImageView">
    <attr name="MvxSource" format="string"/>
  </declare-styleable>
</resources> 

添加 Setup 类

每个 MvvmCross UI 项目都需要一个 `Setup` 类。

这个类位于我们 UI 项目的根命名空间(文件夹)中,负责初始化 `MvvmCross` 框架和你的应用程序,包括

  • 控制反转 (IoC) 系统
  • MvvmCross 数据绑定
  • 您的 App 及其 ViewModel 集合
  • 你的 UI 项目及其视图集合

大部分功能都是自动为你提供的。在你的 `Droid` UI 项目中,你只需要提供

  • 您的 App - 您与业务逻辑和 ViewModel 内容的连接
  • 一些用于 Json.Net 插件和导航机制的初始化

对于 TipCalcSetup.cs 中需要的所有内容如下

using Android.App;
using Android.Content;
using Cirrious.MvvmCross.Droid.Platform;
using Cirrious.MvvmCross.Droid.Views;
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();
        }

        protected override IMvxNavigationSerializer CreateNavigationSerializer()
        {
            Cirrious.MvvmCross.Plugins.Json.PluginLoader.Instance.EnsureLoaded(true);
            return new MvxJsonNavigationSerializer();
        }
    }

注意:你可能想知道为什么 Json 导航是在你的 Setup 代码中初始化的,而其他大部分初始化都是自动完成的。其原因在于 MvvmCross 努力最小化对外部项目的依赖。通过不在 MvvmCross 内部引用 JSON.Net,开发者以后可以自由选择完全不同的序列化机制——例如,他们可以自由选择 `ServiceStack.Text`、`System.Xml.Serialization` 甚至是某些自定义的二进制序列化器。

添加您的 View

添加 Android 布局 XML (AXML)

本教程不打算介绍 Android XML 布局。

相反,我在这里只说最基本的内容。如果你是 Android 新手,你可以从很多地方了解更多关于 Android XML 的信息,包括官方文档。如果你有 XAML 背景——你是个 XAMLite——那么我会提供一些简单的 XAML-AXML 对比来帮助你。

为了实现基本布局

  • 我们将在 /resources/Layout 文件夹中添加一个新的 AXML 文件 - View_Tip.axml

  • 我们将使用 Xamarin Android 设计器或 Visual Studio XML 编辑器来编辑它——设计器为我们提供可视化显示,而 VS 编辑器有时会提供 XML 智能感知。

    <?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>
  • 我们将添加一个本地应用命名空间 - http://schemas.android.com/apk/res/TipCalc.UI.Droid - 这就像在 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>
  • 注意这个“布局”默认已经是一个垂直的 `LinearLayout`——对于 XAMLites 来说,这个布局就像一个 `StackPanel`,但非常重要的是要指定垂直方向。

  • 在这个布局中,我们将添加一些 `TextViews` 来提供一些 `static` 文本标签——对于 XAMLites 来说,这就像 `TextBlocks`。

    <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" />
  • 我们还会添加一个短而宽的 `View`,背景为黄色,以提供一点装饰。

    <View
        android:layout_width="fill_parent"
        android:layout_height="1dp"
        android:background="#ffff00" />
  • 我们将添加一些用于数据显示和输入的 `View`,并且我们将把这些 `View` 数据绑定到我们 `TipViewModel` 中的属性上。

    • 一个用于输入 `SubTotal` 文本数据的 `EditText` - 对于 XAMLites 来说,这是一个 `TextBox`。

      <EditText
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          local:MvxBind="Text SubTotal" />
    • 一个用于通过触摸/滑动输入 Generosity 的 `SeekBar` - 对于 XAMLites 来说,这就像一个 `ProgressBar`。

      <SeekBar
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:max="40"
          local:MvxBind="Progress Generosity" />
    • 我们将添加一个 `TextView` 来显示计算得出的小费

      <TextView
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          local:MvxBind="Text Tip" />

综合起来,它看起来像这样

<?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>

关于数据绑定语法

我们第一个示例中的每个数据绑定块看起来都差不多

local:MvxBind="Text SubTotal"

这行代码的意思是

  • 数据绑定
    • `View` 上的 `Text` 属性
    • 到 `DataContext` 上的 `SubTotal` 属性——在这里 `DataContext` 将是 `TipViewModel`
  • 因此
    • 每当 `TipViewModel` 对 `SubTotal` 调用 `RaisePropertyChanged` 时,`View` 就应该更新
    • 并且每当用户在 `View` 中输入文本时,`SubTotal` 的值就应该被设置到 `TipViewModel` 上

注意,这种 `TwoWay`(双向)绑定与 XAML 不同,在 XAML 中,默认的 `BindingMode` 通常只是 `OneWay`(单向)。

在后面的主题中,我们将回头向你展示更多数据绑定的选项,包括如何使用 `ValueConverters`,但目前我们所有的绑定都使用这种简单的 `ViewProperty ViewModelProperty` 语法。

添加 View 类

在 AXML 布局完成后,我们现在可以添加用于显示此内容的 C# `Activity`。对于有 XAML 背景的开发者来说,这些 `Activity` 类大致相当于 WindowsPhone 或 WindowsStore 应用程序中的 `Page` 对象——它们拥有“整个屏幕”,并有一个生命周期,这意味着任何时候只有一个 `Activity` 会被显示。

要创建我们的 `Activity` - 它也将是我们的 Mvvm View

  • 在你的 `TipCalc.UI.Droid` 项目中创建一个 Views 文件夹

  • 在这个文件夹中,创建一个新的 C# 类 - `TipView`

  • 这个类将

    • 继承自 `MvxActivity`

      public class TipCalcView : MvxActivity
    • 用 `Xamarin.Android Activity` 特性标记,将其标记为项目的主启动器 (`MainLauncher`)

      [Activity(MainLauncher=true)]
    • 提供一个新的 `ViewModel` 属性来指定它期望的 `ViewModel` 类型——即 `TipViewModel`

      public new TipViewModel ViewModel
      {
          get { return (TipViewModel)base.ViewModel; }
          set { base.ViewModel = value; }
      }
    • 使用 `OnViewModelSet` 从 AXML 加载其 `ContentView`——这将使用由 Android 和 Xamarin 工具生成的资源标识符。

      protected override void OnViewModelSet()
      {
          SetContentView(Resource.Layout.View_Tip);
      }

因此,这个完整的类非常简单

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);
        }
    }
}

Android UI 完成了!

此时,您应该能够运行您的应用程序。

当它启动时...您应该看到

v1

如果你想让它“更漂亮”,可以尝试在你的 AXML 中添加一些属性——比如

    android:background="#00007f"
    android:textColor="#ffffff"
    android:textSize="24dp"
    android:layout_margin="30dp"
    android:padding="20dp"
    android:layout_marginTop="10dp"

在很短的时间内,你应该可以创建一个“有样式”的东西……

v2

……但要让它看起来“漂亮”可能需要一些设计技巧!

继续

我们还可以做更多的事情来让这个用户界面更美观,让应用更丰富……但对于这第一个应用程序,我们暂时就到这里。

让我们继续 `Xamarin.iOS` 和 Windows!

文章

历史 

  • 2013年3月22日 - 首次提交。
© . All rights reserved.