Пример кроссплатформенного приложения: задача

TaskyPortable — это простое приложение списка задач. В этом документе описывается, как он был разработан и построен, следуя инструкциям в документе "Создание кроссплатформенных приложений ". В обсуждении рассматриваются следующие области:

Процесс разработки

Рекомендуется создать дорожную карту для того, что вы хотите достичь, прежде чем приступить к написанию кода. Это особенно верно для кроссплатформенной разработки, где вы создаете функциональные возможности, которые будут предоставляться несколькими способами. Начиная с четкой идеи о том, что вы создаете, экономит время и усилия позже в цикле разработки.

Требования

Первым шагом в разработке приложения является определение необходимых функций. Это могут быть высокоуровневые цели или подробные варианты использования. Задача имеет простые функциональные требования:

  • Просмотр списка задач
  • Добавление, изменение и удаление задач
  • Задайте для задачи состояние "готово"

Следует рассмотреть возможность использования функций для конкретной платформы. Может ли задача воспользоваться преимуществами геозон iOS или Windows Телефон Live Tiles? Даже если вы не используете функции для конкретной платформы в первой версии, необходимо заранее спланировать, чтобы обеспечить их размещение на уровнях бизнес- и данных.

Проектирование пользовательского интерфейса

Начните с высокоуровневого дизайна, который можно реализовать на целевых платформах. Обратите внимание на ограничения пользовательского интерфейса с спецификациями платформы. Например, TabBarController в iOS может отображаться более пяти кнопок, в то время как эквивалент Windows Телефон может отображать до четырех. Нарисуйте поток экрана с помощью выбранного средства (работа бумаги).

Draw the screen-flow using the tool of your choice paper works

Модель данных

Зная, какие данные должны храниться, поможет определить, какой механизм сохраняемости следует использовать. Дополнительные сведения о доступных механизмах хранения см. в кроссплатформенных доступах к данным и помогут решить их. Для этого проекта мы будем использовать SQLite.NET.

Задача должна хранить три свойства для каждого объекта TaskItem:

  • Name — строка.
  • Заметки — строка
  • Готово — Логический

Основные функции

Рассмотрим API, который пользовательский интерфейс должен использовать для удовлетворения требований. Для списка задач требуются следующие функции:

  • Вывод списка всех задач — для отображения основного списка всех доступных задач
  • Получение одной задачи — при касании строки задачи
  • Сохранение одной задачи — при изменении задачи
  • Удаление одной задачи — при удалении задачи
  • Создание пустой задачи — при создании новой задачи

Для повторного использования кода этот API следует реализовать один раз в переносимой библиотеке классов.

Внедрение

После того как проект приложения будет согласован, рассмотрите, как оно может быть реализовано в виде кроссплатформенного приложения. Это станет архитектурой приложения. Следуя инструкциям в документе "Создание кроссплатформенных приложений" , код приложения должен быть разбит на следующие части:

  • Общий код — общий проект, содержащий повторно используемый код для хранения данных задачи; предоставляет класс модели и API для управления сохранением и загрузкой данных.
  • Код для конкретной платформы — проекты, которые реализуют собственный пользовательский интерфейс для каждой операционной системы, используя общий код в качестве серверной части.

Platform-specific projects implement a native UI for each operating system, utilizing the common code as the back end

Эти две части описаны в следующих разделах.

Общий код (PCL)

Tasky Portable использует стратегию переносимой библиотеки классов для совместного использования общего кода. См. документ "Параметры кода общего доступа" для описания параметров совместного использования кода.

Весь общий код, включая уровень доступа к данным, код базы данных и контракты, помещается в проект библиотеки.

Полный проект PCL показан ниже. Весь код в переносимой библиотеке совместим с каждой целевой платформой. При развертывании каждое собственное приложение будет ссылаться на библиотеку.

When deployed, each native app will reference that library

