Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В рамках сборки .NET для Android ресурсы Android обрабатываются, предоставляя идентификаторы Android через созданную _Microsoft.Android.Resource.Designer.dll сборку.
Например, учитывая файл Reources\layout\Main.axml с содержимым:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android">
<Button android:id="@+id/myButton" />
<fragment
android:id="@+id/log_fragment"
android:name="commonsamplelibrary.LogFragment"
/>
<fragment
android:id="@+id/secondary_log_fragment"
android:name="CommonSampleLibrary.LogFragment"
/>
</LinearLayout>
Затем во время сборки создается сборка _Microsoft.Android.Resource.Designer.dll с содержимым, похожим на следующее:
namespace _Microsoft.Android.Resource.Designer;
partial class Resource {
partial class Id {
public static int myButton {get;}
public static int log_fragment {get;}
public static int secondary_log_fragment {get;}
}
partial class Layout {
public static int Main {get;}
}
}
Традиционно взаимодействие с ресурсами выполняется в коде с помощью констант из Resource типа и FindViewById<T>() метода:
partial class MainActivity : Activity {
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.Main);
Button button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate {
button.Text = $"{count++} clicks!";
};
}
}
Начиная с Xamarin.Android 8.4, существует два дополнительных способа взаимодействия с ресурсами Android при использовании C#:
Чтобы включить эти новые функции, задайте
$(AndroidGenerateLayoutBindings) Свойство MSBuild в True командной строке msbuild:
dotnet build -p:AndroidGenerateLayoutBindings=true MyProject.csproj
или в вашем .csproj-файле:
<PropertyGroup>
<AndroidGenerateLayoutBindings>true</AndroidGenerateLayoutBindings>
</PropertyGroup>
Привязки
Привязка — это созданный класс, один на файл макета Android, который содержит строго типизированные свойства для всех идентификаторов в файле макета. Типы привязки создаются в пространстве имен global::Bindings с именами типов, которые соответствуют именам файлов макета.
Типы привязки создаются для всех файлов макета, содержащих все идентификаторы Android.
Учитывая файл макета Android Resources\layout\Main.axml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xamarin="http://schemas.xamarin.com/android/xamarin/tools">
<Button android:id="@+id/myButton" />
<fragment
android:id="@+id/fragmentWithExplicitManagedType"
android:name="commonsamplelibrary.LogFragment"
xamarin:managedType="CommonSampleLibrary.LogFragment"
/>
<fragment
android:id="@+id/fragmentWithInferredType"
android:name="CommonSampleLibrary.LogFragment"
/>
</LinearLayout>
затем будет создан следующий тип:
// Generated code
namespace Binding {
sealed class Main : global::Xamarin.Android.Design.LayoutBinding {
[global::Android.Runtime.PreserveAttribute (Conditional=true)]
public Main (
global::Android.App.Activity client,
global::Xamarin.Android.Design.OnLayoutItemNotFoundHandler itemNotFoundHandler = null)
: base (client, itemNotFoundHandler) {}
[global::Android.Runtime.PreserveAttribute (Conditional=true)]
public Main (
global::Android.Views.View client,
global::Xamarin.Android.Design.OnLayoutItemNotFoundHandler itemNotFoundHandler = null)
: base (client, itemNotFoundHandler) {}
Button __myButton;
public Button myButton => FindView (global::Xamarin.Android.Tests.CodeBehindFew.Resource.Id.myButton, ref __myButton);
CommonSampleLibrary.LogFragment __fragmentWithExplicitManagedType;
public CommonSampleLibrary.LogFragment fragmentWithExplicitManagedType =>
FindFragment (global::Xamarin.Android.Tests.CodeBehindFew.Resource.Id.fragmentWithExplicitManagedType, __fragmentWithExplicitManagedType, ref __fragmentWithExplicitManagedType);
global::Android.App.Fragment __fragmentWithInferredType;
public global::Android.App.Fragment fragmentWithInferredType =>
FindFragment (global::Xamarin.Android.Tests.CodeBehindFew.Resource.Id.fragmentWithInferredType, __fragmentWithInferredType, ref __fragmentWithInferredType);
}
}
Базовый тип привязки Xamarin.Android.Design.LayoutBindingне является частью библиотеки классов .NET для Android, но поставляется с .NET для Android в виде исходного кода и включается в сборку приложения автоматически при каждом использовании привязок.
Созданный тип привязки может быть построен вокруг Activity экземпляров, что позволяет строго типизированный доступ к идентификаторам в файле макета.
// User-written code
partial class MainActivity : Activity {
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.Main);
var binding = new Binding.Main (this);
Button button = binding.myButton;
button.Click += delegate {
button.Text = $"{count++} clicks!";
};
}
}
Типы привязки также могут создаваться вокруг View экземпляров, позволяя строго типизированный доступ к идентификаторам ресурсов в представлении или его дочерних элементах:
var binding = new Binding.Main (some_view);
Отсутствующие идентификаторы ресурсов
Свойства типов привязки по-прежнему используют FindViewById<T>() в своей реализации. Если FindViewById<T>() возвращает null, то поведение по умолчанию заключается в том, чтобы свойство выбрасывало InvalidOperationException вместо возврата null.
Это поведение по умолчанию может быть переопределено путем передачи делегата обработчика ошибок созданной привязке при ее создании.
// User-written code
partial class MainActivity : Activity {
Java.Lang.Object? OnLayoutItemNotFound (int resourceId, Type expectedViewType)
{
// Find and return the View or Fragment identified by `resourceId`
// or `null` if unknown
return null;
}
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.Main);
var binding = new Binding.Main (this, OnLayoutItemNotFound);
}
}
Метод OnLayoutItemNotFound() вызывается, когда не удалось найти идентификатор ресурса для View или Fragment.
Обработчик должен возвращать либо null, в этом случае будет выброшено InvalidOperationException, или, предпочтительно, возвращать экземпляр View или Fragment, соответствующий идентификатору, переданному обработчику. Возвращаемый объект должен иметь правильный тип, соответствующий типу соответствующего свойства Binding. Возвращаемое значение приводится к такому типу, поэтому если объект неправильно типизирован, исключение будет выброшено.
Code-Behind
Code-Behind включает создание partial класса во время сборки, содержащего строго типизированные свойства для всех идентификаторов в файле макета .
Code-Behind основан на механизме привязки, требуя, чтобы файлы макета активировали генерацию Code-Behind, используя новый XML-атрибут xamarin:classes, который является списком, разделенным с помощью ;, полных имен классов, которые необходимо создать.
Учитывая файл Resources\layout\Main.axmlмакета Android:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xamarin="http://schemas.xamarin.com/android/xamarin/tools"
xamarin:classes="Example.MainActivity">
<Button android:id="@+id/myButton" />
<fragment
android:id="@+id/fragmentWithExplicitManagedType"
android:name="commonsamplelibrary.LogFragment"
xamarin:managedType="CommonSampleLibrary.LogFragment"
/>
<fragment
android:id="@+id/fragmentWithInferredType"
android:name="CommonSampleLibrary.LogFragment"
/>
</LinearLayout>
Во время сборки будет создан следующий тип:
// Generated code
namespace Example {
partial class MainActivity {
Binding.Main __layout_binding;
public override void SetContentView (global::Android.Views.View view);
void SetContentView (global::Android.Views.View view,
global::Xamarin.Android.Design.LayoutBinding.OnLayoutItemNotFoundHandler onLayoutItemNotFound);
public override void SetContentView (global::Android.Views.View view, global::Android.Views.ViewGroup.LayoutParams @params);
void SetContentView (global::Android.Views.View view, global::Android.Views.ViewGroup.LayoutParams @params,
global::Xamarin.Android.Design.LayoutBinding.OnLayoutItemNotFoundHandler onLayoutItemNotFound);
public override void SetContentView (int layoutResID);
void SetContentView (int layoutResID,
global::Xamarin.Android.Design.LayoutBinding.OnLayoutItemNotFoundHandler onLayoutItemNotFound);
partial void OnSetContentView (global::Android.Views.View view, ref bool callBaseAfterReturn);
partial void OnSetContentView (global::Android.Views.View view, global::Android.Views.ViewGroup.LayoutParams @params, ref bool callBaseAfterReturn);
partial void OnSetContentView (int layoutResID, ref bool callBaseAfterReturn);
public Button myButton => __layout_binding?.myButton;
public CommonSampleLibrary.LogFragment fragmentWithExplicitManagedType => __layout_binding?.fragmentWithExplicitManagedType;
public global::Android.App.Fragment fragmentWithInferredType => __layout_binding?.fragmentWithInferredType;
}
}
Это позволяет использовать более интуитивно понятные идентификаторы ресурсов в макете:
// User-written code
partial class MainActivity : Activity {
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.Main);
myButton.Click += delegate {
button.Text = $"{count++} clicks!";
};
}
}
Обработчик ошибок OnLayoutItemNotFound можно передать в качестве последнего параметра в любой перегрузке SetContentView, используемой действием:
// User-written code
partial class MainActivity : Activity {
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.Main, OnLayoutItemNotFound);
}
Java.Lang.Object? OnLayoutItemNotFound (int resourceId, Type expectedViewType)
{
// Find and return the View or Fragment identified by `resourceId`
// or `null` if unknown
return null;
}
}
Так как Code-Behind использует частичные классы, все объявления частичного класса должны использоваться partial class в объявлении, в противном случае ошибка компилятора C# CS0260 будет создана во время сборки.
Настройка
Созданный тип кода всегда переопределяет Activity.SetContentView(), и по умолчанию он всегда вызывает base.SetContentView(), пересылая параметры. Если это не нужно, следует переопределить один из OnSetContentView()partial методов, чтобы установить callBaseAfterReturn в false.
// Generated code
namespace Example
{
partial class MainActivity {
partial void OnSetContentView (global::Android.Views.View view, ref bool callBaseAfterReturn);
partial void OnSetContentView (global::Android.Views.View view, global::Android.Views.ViewGroup.LayoutParams @params, ref bool callBaseAfterReturn);
partial void OnSetContentView (int layoutResID, ref bool callBaseAfterReturn);
}
}
Пример созданного кода
// Generated code
namespace Example
{
partial class MainActivity {
Binding.Main? __layout_binding;
public override void SetContentView (global::Android.Views.View view)
{
__layout_binding = new global::Binding.Main (view);
bool callBase = true;
OnSetContentView (view, ref callBase);
if (callBase) {
base.SetContentView (view);
}
}
void SetContentView (global::Android.Views.View view, global::Xamarin.Android.Design.LayoutBinding.OnLayoutItemNotFoundHandler onLayoutItemNotFound)
{
__layout_binding = new global::Binding.Main (view, onLayoutItemNotFound);
bool callBase = true;
OnSetContentView (view, ref callBase);
if (callBase) {
base.SetContentView (view);
}
}
public override void SetContentView (global::Android.Views.View view, global::Android.Views.ViewGroup.LayoutParams @params)
{
__layout_binding = new global::Binding.Main (view);
bool callBase = true;
OnSetContentView (view, @params, ref callBase);
if (callBase) {
base.SetContentView (view, @params);
}
}
void SetContentView (global::Android.Views.View view, global::Android.Views.ViewGroup.LayoutParams @params, global::Xamarin.Android.Design.LayoutBinding.OnLayoutItemNotFoundHandler onLayoutItemNotFound)
{
__layout_binding = new global::Binding.Main (view, onLayoutItemNotFound);
bool callBase = true;
OnSetContentView (view, @params, ref callBase);
if (callBase) {
base.SetContentView (view, @params);
}
}
public override void SetContentView (int layoutResID)
{
__layout_binding = new global::Binding.Main (this);
bool callBase = true;
OnSetContentView (layoutResID, ref callBase);
if (callBase) {
base.SetContentView (layoutResID);
}
}
void SetContentView (int layoutResID, global::Xamarin.Android.Design.LayoutBinding.OnLayoutItemNotFoundHandler onLayoutItemNotFound)
{
__layout_binding = new global::Binding.Main (this, onLayoutItemNotFound);
bool callBase = true;
OnSetContentView (layoutResID, ref callBase);
if (callBase) {
base.SetContentView (layoutResID);
}
}
partial void OnSetContentView (global::Android.Views.View view, ref bool callBaseAfterReturn);
partial void OnSetContentView (global::Android.Views.View view, global::Android.Views.ViewGroup.LayoutParams @params, ref bool callBaseAfterReturn);
partial void OnSetContentView (int layoutResID, ref bool callBaseAfterReturn);
public Button myButton => __layout_binding?.myButton;
public CommonSampleLibrary.LogFragment fragmentWithExplicitManagedType => __layout_binding?.fragmentWithExplicitManagedType;
public global::Android.App.Fragment fragmentWithInferredType => __layout_binding?.fragmentWithInferredType;
}
}
Атрибуты XML макета
Многие новые xml-атрибуты макета управляют привязкой и Code-Behind поведением, которые находятся в xamarin пространстве имен XML (xmlns:xamarin="http://schemas.xamarin.com/android/xamarin/tools").
К ним относятся:
xamarin:classes
xamarin:classes атрибут XML используется в составе Code-Behind, чтобы указать, какие типы следует создавать.
xamarin:classes XML-атрибут содержит разделенный ;список полных имен классов, которые следует создать.
xamarin:managedType
xamarin:managedType Атрибут макета используется для явного указания управляемого типа для предоставления привязанного идентификатора как. Если тип не указан, его будут выводить из декларирующего контекста, например, <Button/> приведет к Android.Widget.Button, а <fragment/> приведет к Android.App.Fragment.
Атрибут xamarin:managedType позволяет использовать более явные объявления типов.
Сопоставление управляемых типов
В управляемой среде часто используются имена виджетов на основе пакета Java, к которым они относятся, и, так же часто, управляемое имя .NET такого типа будет иметь другое имя в стиле .NET в той же среде. Генератор кода может выполнить ряд очень простых настроек, чтобы попытаться сопоставить код, например:
Сделайте заглавными буквами все части пространства имен типа и его имени. Например,
java.package.myButtonстанетJava.Package.MyButtonДелайте заглавными двухбуквенные компоненты пространства имен типа. Например,
android.os.SomeTypeстанетAndroid.OS.SomeTypeПросмотрите ряд заранее определённых пространств имён, которые имеют известные сопоставления. В настоящее время список содержит следующие сопоставления:
-
android.view—>Android.Views -
com.actionbarsherlock—>ABSherlock -
com.actionbarsherlock.widget—>ABSherlock.Widget -
com.actionbarsherlock.view—>ABSherlock.View -
com.actionbarsherlock.app—>ABSherlock.App
-
Поиск ряда жестко закодированных типов во внутренних таблицах. В настоящее время список включает следующие типы:
-
WebView—>Android.Webkit.WebView
-
Удалите количество жестко закодированных префиксов пространства имен. В настоящее время список содержит следующие префиксы:
com.google.
Однако если приведенные выше попытки терпят неудачу, необходимо изменить макет, использующий виджет с таким несопоставленным типом, чтобы добавить xamarin объявление XML в корневой элемент макета и xamarin:managedType в элемент, который требует сопоставления. Например:
<fragment
android:id="@+id/log_fragment"
android:name="commonsamplelibrary.LogFragment"
xamarin:managedType="CommonSampleLibrary.LogFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Для собственного типа commonsamplelibrary.LogFragment будет использоваться тип CommonSampleLibrary.LogFragment.
Вы можете избежать добавления объявления пространства имен XML и xamarin:managedType атрибута, просто именуя тип с помощью управляемого имени, например приведенный выше фрагмент может быть переопределен следующим образом:
<fragment
android:name="CommonSampleLibrary.LogFragment"
android:id="@+id/secondary_log_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Фрагменты: особый случай
Экосистема Android в настоящее время поддерживает две различные реализации Fragment мини-приложения:
-
Android.App.FragmentКлассический фрагмент, поставляемый с базовой системой Android -
AndroidX.Fragment.App.FragmentВXamarin.AndroidX.FragmentПакет NuGet.
Эти классы несовместимы друг с другом, поэтому при создании кода привязки для <fragment> элементов в файлах макета необходимо учитывать особое внимание. .NET для Android должен выбрать одну Fragment реализацию в качестве используемой по умолчанию, если <fragment> элемент не имеет определенного типа (управляемого или другого). Генератор кода привязки использует
$(AndroidFragmentType) Свойство MSBuild для этой цели. Свойство может быть переопределено пользователем, чтобы указать тип, отличный от типа по умолчанию. Свойство задано Android.App.Fragment по умолчанию и переопределяется пакетами NuGet AndroidX.
Если созданный код не компилируется, файл макета следует изменить, указав управляемый тип фрагмента.
Выбор и обработка макета программной части
Отбор
По умолчанию генерация связанного программного кода отключена. Чтобы включить обработку всех макетов в любом из Resource\layout* каталогов, содержащих по крайней мере один элемент с атрибутом //*/@android:id, установите для свойства $(AndroidGenerateLayoutBindings) значение True на командной строке msbuild:
dotnet build -p:AndroidGenerateLayoutBindings=true MyProject.csproj
или в вашем .csproj-файле:
<PropertyGroup>
<AndroidGenerateLayoutBindings>true</AndroidGenerateLayoutBindings>
</PropertyGroup>
Кроме того, вы можете оставить код отключенным глобально и включить его только для определенных файлов. Чтобы включить Code-Behind для определенного .axml файла, измените файл на действие сборки
@(AndroidBoundLayout) изменив .csproj файл и заменив AndroidResource на AndroidBoundLayout:
<!-- This -->
<AndroidResource Include="Resources\layout\Main.axml" />
<!-- should become this -->
<AndroidBoundLayout Include="Resources\layout\Main.axml" />
Обработка
Макеты сгруппированы по имени, с шаблонами с похожими именами из разныхResource\layout* каталогов, которые образуют одну группу. Такие группы обрабатываются так, как если бы они были одним макетом. В таком случае может возникнуть столкновение типов между двумя мини-приложениями, найденными в разных макетах, принадлежащих одной группе. В таком случае созданное свойство не сможет иметь точный тип виджета, а скорее "упрощённый". Разложение соответствует приведенному ниже алгоритму:
Если все конфликтующие мини-приложения являются
Viewпроизводными, тип свойства будетAndroid.Views.ViewЕсли все конфликтующие типы являются
Fragmentпроизводными, тип свойства будетAndroid.App.FragmentЕсли конфликтующие мини-приложения содержат как
View, так иFragment, тип свойства будетglobal::System.Object
Созданный код
Если вам интересно, как выглядит сгенерированный код для ваших макетов, пожалуйста, загляните в папку obj\$(Configuration)\generated в каталоге вашего решения.