Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Som en del av .NET för Android-versionen bearbetas Android-resurser och exponerar Android-ID:er via en genererad _Microsoft.Android.Resource.Designer.dll sammansättning.
Till exempel, med tanke på filen Reources\layout\Main.axml med innehåll:
<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>
Vid byggtillfället skapas sedan en _Microsoft.Android.Resource.Designer.dll assembly med innehåll som liknar:
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;}
}
}
Traditionellt skulle interaktion med resurser göras i kod, med hjälp av konstanterna från Resource typen och FindViewById<T>() metoden:
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!";
};
}
}
Från och med Xamarin.Android 8.4 finns det ytterligare två sätt att interagera med Android-resurser när du använder C#:
Om du vill aktivera dessa nya funktioner ställer du in
$(AndroidGenerateLayoutBindings) MSBuild-egenskapen till True antingen på kommandoraden msbuild:
dotnet build -p:AndroidGenerateLayoutBindings=true MyProject.csproj
eller i .csproj-filen:
<PropertyGroup>
<AndroidGenerateLayoutBindings>true</AndroidGenerateLayoutBindings>
</PropertyGroup>
Bindningar
En bindning är en genererad klass, en per Android-layoutfil, som innehåller starkt skrivna egenskaper för alla ID:er i layoutfilen. Bindningstyper genereras till global::Bindings namnområdet med typnamn som speglar layoutfilens filnamn.
Bindningstyper skapas för alla layoutfiler som innehåller alla Android-ID:er.
Med tanke på Android Layout-filen 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>
Då genereras följande typ:
// 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);
}
}
Bindningens bastyp Xamarin.Android.Design.LayoutBinding är inte en del av .NET för Android-klassbiblioteket, utan levereras i stället med .NET för Android i källformulär och ingår i programmets version automatiskt när bindningar används.
Den genererade bindningstypen kan skapas runt Activity instanser, vilket ger starkt typad åtkomst till ID:n i layoutfilen.
// 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!";
};
}
}
Bindningstyper kan också konstrueras kring View instanser, vilket ger starkt typad åtkomst till resurs-ID:er i vyn eller dess underordnade:
var binding = new Binding.Main (some_view);
Resurs-ID:t saknas
Egenskaper för bindningstyper används FindViewById<T>() fortfarande i implementeringen. Om FindViewById<T>() returnerar nullär standardbeteendet att egenskapen genererar en InvalidOperationException i stället för att nullreturnera .
Det här standardbeteendet kan åsidosättas genom att ett felhanteringsdelegat skickas till den genererade bindningen vid instansieringen:
// 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);
}
}
Metoden OnLayoutItemNotFound() anropas när det inte gick att hitta ett resurs-ID för en View eller ett Fragment .
Hanteraren måste returnera antingen null, i vilket fall InvalidOperationException kommer att kastas, eller, helst, returnera den View- eller Fragment-instans som motsvarar det ID som skickas till hanteraren. Det returnerade objektet måste vara av rätt typ som matchar typen av motsvarande bindningsegenskap. Det returnerade värdet skickas till den typen, så om objektet inte är korrekt skrivet genereras ett undantag.
Code-Behind
Code-Behind omfattar byggtidsgenerering av en partial klass som innehåller starkt skrivna egenskaper för alla ID:er i layoutfilen.
Code-Behind bygger ovanpå bindningsmekanismen, samtidigt som det kräver att layoutfilerna väljer Code-Behind generation med hjälp av det nya xamarin:classes XML-attributet, som är en ;-avgränsad lista med fullständiga klassnamn som ska genereras.
Med tanke på Android Layout-filen Resources\layout\Main.axml:
<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>
vid byggtiden skapas följande typ:
// 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;
}
}
Detta möjliggör mer "intuitiv" användning av resurs-ID:t i layouten:
// 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!";
};
}
}
Felhanteraren OnLayoutItemNotFound kan skickas som den sista parametern i vilken överlagring av SetContentView aktivitet som används:
// 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;
}
}
Eftersom Code-Behind förlitar sig på partiella klasser måste alla deklarationer av en partiell klass använda partial class i deklarationen, annars genereras ett CS0260 C#-kompilatorfel vid bygget.
Kundanpassning
Den genererade typen Bakom kod OnSetContentView()partial metoderna åsidosättas och ställas in callBaseAfterReturn på 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);
}
}
Exempel på genererad kod
// 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-attribut för layout
Många nya XML-layoutattribut styr bindning och Code-Behind beteende, som finns inom xamarin XML-namnområdet (xmlns:xamarin="http://schemas.xamarin.com/android/xamarin/tools").
Dessa inkluderar:
xamarin:classes
xamarin:classes XML-attributet används som en del av Code-Behind för att ange vilka typer som ska genereras.
xamarin:classes XML-attributet innehåller en ;-avgränsad lista med fullständiga klassnamn som ska genereras.
xamarin:managedType
Layoutattributet xamarin:managedType används för att uttryckligen ange den hanterade typen för att exponera det bundna ID:t som. Om den inte anges härleds typen från deklareringskontexten, t.ex. <Button/> resulterar i en Android.Widget.Button, och <fragment/> resulterar i en Android.App.Fragment.
Attributet xamarin:managedType tillåter mer explicita typdeklarationer.
Hanterad typmappning
Det är ganska vanligt att använda widgetnamn baserat på Java-paketet som de kommer från, och lika ofta har det hanterade .NET-namnet av den typen ett annat namn (.NET-format) i det hanterade landet. Kodgeneratorn kan utföra ett antal mycket enkla justeringar för att försöka matcha koden, till exempel:
Använd versaler för alla komponenter i typens namnområde och namn. Till exempel
java.package.myButtonskulle bliJava.Package.MyButtonVersalera komponenter med två bokstäver i typnamnområdet. Till exempel
android.os.SomeTypeskulle bliAndroid.OS.SomeTypeLeta upp ett antal hårdkodade namnområden som har kända mappningar. För närvarande innehåller listan följande mappningar:
-
android.view->Android.Views -
com.actionbarsherlock->ABSherlock -
com.actionbarsherlock.widget->ABSherlock.Widget -
com.actionbarsherlock.view->ABSherlock.View -
com.actionbarsherlock.app->ABSherlock.App
-
Leta upp ett antal hårdkodade typer i interna tabeller. Listan innehåller för närvarande följande typer:
-
WebView->Android.Webkit.WebView
-
Ta bort antalet hårdkodade namnområdesprefix. För närvarande innehåller listan följande prefix:
com.google.
Om ovanstående försök misslyckas måste du dock ändra layouten som använder en widget med en sådan ommappad typ för att lägga till både xamarin XML-namnområdesdeklarationen till rotelementet i layouten och xamarin:managedType till elementet som kräver mappningen. Till exempel:
<fragment
android:id="@+id/log_fragment"
android:name="commonsamplelibrary.LogFragment"
xamarin:managedType="CommonSampleLibrary.LogFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Använder typen CommonSampleLibrary.LogFragment för den inbyggda typen commonsamplelibrary.LogFragment.
Du kan undvika att lägga till XML-namnområdesdeklarationen och attributet xamarin:managedType genom att helt enkelt namnge typen med dess förvaltade namn, till exempel kan ovanstående fragment omdeklareras på följande sätt:
<fragment
android:name="CommonSampleLibrary.LogFragment"
android:id="@+id/secondary_log_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Fragment: ett specialfall
Android-ekosystemet stöder för närvarande två distinkta implementeringar av widgeten Fragment :
-
Android.App.FragmentDet "klassiska" fragmentet som levereras med det grundläggande Android-systemet -
AndroidX.Fragment.App.Fragment, iXamarin.AndroidX.FragmentNuGet-paket.
Dessa klasser är inte kompatibla med varandra och därför måste särskild försiktighet iakttas vid generering av bindningskod för <fragment> element i layoutfilerna. .NET för Android måste välja en Fragment implementering som standard som ska användas om elementet <fragment> inte har någon specifik typ (hanterad eller på annat sätt) angiven. Bindningskodgeneratorn använder
$(AndroidFragmentType) MSBuild-egenskapen för det ändamålet. Egenskapen kan åsidosättas av användaren för att ange en annan typ än standardvärdet. Egenskapen är inställd på Android.App.Fragment som standard och åsidosätts av AndroidX NuGet-paketen.
Om den genererade koden inte kompilerar måste layoutfilen ändras genom att ange hanterade typen av fragmentet i fråga.
Layoutval och bearbetning i kodbakgrunden
Urval
Som standard är code-behind-generering inaktiverad. Om du vill aktivera bearbetning för alla layouter i någon av de Resource\layout* kataloger som innehåller minst ett enda element med //*/@android:id attributet anger du $(AndroidGenerateLayoutBindings) egenskapen MSBuild till True antingen på kommandoraden msbuild:
dotnet build -p:AndroidGenerateLayoutBindings=true MyProject.csproj
eller i .csproj-filen:
<PropertyGroup>
<AndroidGenerateLayoutBindings>true</AndroidGenerateLayoutBindings>
</PropertyGroup>
Alternativt kan du lämna code-behind inaktiverat globalt och aktivera det endast för specifika filer. Om du vill aktivera Code-Behind för en viss .axml fil ändrar du filen så att den har en Build-åtgärd på
@(AndroidBoundLayout) genom att redigera .csproj filen och ersätta AndroidResource med AndroidBoundLayout:
<!-- This -->
<AndroidResource Include="Resources\layout\Main.axml" />
<!-- should become this -->
<AndroidBoundLayout Include="Resources\layout\Main.axml" />
Processing
Layouter grupperas efter namn, med mallar med liknande namn från olikaResource\layout* kataloger som består av en enda grupp. Sådana grupper bearbetas som om de vore en enda layout. Det är möjligt att det i sådana fall blir en typkonflikt mellan två widgetar som finns i olika layouter som tillhör samma grupp. I sådana fall kommer den genererade egenskapen inte att kunna ha den exakta widgettypen, utan snarare en "förfallen" egenskap. Förfall följer algoritmen nedan:
Om alla motstridiga widgetar är
Viewderivat blir egenskapstypenAndroid.Views.ViewOm alla typer av motstridiga typer är
Fragmentderivat kommer egenskapstypen att varaAndroid.App.FragmentOm de motstridiga widgetarna innehåller både en
Viewoch enFragment, kommer egenskapstypen att varaglobal::System.Object
Genererad kod
Om du är intresserad av hur den genererade koden ser ut för dina layouter kan du ta en titt i obj\$(Configuration)\generated-mappen i din lösningskatalog.