На схеме классов ниже показаны классы, сгруппированные по слоям. Класс SQLiteConnection является стандартным кодом из пакета Sqlite-NET. Остальные классы — это пользовательский код для Tasky. TaskItem Классы TaskItemManager представляют API, предоставляемые приложениям для конкретной платформы.

The TaskItemManager and TaskItem classes represent the API that is exposed to the platform-specific applications

Использование пространств имен для разделения слоев помогает управлять ссылками между каждым слоем. Проекты, относящиеся к платформе, должны включать using только инструкцию для бизнес-уровня. Уровень доступа к данным и уровень данных должны быть инкапсулированы API, предоставляемым TaskItemManager в бизнес-слое.

Ссылки

Переносимые библиотеки классов должны использоваться на нескольких платформах, каждый из которых имеет различные уровни поддержки функций платформы и платформы. Из-за этого существуют ограничения, на которые можно использовать пакеты и библиотеки платформы. Например, Xamarin.iOS не поддерживает ключевое слово c# dynamic , поэтому переносимая библиотека классов не может использовать любой пакет, зависящий от динамического кода, даже если такой код будет работать в Android. Visual Studio для Mac не позволит добавлять несовместимые пакеты и ссылки, но следует учитывать ограничения, чтобы избежать сюрпризов позже.

Примечание. Вы увидите, что библиотеки платформы ссылок на проекты не использовались. Эти ссылки включены в состав шаблонов проектов Xamarin. Когда приложения компилируются, процесс связывания удаляет неуправляемый код, поэтому даже если System.Xml ссылка была указана, он не будет включен в окончательное приложение, так как мы не используем какие-либо функции Xml.

Уровень данных (DL)

Уровень данных содержит код, который выполняет физическое хранилище данных , будь то база данных, неструктурированные файлы или другой механизм. Уровень данных Tasky состоит из двух частей: библиотеки SQLite-NET и пользовательского кода, добавленного для его подключения.

Задача использует пакет NuGet Sqlite-net (опубликованный Фрэнком Крюгером) для внедрения кода SQLite-NET, который предоставляет интерфейс базы данных "Сопоставление объектов-реляционных" (ORM). Класс TaskItemDatabase наследует и SQLiteConnection добавляет необходимые методы Create, Read, Update, Delete (CRUD) для чтения и записи данных в SQLite. Это простая реализация универсальных методов CRUD, которые можно повторно использовать в других проектах.

Это TaskItemDatabase одноэлементный элемент, гарантирующий, что весь доступ возникает в одном экземпляре. Блокировка используется для предотвращения параллельного доступа из нескольких потоков.

SQLite в Windows Телефон

Хотя iOS и Android оба корабля с SQLite в составе операционной системы, Windows Телефон не включает совместимый ядро СУБД. Для совместного использования кода на всех трех платформах требуется собственная версия SQLite для Windows Phone. Дополнительные сведения о настройке проекта Windows Телефон для Sqlite см. в статье "Работа с локальной базой данных".

Использование интерфейса для общего доступа к данным

Уровень данных зависит от BL.Contracts.IBusinessIdentity того, чтобы он смог реализовать абстрактные методы доступа к данным, требующие первичного ключа. Затем любой класс бизнес-уровня, реализующий интерфейс, можно сохранить на уровне данных.

Интерфейс просто задает целочисленное свойство, которое будет выступать в качестве первичного ключа:

public interface IBusinessEntity {
    int ID { get; set; }
}

Базовый класс реализует интерфейс и добавляет атрибуты SQLite-NET, чтобы пометить его как автоматически добавочный первичный ключ. Затем любой класс в бизнес-слое, реализующий этот базовый класс, можно сохранить в уровне данных:

public abstract class BusinessEntityBase : IBusinessEntity {
    public BusinessEntityBase () {}
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }
}

Пример универсальных методов на уровне данных, использующих интерфейс, — это следующий GetItem<T> метод:

public T GetItem<T> (int id) where T : BL.Contracts.IBusinessEntity, new ()
{
    lock (locker) {
        return Table<T>().FirstOrDefault(x => x.ID == id);
    }
}

