Всем Андроид-разработчикам должен быть очень знаком код вроде:
private Button _myButton; public Button MyButton { get { return _myButton ?? (_myButton = this.FindViewById<Button>(Resource.Id.MyButton)); } }
Все еще пишете это руками? Используете FindViewById? Если при этом вы еще и используете Xamarin — то у меня есть решение! :)
В чем же, собственно, проблема? Вспомним, как с подобными вещами обстоит дело на других платформах. В старом-добром WinForms при добавлении на форму (Form1) кнопок или любых других элементов управления, Visual Studio создает файл дизайнера (Form1.designer.cs), в котором автогенерируется код для доступа ко всем контролам. Таким образом обращаться к кнопкам можно из Form1.cs как-то так:
button1.Text = "Some text";
В мобильной разработке для iOS с помощью Xamarin присутствует подобная же магия: при создании ViewController’ов ссылки на слинкованные контролы появляются в partial классе, и код становится очень похожим на winforms-вариант.
В Xamarin.Android, к сожалению, подобной «магии» нет, и доступ к контролам из code-behind обычно осуществляется по их идентификаторам с помощью метода FindViewById()
Например, при разметке:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <Button android:id="@+id/MyButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/Hello" /> </LinearLayout>
Чтобы назначить обработчик события для кнопки необходимо сделать следующее:
Button button = FindViewById<Button>(Resource.Id.MyButton); button.Click += delegate { button.Text = string.Format("{0} clicks!", count++); };
Возникающие при этом проблемы довольно очевидны:
- отсутствие строгой типизации (возможность указать неверный тип контрола);
- можно перепутать ID элемента и сослаться на несуществующий в текущей разметке контрол;
- приходится писать типовой код вручную;
Исправить досадное упущение и добавить .designer.cs в Xamarin.Android призвано расширение AndroidDesignerGenerator. После его установки для каждой .axml разметки шаблоном Т4 будет сгенерирован partial класс, содержащий свойства, аналогичные самому первому примеру в этой заметке.
T4-шаблон анализирует axml-разметку, и создает требуемые partial-классы. Чтобы их использовать, к вашим собственным Activity/Fragment классам тоже нужно добавить ключевое слово partial (public partial class Activity1 : Activity).
Генератор поддерживает стандартные Android-контролы, кастомные контролы при указании в разметке полного пути (вместе с namespace), а также теги include и merge.
Вместе с t4-шаблоном идет файл с настройками в формате xml, смысл которого достаточно легко можно понять:
<AndroidDesignerGenerator> <DefaultDesignerNamespace>AndroidApplication1</DefaultDesignerNamespace> <Axmls> <!-- Simple usage (will generate designers for all .axml files). Classname is the same as layout file name (without .axml), Namespace is your project's default namespace --> <Axml>.*\.axml</Axml> <!-- You can generate designers only for certain files by providing a regular expression filter --> <!-- <Axml>(.*)Activity.axml</Axml> --> <!-- You could also specify a custom namespace or class names for certain layouts. You can refer to regex matches in Namespace or ClassName tags: '$1', '$2', '$3', etc. --> <!-- <Axml Namespace="AndroidApplication1.$1" ClassName="$0">(.*)Activity.axml</Axml> --> </Axmls> </AndroidDesignerGenerator>
Все это позволит с легкостью, удобством и строгой типизацией работать с UI элементами в Андроиде.
И даже при использовании mvvm-фреймворков типа MvvmCross, где можно создавать байндинги прямо из разметки, добавление привязок через код может быть предпочтительнее, именно за счет строгой типизации. В этом случае при переименовании свойства во ВьюМодели, о сломавшемся байндинге мы узнаем уже на этапе компиляции, а не при запуске приложения и проверке всех элементов.
Установить всё это счастье можно через nuget, пакет называется AndroidDesignerGenerator. Все исходники идут прямо в t4-шаблоне, полная свобода для кастомизаций по вашему желанию :) По запросу могу выложить исходники на github для совместной работы.