As part of the .NET for Android build, Android resources
are processed, exposing Android IDs via a generated
_Microsoft.Android.Resource.Designer.dll assembly.
For example, given the file Reources\layout\Main.axml with contents:
A binding is a generated class, one per Android layout file, which contains
strongly typed properties for all of the ids within the layout file. Binding
types are generated into the global::Bindings namespace, with type names
which mirror the filename of the layout file.
Binding types are created for all layout files which contain any Android IDs.
Given the Android Layout file Resources\layout\Main.axml:
The binding's base type, Xamarin.Android.Design.LayoutBinding is not part of the
.NET for Android class library but rather shipped with .NET for Android in source form
and included in the application's build automatically whenever bindings are used.
The generated binding type can be created around Activity instances, allowing
for strongly-typed access to IDs within the layout file:
Binding types may also be constructed around View instances, allowing
strongly-typed access to Resource IDs within the View or its children:
C#
var binding = new Binding.Main (some_view);
Missing Resource IDs
Properties on binding types still use FindViewById<T>() in their
implementation. If FindViewById<T>() returns null, then the default
behavior is for the property to throw an InvalidOperationException
instead of returning null.
This default behavior may be overridden by passing an error handler delegate to
the generated binding on its instantiation:
C#
// User-written codepartialclassMainActivity : Activity {
Java.Lang.Object? OnLayoutItemNotFound (int resourceId, Type expectedViewType)
{
// Find and return the View or Fragment identified by `resourceId`// or `null` if unknownreturnnull;
}
protectedoverridevoidOnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.Main);
var binding = new Binding.Main (this, OnLayoutItemNotFound);
}
}
The OnLayoutItemNotFound() method is invoked when a resource ID for a View or a Fragment
could not be found.
The handler must return either null, in which case the InvalidOperationException will be
thrown or, preferably, return the View or Fragment instance that corresponds to the
ID passed to the handler. The returned object must be of the correct type matching the type
of the corresponding Binding property. The returned value is cast to that type, so if the object
isn't correctly typed an exception will be thrown.
Code-Behind
Code-Behind involves build-time generation of a partial class which contains
strongly typed properties for all of the ids within the layout file.
Code-Behind builds atop the Binding mechanism, while requiring that layout
files "opt-in" to Code-Behind generation by using the new
xamarin:classes XML attribute, which is a ;-separated
list of full class names to be generated.
Given the Android Layout file Resources\layout\Main.axml:
The OnLayoutItemNotFound error handler can be passed as the last parameter of whatever overload
of SetContentView the activity is using:
C#
// User-written codepartialclassMainActivity : Activity {
protectedoverridevoidOnCreate (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 unknownreturnnull;
}
}
As Code-Behind relies on partial classes, all declarations of a partial class
must use partial class in their declaration, otherwise a CS0260
C# compiler error will be generated at build time.
Customization
The generated Code Behind type always overrides Activity.SetContentView(),
and by default it always calls base.SetContentView(), forwarding the
parameters. If this is not desired, then one of the OnSetContentView()partial methods should be overridden, setting callBaseAfterReturn
to false:
Many new Layout XML attributes control Binding and Code-Behind behavior, which
are within the xamarin XML namespace
(xmlns:xamarin="http://schemas.xamarin.com/android/xamarin/tools").
These include:
The xamarin:classes XML attribute contains a ;-separated list of
full class names that should be generated.
xamarin:managedType
The xamarin:managedType layout attribute is used to explicitly specify the
managed type to expose the bound ID as. If not specified, the type will be
inferred from the declaring context, e.g. <Button/> will result in an
Android.Widget.Button, and <fragment/> will result in an
Android.App.Fragment.
The xamarin:managedType attribute allows for more explicit type declarations.
Managed type mapping
It is quite common to use widget names based on the Java package they come
from and, equally as often, the managed .NET name of such type will have a
different (.NET style) name in the managed land. The code generator can perform
a number of very simple adjustments to try to match the code, such as:
Capitalize all the components of the type namespace and name. For instance
java.package.myButton would become Java.Package.MyButton
Capitalize two-letter components of the type namespace. For instance
android.os.SomeType would become Android.OS.SomeType
Look up a number of hard-coded namespaces which have known mappings.
Currently the list includes the following mappings:
Strip number of hard-coded namespace prefixes. Currently the list includes the following prefixes:
com.google.
If, however, the above attempts fail, you will need to modify the layout which
uses a widget with such an unmapped type to add both the xamarin XML
namespace declaration to the root element of the layout and the
xamarin:managedType to the element requiring the mapping. For instance:
Will use the CommonSampleLibrary.LogFragment type for the native type commonsamplelibrary.LogFragment.
You can avoid adding the XML namespace declaration and the
xamarin:managedType attribute by simply naming the type using its managed
name, for instance the above fragment could be redeclared as follows:
These classes are not compatible with each other and so special care must be
taken when generating binding code for <fragment> elements in the layout files. .NET for Android must
choose one Fragment implementation as the default one to be used if the <fragment> element does not
have any specific type (managed or otherwise) specified. Binding code generator uses the
$(AndroidFragmentType)
MSBuild property for that purpose. The property can be overriden by the user to specify a type different
than the default one. The property is set to Android.App.Fragment by default, and is overridden by the
AndroidX NuGet packages.
If the generated code does not build, the layout file must be amended by specifying the manged type of the
fragment in question.
Code-behind layout selection and processing
Selection
By default code-behind generation is disabled. To enable processing for all
layouts in any of the Resource\layout* directories that contain at least a
single element with the //*/@android:id attribute, set the
$(AndroidGenerateLayoutBindings) MSBuild property to True either on the
msbuild command line:
Alternatively, you can leave code-behind disabled globally and enable it only
for specific files. To enable Code-Behind for a particular .axml file, change
the file to have a Build action of
@(AndroidBoundLayout)
by editing your .csproj file and replacing AndroidResource with AndroidBoundLayout:
XML
<!-- This --><AndroidResourceInclude="Resources\layout\Main.axml" /><!-- should become this --><AndroidBoundLayoutInclude="Resources\layout\Main.axml" />
Processing
Layouts are grouped by name, with like-named templates from differentResource\layout* directories comprising a single group. Such groups are
processed as if they were a single layout. It is possible that in such case
there will be a type clash between two widgets found in different layouts
belonging to the same group. In such case the generated property will not be
able to have the exact widget type, but rather a "decayed" one. Decaying
follows the algorithm below:
If all of the conflicting widgets are View derivatives, the property
type will be Android.Views.View
If all of the conflicting types are Fragment derivatives, the property
type will be Android.App.Fragment
If the conflicting widgets contain both a View and a Fragment, the
property type will be global::System.Object
Generated code
If you are interested in how the generated code looks for your layouts, please
take a look in the obj\$(Configuration)\generated folder in your solution
directory.
Liity tapaamissarjaan ja luo skaalattavia tekoälyratkaisuja, jotka perustuvat reaalimaailman käyttötapauksiin muiden kehittäjien ja asiantuntijoiden kanssa.
Luo tietojen sidonnan sisältävä käyttöliittymä. Käyttöliittymä päivittyy automaattisesti uusimpien tietojen perusteella, kun taas tiedot päivittyvät käyttöliittymän muutosten johdosta.