Блокировка для предотвращения параллельного доступа

Блокировка реализуется в классе, чтобы предотвратить одновременный TaskItemDatabase доступ к базе данных. Это позволяет обеспечить сериализацию параллельного доступа из разных потоков (в противном случае компонент пользовательского интерфейса может попытаться считывать базу данных одновременно с обновлением фонового потока). Ниже показано, как реализована блокировка:

static object locker = new object ();
public IEnumerable<T> GetItems<T> () where T : BL.Contracts.IBusinessEntity, new ()
{
    lock (locker) {
        return (from i in Table<T> () select i).ToList ();
    }
}
public T GetItem<T> (int id) where T : BL.Contracts.IBusinessEntity, new ()
{
    lock (locker) {
        return Table<T>().FirstOrDefault(x => x.ID == id);
    }
}

Большую часть кода уровня данных можно использовать повторно в других проектах. Единственный код для конкретного приложения в слое — CreateTable<TaskItem> вызов конструктора TaskItemDatabase .

Уровень доступа к данным (DAL)

Класс TaskItemRepository инкапсулирует механизм хранения данных с строго типизированным API, который позволяет TaskItem создавать, удалять, извлекать и обновлять объекты.

Использование условной компиляции

Класс использует условную компиляцию для задания расположения файла. Это пример реализации дивергенции платформы. Свойство, возвращающее путь, компилируется в разные коды на каждой платформе. Ниже приведены директивы компилятора для конкретного кода и платформы:

public static string DatabaseFilePath {
    get {
        var sqliteFilename = "TaskDB.db3";
#if SILVERLIGHT
        // Windows Phone expects a local path, not absolute
        var path = sqliteFilename;
#else
#if __ANDROID__
        // Just use whatever directory SpecialFolder.Personal returns
        string libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); ;
#else
        // we need to put in /Library/ on iOS5.1+ to meet Apple's iCloud terms
        // (they don't want non-user-generated data in Documents)
        string documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal); // Documents folder
        string libraryPath = Path.Combine (documentsPath, "..", "Library"); // Library folder
#endif
        var path = Path.Combine (libraryPath, sqliteFilename);
                #endif
                return path;
    }
}

В зависимости от платформы выходные данные будут иметь значение "<путь к приложению/библиотека/TaskDB.db3" для iOS, "<путь> к приложению>/Documents/TaskDB.db3" для Android или "TaskDB.db3" для Windows Телефон.

Бизнес-уровень (BL)

Бизнес-слой реализует классы модели и фасад для управления ими. В Tasky модель является TaskItem классом и TaskItemManager реализует шаблон фасада для предоставления API для управления TaskItems.

Фасад

TaskItemManager упаковывает DAL.TaskItemRepository методы get, save and Delete, которые будут ссылаться на уровни приложения и пользовательского интерфейса.

Бизнес-правила и логика будут помещены здесь, если это необходимо, например любые правила проверки, которые должны быть удовлетворены перед сохранением объекта.

API для кода, зависящего от платформы

После написания общего кода пользовательский интерфейс должен быть создан для сбора и отображения данных, предоставляемых им. Класс TaskItemManager реализует шаблон Фасада для предоставления простого API для доступа к коду приложения.

Код, написанный в каждом проекте для конкретной платформы, обычно тесно связан с собственным пакетом SDK этого устройства, и доступ к общему коду с помощью API, определенного этим TaskItemManagerapi. К ним относятся методы и бизнес-классы, которые он предоставляет, например TaskItem.

Образы не совместно используются на разных платформах, но добавляются независимо в каждый проект. Это важно, так как каждая платформа обрабатывает изображения по-разному, используя разные имена файлов, каталоги и разрешения.

В остальных разделах рассматриваются сведения о реализации для конкретной платформы пользовательского интерфейса tasky.

Приложение iOS

