Gambaran Umum
Sebagai bagian dari build .NET untuk Android, sumber daya Android diproses, mengekspos ID Android melalui rakitan yang dihasilkan_Microsoft.Android.Resource.Designer.dll
.
Misalnya, mengingat file Reources\layout\Main.axml
dengan konten:
<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>
Kemudian selama build-time assembly _Microsoft.Android.Resource.Designer.dll
dengan konten yang mirip dengan:
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;}
}
}
Secara tradisional, berinteraksi dengan Sumber Daya akan dilakukan dalam kode, menggunakan konstanta dari Resource
jenis dan FindViewById<T>()
metode :
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!";
};
}
}
Dimulai dengan Xamarin.Android 8.4, ada dua cara tambahan untuk berinteraksi dengan sumber daya Android saat menggunakan C#:
Untuk mengaktifkan fitur baru ini, atur $(AndroidGenerateLayoutBindings)
Properti MSBuild ke True
baik pada baris perintah msbuild:
dotnet build -p:AndroidGenerateLayoutBindings=true MyProject.csproj
atau dalam file .csproj Anda:
<PropertyGroup>
<AndroidGenerateLayoutBindings>true</AndroidGenerateLayoutBindings>
</PropertyGroup>
Pengikatan
Pengikatan adalah kelas yang dihasilkan, satu per file tata letak Android, yang berisi properti yang sangat diketik untuk semua id dalam file tata letak. Jenis pengikatan dihasilkan ke dalam global::Bindings
namespace layanan, dengan nama jenis yang mencerminkan nama file file tata letak.
Jenis pengikatan dibuat untuk semua file tata letak yang berisi ID Android apa pun.
Mengingat file Resources\layout\Main.axml
Tata Letak Android :
<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>
maka jenis berikut akan dihasilkan:
// 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);
}
}
Jenis dasar pengikatan, Xamarin.Android.Design.LayoutBinding
bukan bagian dari pustaka kelas .NET untuk Android, melainkan dikirim dengan .NET untuk Android dalam bentuk sumber dan disertakan dalam build aplikasi secara otomatis setiap kali pengikatan digunakan.
Jenis pengikatan yang dihasilkan dapat dibuat di sekitar Activity
instans, memungkinkan akses yang sangat ditik ke ID dalam file tata letak:
// 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!";
};
}
}
Jenis pengikatan juga dapat dibangun di sekitar View
instans, memungkinkan akses yang sangat ditik ke ID Sumber Daya dalam Tampilan atau turunannya:
var binding = new Binding.Main (some_view);
ID Sumber Daya Hilang
Properti pada jenis pengikatan masih digunakan FindViewById<T>()
dalam implementasinya. Jika FindViewById<T>()
mengembalikan null
, maka perilaku default adalah properti untuk melemparkan InvalidOperationException
alih-alih mengembalikan null
.
Perilaku default ini dapat ditimpa dengan meneruskan delegasi handler kesalahan ke pengikatan yang dihasilkan pada instansiasinya:
// 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);
}
}
Metode OnLayoutItemNotFound()
ini dipanggil ketika ID sumber daya untuk atau View
Fragment
tidak dapat ditemukan.
Handler harus mengembalikan , null
dalam hal ini InvalidOperationException
akan dilemparkan atau, sebaiknya, mengembalikan View
instans atau Fragment
yang sesuai dengan ID yang diteruskan ke handler. Objek yang dikembalikan harus dari jenis yang benar yang cocok dengan jenis properti Pengikatan yang sesuai. Nilai yang dikembalikan ditransmisikan ke jenis tersebut, jadi jika objek tidak diketik dengan benar, pengecualian akan dilemparkan.
Kode-Belakang
Code-Behind melibatkan pembuatan build-time kelas partial
yang berisi properti yang sangat diketik untuk semua id dalam file tata letak.
Code-Behind dibangun di atas mekanisme Pengikatan, sambil mengharuskan file tata letak "ikut serta" ke pembuatan Code-Behind dengan menggunakan atribut XML baru xamarin:classes
, yang merupakan ;
daftar nama kelas lengkap yang dipisahkan untuk dihasilkan.
Mengingat file Resources\layout\Main.axml
Tata Letak 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>
pada waktu build, jenis berikut akan diproduksi:
// 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;
}
}
Ini memungkinkan penggunaan ID Sumber Daya yang lebih "intuitif" dalam tata letak:
// 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!";
};
}
}
Handler OnLayoutItemNotFound
kesalahan dapat diteruskan sebagai parameter terakhir dari kelebihan SetContentView
aktivitas apa pun yang menggunakan:
// 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;
}
}
Karena Code-Behind bergantung pada kelas parsial, semua deklarasi kelas parsial harus digunakan partial class
dalam deklarasi mereka, jika tidak , kesalahan kompilator CS0260 C# akan dihasilkan pada waktu build.
Penyesuaian
Jenis Code Behind yang dihasilkan selalu mengambil alih Activity.SetContentView()
, dan secara default selalu memanggil base.SetContentView()
, meneruskan parameter. Jika ini tidak diinginkan, maka salah OnSetContentView()
partial
satu metode harus ditimpa, mengatur callBaseAfterReturn
ke :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);
}
}
Contoh Kode yang Dihasilkan
// 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;
}
}
Atribut XML Tata Letak
Banyak atribut XML Tata Letak baru mengontrol perilaku Pengikatan dan Code-Behind, yang berada dalam xamarin
namespace XML (xmlns:xamarin="http://schemas.xamarin.com/android/xamarin/tools"
).
Ini termasuk:
xamarin:classes
Atribut xamarin:classes
XML digunakan sebagai bagian dari Code-Behind untuk menentukan jenis mana yang harus dihasilkan.
Atribut xamarin:classes
XML berisi ;
daftar nama kelas lengkap yang dipisahkan yang harus dihasilkan.
xamarin:managedType
Atribut xamarin:managedType
tata letak digunakan untuk secara eksplisit menentukan jenis terkelola untuk mengekspos ID terikat sebagai. Jika tidak ditentukan, jenis akan disimpulkan dari konteks deklarasikan, misalnya <Button/>
akan menghasilkan Android.Widget.Button
, dan <fragment/>
akan menghasilkan Android.App.Fragment
.
Atribut xamarin:managedType
ini memungkinkan deklarasi jenis yang lebih eksplisit.
Pemetaan jenis terkelola
Sangat umum untuk menggunakan nama widget berdasarkan paket Java yang berasal dari dan, sama seringnya, nama .NET terkelola dari jenis tersebut akan memiliki nama (.NET style) yang berbeda di tanah terkelola. Generator kode dapat melakukan sejumlah penyesuaian yang sangat sederhana untuk mencoba mencocokkan kode, seperti:
Kapitalkan semua komponen jenis namespace layanan dan nama. Misalnya
java.package.myButton
akan menjadiJava.Package.MyButton
Kapitalkan komponen dua huruf dari namespace tipe. Misalnya
android.os.SomeType
akan menjadiAndroid.OS.SomeType
Cari sejumlah namespace layanan yang dikodekan secara permanen yang memiliki pemetaan yang diketahui. Saat ini daftar menyertakan pemetaan berikut:
android.view
->Android.Views
com.actionbarsherlock
->ABSherlock
com.actionbarsherlock.widget
->ABSherlock.Widget
com.actionbarsherlock.view
->ABSherlock.View
com.actionbarsherlock.app
->ABSherlock.App
Cari sejumlah jenis yang dikodekan secara permanen dalam tabel internal. Saat ini daftar menyertakan jenis berikut:
WebView
->Android.Webkit.WebView
Nomor strip awalan namespace yang dikodekan secara permanen. Saat ini daftar menyertakan awalan berikut:
com.google.
Namun, jika upaya di atas gagal, Anda harus memodifikasi tata letak yang menggunakan widget dengan jenis yang tidak dipetakan seperti itu xamarin
untuk menambahkan deklarasi namespace XML ke elemen akar tata letak dan xamarin:managedType
ke elemen yang memerlukan pemetaan. Contohnya:
<fragment
android:id="@+id/log_fragment"
android:name="commonsamplelibrary.LogFragment"
xamarin:managedType="CommonSampleLibrary.LogFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Akan menggunakan CommonSampleLibrary.LogFragment
jenis untuk jenis commonsamplelibrary.LogFragment
asli .
Anda dapat menghindari penambahan deklarasi namespace XML dan xamarin:managedType
atribut hanya dengan menamai jenis menggunakan nama terkelolanya, misalnya fragmen di atas dapat direeklarasikan ulang sebagai berikut:
<fragment
android:name="CommonSampleLibrary.LogFragment"
android:id="@+id/secondary_log_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
Fragmen: kasus khusus
Ekosistem Android saat ini mendukung dua implementasi widget yang Fragment
berbeda:
Android.App.Fragment
Fragmen "klasik" dikirim dengan sistem Android dasarAndroidX.Fragment.App.Fragment
, dalamXamarin.AndroidX.Fragment
Paket NuGet.
Kelas-kelas ini tidak kompatibel satu sama lain dan jadi perawatan khusus harus diambil saat menghasilkan kode pengikatan untuk <fragment>
elemen dalam file tata letak. .NET untuk Android harus memilih satu Fragment
implementasi sebagai implementasi default yang akan digunakan jika <fragment>
elemen tidak memiliki jenis tertentu (terkelola atau sebaliknya) yang ditentukan. Generator kode pengikatan menggunakan $(AndroidFragmentType)
Properti MSBuild untuk tujuan tersebut. Properti dapat ditimpa oleh pengguna untuk menentukan jenis yang berbeda dari yang default. Properti diatur ke Android.App.Fragment
secara default, dan ditimpa oleh paket AndroidX NuGet.
Jika kode yang dihasilkan tidak dibuat, file tata letak harus diubah dengan menentukan jenis fragmen yang dirusak yang dimaksud.
Pemilihan dan pemrosesan tata letak code-behind
Pilihan
Secara default pembuatan code-behind dinonaktifkan. Untuk mengaktifkan pemrosesan untuk semua tata letak di salah Resource\layout*
satu direktori yang berisi setidaknya satu elemen dengan //*/@android:id
atribut , atur $(AndroidGenerateLayoutBindings)
properti MSBuild ke True
pada baris perintah msbuild:
dotnet build -p:AndroidGenerateLayoutBindings=true MyProject.csproj
atau dalam file .csproj Anda:
<PropertyGroup>
<AndroidGenerateLayoutBindings>true</AndroidGenerateLayoutBindings>
</PropertyGroup>
Atau, Anda dapat membiarkan code-behind dinonaktifkan secara global dan mengaktifkannya hanya untuk file tertentu. Untuk mengaktifkan Code-Behind untuk file tertentu .axml
, ubah file agar memiliki tindakan Build @(AndroidBoundLayout)
dengan mengedit file Anda .csproj
dan mengganti AndroidResource
dengan AndroidBoundLayout
:
<!-- This -->
<AndroidResource Include="Resources\layout\Main.axml" />
<!-- should become this -->
<AndroidBoundLayout Include="Resources\layout\Main.axml" />
Sedang diproses
Tata letak dikelompokkan menurut nama, dengan templat bernama seperti dari direktori yang berbedaResource\layout*
yang terdiri dari satu grup. Grup tersebut diproses seolah-olah mereka adalah tata letak tunggal. Ada kemungkinan bahwa dalam kasus seperti itu akan ada bentrokan jenis antara dua widget yang ditemukan di tata letak yang berbeda milik grup yang sama. Dalam kasus seperti itu, properti yang dihasilkan tidak akan dapat memiliki jenis widget yang tepat, melainkan properti "membusuk". Pembusukan mengikuti algoritma di bawah ini:
Jika semua widget yang bertentangan adalah
View
turunan, jenis properti akan menjadiAndroid.Views.View
Jika semua jenis yang bertentangan adalah
Fragment
turunan, jenis properti akan menjadiAndroid.App.Fragment
Jika widget yang bertentangan berisi dan
View
Fragment
, jenis properti akanglobal::System.Object
Kode yang dihasilkan
Jika Anda tertarik dengan bagaimana kode yang dihasilkan mencari tata letak Anda, silakan lihat di obj\$(Configuration)\generated
folder di direktori solusi Anda.