개요
Android용 .NET 빌드의 일부로 Android 리소스가 처리되어 생성된 _Microsoft.Android.Resource.Designer.dll
어셈블리를 통해 Android ID가 노출됩니다.
예를 들어 내용이 포함된 파일이 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;}
}
}
일반적으로 리소스와 상호 작용하는 작업은 형식 및 FindViewById<T>()
메서드의 상수로 코드에서 Resource
수행됩니다.
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부터 C#을 사용할 때 Android 리소스와 상호 작용하는 두 가지 추가 방법이 있습니다.
이러한 새 기능을 사용하도록 설정하려면 $(AndroidGenerateLayoutBindings)
msbuild 명령줄에 있는 MSBuild 속성 True
:
dotnet build -p:AndroidGenerateLayoutBindings=true MyProject.csproj
또는 .csproj 파일에서 다음을 수행합니다.
<PropertyGroup>
<AndroidGenerateLayoutBindings>true</AndroidGenerateLayoutBindings>
</PropertyGroup>
바인딩
바인딩은 Android 레이아웃 파일당 하나씩 생성된 클래스로, 레이아웃 파일 내의 모든 ID에 대해 강력한 형식의 속성을 포함합니다. 바인딩 형식은 레이아웃 파일의 global::Bindings
파일 이름을 미러링하는 형식 이름을 사용하여 네임스페이스에 생성됩니다.
바인딩 형식은 Android ID를 포함하는 모든 레이아웃 파일에 대해 만들어집니다.
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
은 Android용 .NET 클래스 라이브러리의 일부가 아니라 원본 형식으로 Android용 .NET과 함께 제공되며 바인딩을 사용할 때마다 애플리케이션 빌드에 자동으로 포함됩니다.
생성된 바인딩 형식은 인스턴스를 중심으로 Activity
만들 수 있으므로 레이아웃 파일 내의 ID에 대한 강력한 형식의 액세스를 허용합니다.
// 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
바인딩 형식을 생성하여 뷰 또는 해당 자식 내의 리소스 ID에 대한 강력한 형식의 액세스를 허용할 수 있습니다.
var binding = new Binding.Main (some_view);
누락된 리소스 ID
바인딩 형식의 속성은 여전히 구현에 사용됩니다 FindViewById<T>()
. 반환되는 경우 FindViewById<T>()
기본 동작은 속성을 반환하는 대신 throw InvalidOperationException
하는 null
것입니다.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()
메서드는 a 또는 Fragment
a의 리소스 ID를 View
찾을 수 없을 때 호출됩니다.
처리기는 throw되거나, 바람직하게는 처리기에 전달된 ID에 View
해당하는 인스턴스를 Fragment
반환해야 합니다 null
InvalidOperationException
. 반환된 개체 는 해당 Binding 속성의 형식과 일치하는 올바른 형식이어야 합니다 . 반환된 값은 해당 형식으로 캐스팅되므로 개체가 올바르게 형식화되지 않은 경우 예외가 throw됩니다.
코드 숨김
코드 숨김에는 레이아웃 파일 내의 모든 ID에 대해 강력한 형식의 partial
속성이 포함된 클래스의 빌드 시간 생성이 포함됩니다 .
코드 숨김은 바인딩 메커니즘 위에 빌드되며, 생성할 전체 클래스 이름의 구분된 목록인 새 xamarin:classes
XML 특성을 ;
사용하여 레이아웃 파일을 코드 숨김 생성에 "옵트인"하도록 요구합니다.
Android 레이아웃 파일이 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>
빌드 시 다음 형식이 생성됩니다.
// 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;
}
}
이렇게 하면 레이아웃 내에서 리소스 ID를 보다 "직관적으로" 사용할 수 있습니다.
// 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 클래스를 사용하므로 partial 클래스 의 모든 선언은 해당 선언에 사용해야 partial class
하며, 그렇지 않으면 빌드 시 CS0260 C# 컴파일러 오류가 생성됩니다.
사용자 지정
생성된 Code Behind 형식 은 항상 재정의 Activity.SetContentView()
되며, 기본적으로 항상 호출 base.SetContentView()
하여 매개 변수를 전달합니다. 원하는 것이 아닌 경우 메서드 중 하나를 재정의partial
OnSetContentView()
하고 다음으로 false
설정 callBaseAfterReturn
해야 합니다.
// 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 특성은 XML 네임스페이스(xmlns:xamarin="http://schemas.xamarin.com/android/xamarin/tools"
)에 있는 바인딩 및 코드 숨김 동작을 xamarin
제어합니다.
여기에는 다음이 포함됩니다.
xamarin:classes
xamarin:classes
XML 특성은 생성해야 하는 형식을 지정하기 위해 Code-Behind의 일부로 사용됩니다.
XML 특성에는 xamarin:classes
생성해야 하는 전체 클래스 이름의 구분된 목록이 포함되어 ;
있습니다.
xamarin:managedType
xamarin:managedType
레이아웃 특성은 바인딩된 ID를 노출할 관리되는 형식을 명시적으로 지정하는 데 사용됩니다. 지정하지 않으면 형식이 선언 컨텍스트에서 유추됩니다. 예를 들어 <Button/>
,가 생성Android.Widget.Button
되고 <fragment/>
.Android.App.Fragment
이 특성은 xamarin:managedType
보다 명시적인 형식 선언을 허용합니다.
관리되는 형식 매핑
이러한 형식의 관리되는 .NET 이름은 관리되는 토지에서 다른(.NET 스타일) 이름을 가지는 경우가 많으며, Java 패키지에 따라 위젯 이름을 사용하는 것이 일반적입니다. 코드 생성기는 다음과 같이 코드를 일치시키려고 매우 간단한 여러 조정을 수행할 수 있습니다.
형식 네임스페이스와 이름의 모든 구성 요소를 대문자로 표시합니다. 예를 들어
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.
그러나 위의 시도가 실패하는 경우 매핑되지 않은 형식의 위젯을 사용하는 레이아웃을 수정하여 XML 네임스페이스 선언을 레이아웃의 루트 요소와 xamarin:managedType
매핑이 필요한 요소에 모두 xamarin
추가해야 합니다. 예를 들면 다음과 같습니다.
<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>
바인딩 코드를 생성할 때 특별히 주의해야 합니다. Android용 .NET은 요소에 특정 형식(관리형 또는 기타)이 지정되지 않은 경우 <fragment>
사용할 기본 구현으로 하나의 Fragment
구현을 선택해야 합니다. 바인딩 코드 생성기는 다음을 사용합니다. $(AndroidFragmentType)
해당 용도에 대한 MSBuild 속성입니다. 사용자가 속성을 재정의하여 기본 형식과 다른 형식을 지정할 수 있습니다. 속성은 기본적으로 설정 Android.App.Fragment
되며 AndroidX NuGet 패키지에 의해 재정의됩니다.
생성된 코드가 빌드되지 않으면 해당 조각의 관리 형식을 지정하여 레이아웃 파일을 수정해야 합니다.
코드 숨김 레이아웃 선택 및 처리
선택 사항
기본적으로 코드 숨김 생성은 사용하지 않도록 설정됩니다. 특성이 있는 하나 이상의 요소가 //*/@android:id
포함된 디렉터리에 있는 Resource\layout*
모든 레이아웃에 대한 처리를 사용하도록 설정하려면 MSBuild 속성을 True
msbuild 명령줄에서 다음 중 하나를 사용하도록 설정합니다$(AndroidGenerateLayoutBindings)
.
dotnet build -p:AndroidGenerateLayoutBindings=true MyProject.csproj
또는 .csproj 파일에서 다음을 수행합니다.
<PropertyGroup>
<AndroidGenerateLayoutBindings>true</AndroidGenerateLayoutBindings>
</PropertyGroup>
또는 코드 숨김을 전역적으로 사용하지 않도록 설정하고 특정 파일에 대해서만 사용하도록 설정할 수 있습니다. 특정 .axml
파일에 대해 Code-Behind를 사용하도록 설정하려면 빌드 작업을 수행하도록 파일을 변경합니다.@(AndroidBoundLayout)
파일을 편집하고 다음으로 AndroidBoundLayout
바꿔 AndroidResource
서 .csproj
<!-- 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
충돌하는 위젯에 a와 a
View
가 모두 포함된 경우 속성 형식은Fragment/>입니다. global::System.Object
생성된 코드
생성된 코드가 레이아웃을 찾는 방식에 관심이 있는 경우 솔루션 디렉터리의 폴더를 obj\$(Configuration)\generated
살펴보세요.