Существует только несколько классов, необходимых для реализации приложения задач iOS с помощью общего проекта PCL для хранения и извлечения данных. Ниже показан полный проект iOS Xamarin.iOS:

iOS project is shown here

Классы показаны на этой схеме, сгруппированы на слои.

The classes are shown in this diagram, grouped into layers

Ссылки

Приложение iOS ссылается на библиотеки пакета SDK для конкретной платформы, например. Xamarin.iOS и MonoTouch.Dialog-1.

Он также должен ссылаться на TaskyPortableLibrary проект PCL. Список ссылок показан здесь:

The references list is shown here

Уровень приложений и уровень пользовательского интерфейса реализованы в этом проекте с помощью этих ссылок.

Уровень приложений (AL)

Уровень приложений содержит классы, зависящие от платформы, необходимые для привязки объектов, предоставляемых PCL пользовательскому интерфейсу. Приложение для конкретного iOS имеет два класса, которые помогают отображать задачи:

  • EditingSource — этот класс используется для привязки списков задач к пользовательскому интерфейсу. Так как MonoTouch.Dialog для списка задач используется этот вспомогательный элемент, чтобы включить функцию прокрутки для удаления в списке UITableView задач. Проводите пальцем к удалению обычно в iOS, но не в Android или Windows Телефон, поэтому конкретный проект iOS является единственным, который реализует его.
  • TaskDialog — этот класс используется для привязки одной задачи к пользовательскому интерфейсу. Он использует MonoTouch.Dialog API Рефлексия ion для "оболочки" TaskItem объекта с классом, который содержит правильные атрибуты, чтобы разрешить правильному форматированию экрана ввода.

Класс TaskDialog использует MonoTouch.Dialog атрибуты для создания экрана на основе свойств класса. Класс выглядит следующим образом:

public class TaskDialog {
    public TaskDialog (TaskItem task)
    {
        Name = task.Name;
        Notes = task.Notes;
        Done = task.Done;
    }
    [Entry("task name")]
    public string Name { get; set; }
    [Entry("other task info")]
    public string Notes { get; set; }
    [Entry("Done")]
    public bool Done { get; set; }
    [Section ("")]
    [OnTap ("SaveTask")]    // method in HomeScreen
    [Alignment (UITextAlignment.Center)]
    public string Save;
    [Section ("")]
    [OnTap ("DeleteTask")]  // method in HomeScreen
    [Alignment (UITextAlignment.Center)]
    public string Delete;
}

Обратите внимание, OnTap что атрибуты требуют имени метода. Эти методы должны существовать в классе, где MonoTouch.Dialog.BindingContext создается объект (в данном случае класс, HomeScreen рассмотренный в следующем разделе).

Уровень пользовательского интерфейса (пользовательский интерфейс)

Уровень пользовательского интерфейса состоит из следующих классов:

  1. AppDelegate — содержит вызовы API внешнего вида для стиля шрифтов и цветов, используемых в приложении. Задача — это простое приложение, поэтому в ней FinishedLaunching нет других задач инициализации.
  2. Экраны — подклассы , определяющие UIViewController каждый экран и его поведение. Экраны связывают пользовательский интерфейс с классами Уровня приложений и общим API ( TaskItemManager ). В этом примере экраны создаются в коде, но они могли бы быть разработаны с помощью конструктора интерфейсов Xcode или конструктора раскадровки.
  3. Изображения — визуальные элементы являются важной частью каждого приложения. Задача содержит изображения экрана-заставки и значка, которые для iOS должны быть предоставлены в регулярном разрешении и разрешении Сетчатки.

Home Screen

Начальный MonoTouch.Dialog экран — это экран, в котором отображается список задач из базы данных SQLite. Он наследует от DialogViewController и реализует код, чтобы задать Root коллекцию TaskItem объектов для отображения.

It inherits from DialogViewController and implements code to set the Root to contain a collection of TaskItem objects for display

Ниже перечислены два основных метода, связанных с отображением и взаимодействием со списком задач:

  1. ЗаполнениеTable — использует метод бизнес-слоя TaskManager.GetTasks для получения коллекции TaskItem объектов для отображения.
  2. Выбрано — при касании строки отображает задачу на новом экране.

Экран сведений о задаче

Сведения о задаче — это экран ввода, позволяющий изменять или удалять задачи.

Задача использует MonoTouch.DialogAPI Рефлексия ion для отображения экрана, поэтому реализация отсутствуетUIViewController. Вместо этого HomeScreen класс создает экземпляр и отображает DialogViewController класс с помощью TaskDialog класса из уровня приложения.

На этом снимке экрана показан пустой экран, демонстрирующий Entry настройку атрибута в полях "Имя " и "Заметки ":

This screenshot shows an empty screen that demonstrates the Entry attribute setting the watermark text in the Name and Notes fields

Функциональные возможности экрана сведений о задаче (например, сохранение или удаление задачи) должны быть реализованы в HomeScreen классе, так как это место MonoTouch.Dialog.BindingContext создания. HomeScreen Следующие методы поддерживают экран сведений о задаче:

  1. ShowTaskDetails — создает экран MonoTouch.Dialog.BindingContext для отрисовки. Он создает экран ввода с помощью отражения для получения имен свойств и типов из TaskDialog класса. Дополнительные сведения, такие как текст водяного знака для входных полей, реализованы с атрибутами свойств.
  2. SaveTask — этот метод ссылается в TaskDialog классе с помощью атрибута OnTap . Он вызывается при нажатии клавиши Save и используется MonoTouch.Dialog.BindingContext для извлечения введенных пользователем данных перед сохранением изменений с помощью TaskItemManager .
  3. DeleteTask — этот метод ссылается в TaskDialog классе с помощью атрибута OnTap . Он используется TaskItemManager для удаления данных с помощью первичного ключа (свойства ID).

Приложение Android

Полный проект Xamarin.Android показан ниже:

Android project is pictured here

Схема классов с классами, сгруппированных по слоям:

The class diagram, with classes grouped by layer

Ссылки

Проект приложения Android должен ссылаться на сборку Xamarin.Android для конкретной платформы, чтобы получить доступ к классам из пакета SDK для Android.

Он также должен ссылаться на проект PCL (например. TaskyPortableLibrary) для доступа к общему коду данных и бизнес-слоя.

TaskyPortableLibrary to access the common data and business layer code

Уровень приложений (AL)

Аналогично версии iOS, которую мы рассмотрели ранее, уровень приложений в версии Android содержит классы, необходимые для привязки объектов, предоставляемых Core к пользовательскому интерфейсу.

TaskListAdapter — чтобы отобразить список<объектов T> , необходимо реализовать адаптер для отображения пользовательских объектов в объекте ListView. Адаптер управляет макетом, используемым для каждого элемента в списке. В этом случае код использует встроенный макет SimpleListItemCheckedAndroid.

Пользовательский интерфейс

Уровень пользовательского интерфейса приложения Android — это сочетание кода и разметки XML.

  • Ресурсы и макет — макеты экрана и структура ячеек строк, реализованная в виде файлов AXML. AXML можно написать вручную или выложить визуально с помощью конструктора пользовательского интерфейса Xamarin для Android.
  • Resources/Drawable — изображения (значки) и настраиваемая кнопка.
  • Экраны — подклассы действий, определяющие каждый экран и его поведение. Связывает пользовательский интерфейс с классами Уровня приложений и общим API (TaskItemManager).

Home Screen

Начальный экран состоит из подкласса HomeScreen действия и HomeScreen.axml файла, который определяет макет (положение кнопки и списка задач). Экран выглядит следующим образом:

The screen looks like this

Код начального экрана определяет обработчики для нажатия кнопки и нажатия элементов в списке, а также заполнение списка в методе (таким образом, что оно отражает изменения, внесенные на OnResume экране сведений о задаче). Данные загружаются с помощью бизнес-уровня TaskItemManager и TaskListAdapter уровня приложений.

Экран сведений о задаче

Экран сведений о задаче также состоит из Activity подкласса и файла макета AXML. Макет определяет расположение входных элементов управления, а класс C# определяет поведение для загрузки и сохранения TaskItem объектов.

The class defines the behavior to load and save TaskItem objects

Все ссылки на библиотеку PCL проходят через TaskItemManager класс.

Приложение windows Телефон

Полный проект Windows Телефон:

Windows Phone App The complete Windows Phone project

На следующей схеме представлены классы, сгруппированные по слоям:

This diagram presents the classes grouped into layers

Ссылки

Проект для конкретной платформы должен ссылаться на необходимые библиотеки для конкретной платформы (напримерMicrosoft.Phone, иSystem.Windows) для создания допустимого приложения windows Телефон.

Он также должен ссылаться на проект PCL (например, TaskyPortableLibraryдля использования TaskItem класса и базы данных).

TaskyPortableLibrary to utilize the TaskItem class and database

Уровень приложений (AL)

Опять же, как и в версиях iOS и Android, слой приложений состоит из не визуальных элементов, которые помогают привязать данные к пользовательскому интерфейсу.

ViewModels

ViewModels упаковывает данные из PCL ( TaskItemManager) и предоставляет его таким образом, чтобы их можно было использовать привязкой данных Silverlight/XAML. Это пример поведения для конкретной платформы (как описано в документе кроссплатформенных приложений).

Пользовательский интерфейс

XAML имеет уникальную возможность привязки данных, которую можно объявить в разметке и уменьшить объем кода, необходимого для отображения объектов:

  1. Страницы — XAML-файлы и их кодовая функция определяют пользовательский интерфейс и ссылаться на ViewModels и проект PCL для отображения и сбора данных.
  2. Изображения — экран-заставка, фон и изображения значков являются ключевой частью пользовательского интерфейса.

MainPage

Класс MainPage использует TaskListViewModel данные для отображения данных с помощью функций привязки данных XAML. Для страницы DataContext задана модель представления, которая заполняется асинхронно. Синтаксис {Binding} в XAML определяет, как отображаются данные.

TaskDetailsPage

Каждая задача отображается путем привязки TaskViewModel xaml к XAML, определенному в taskDetailsPage.xaml. Данные задачи извлекаются с помощью TaskItemManager бизнес-уровня.

Результаты

Полученные приложения выглядят следующим образом на каждой платформе:

iOS

Приложение использует стандартный пользовательский интерфейс iOS, например кнопку "добавить", расположенную на панели навигации и используя встроенный значок плюс (+). Он также использует поведение кнопки "назад" по умолчанию и поддерживает "проводите пальцем к удалению UINavigationController " в таблице.

It also uses the default UINavigationController back button behavior and supports swipe-to-delete in the tableIt also uses the default UINavigationController back button behavior and supports swipe-to-delete in the table

Android

Приложение Android использует встроенные элементы управления, включая встроенный макет для строк, для которых требуется отображаться "галок". В дополнение к кнопке "Назад" на экране поддерживается поведение оборудования или системы.

The hardware/system back behavior is supported in addition to an on-screen back buttonThe hardware/system back behavior is supported in addition to an on-screen back button

Windows Phone

Приложение windows Телефон использует стандартный макет, заполняя панель приложений в нижней части экрана вместо панели навигации в верхней части экрана.

The Windows Phone app uses the standard layout, populating the app bar at the bottom of the screen instead of a nav bar at the topThe Windows Phone app uses the standard layout, populating the app bar at the bottom of the screen instead of a nav bar at the top

Итоги

В этом документе представлено подробное описание того, как принципы многоуровневого проектирования приложений были применены к простому приложению для упрощения повторного использования кода на трех мобильных платформах: iOS, Android и Windows Телефон.

Он описал процесс, используемый для проектирования слоев приложений, и обсудил, какие функции кода и функциональности реализованы на каждом уровне.

Код можно скачать с github.