Skip to content

Commit c75220e

Browse files
jonathanpeppersjonpryor
authored andcommitted
[Mono.Android] add RequireViewById<T>() for better NRT support (#7158)
Context: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/View.java;l=25322 Context: https://developer.android.com/reference/android/view/View#requireViewById(int) In porting Xamarin.Android samples to .NET 6, using latest C# 10 features like `$(Nullable)=enable`, you run into a pattern such as: var foo = FindViewById<TextView>(Resource.Id.foo); ArgumentNullException.ThrowIfNull(foo); foo.Text = "bar"; `var` here is a nullable `TextView?`, so you have to `null`-check. This is pretty verbose, and actually seems to make nullable reference types annoying… API-28 introduced a [`View.requireViewById(int)` method][0]: // Java /* partial */ class View { <T> public final T requireViewById (int id); } This was bound as as a non-generic [`View.RequireViewById(int)`][1] method: // C# partial class View { public Java.Lang.Object RequireViewById (int id); } "Overload" `View.RequireViewById()` with a generic version, similar to the existing [`View.FindViewById<T>(int)` overload][2]: // C# partial class View { public T RequireViewById<T>(int id) where T : View { … } } Introduce an equivalent `Activity.RequireViewById<T>(int)` method: // C# partial class Activity { public T RequireViewById<T>(int id) where T : View { … } } This allows developers to simply: TextView foo = RequireViewById<TextView>(Resource.Id.foo); foo.Text = "bar"; or use `var` if you prefer. If the `id` isn't found, a `Java.Lang.IllegalArgumentException` is thrown with a message containing the `typeof(T)` and hex value of id. Note: these new `RequireViewById<T>()` methods are only available on: * .NET 7 (xamarin-android/main) * .NET 6 + API-33 (`net6.0-android33.0`) * Future API-34 bindings. Meaning that "Classic" Xamarin.Android 13.0 will *not* contain these methods. It's too late to easily change `$(TargetFrameworkVersion)`=v13.0, and if we added it we could get into a state where a future NuGet package used these methods, was built against Xamarin.Android 13.0, and the new methods could not be resolved, resulting in build failures. [0]: https://developer.android.com/reference/android/view/View#requireViewById(int) [1]: https://docs.microsoft.com/en-us/dotnet/api/android.views.view.requireviewbyid?view=xamarin-android-sdk-12 [2]: https://docs.microsoft.com/en-us/dotnet/api/android.views.view.findviewbyid?view=xamarin-android-sdk-12#android-views-view-findviewbyid-1(system-int32)
1 parent 0154c97 commit c75220e

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

src/Mono.Android/Android.App/Activity.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@ partial class Activity {
1212
return this.FindViewById (id)!.JavaCast<T> ();
1313
}
1414

15+
#if NET7_0_OR_GREATER || (NET6_0_OR_GREATER && ANDROID_33) || ANDROID_34
16+
// See: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/Activity.java;l=3430
17+
public T RequireViewById<T> (int id)
18+
where T : Android.Views.View
19+
{
20+
var view = FindViewById<T> (id);
21+
if (view == null) {
22+
throw new Java.Lang.IllegalArgumentException ($"Parameter 'id' of value 0x{id:X} does not reference a View of type '{typeof (T)}' inside this Activity");
23+
}
24+
return view;
25+
}
26+
#endif // NET7_0_OR_GREATER || (NET6_0_OR_GREATER && ANDROID_33) || ANDROID_34
27+
1528
public void StartActivityForResult (Type activityType, int requestCode)
1629
{
1730
var intent = new Android.Content.Intent (this, activityType);

src/Mono.Android/Android.Views/View.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ public T FindViewById<T> (int id)
2929
return this.FindViewById (id).JavaCast<T> ();
3030
}
3131

32+
#if NET7_0_OR_GREATER || (NET6_0_OR_GREATER && ANDROID_33) || ANDROID_34
33+
// See: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/View.java;l=25322
34+
public T RequireViewById<T> (int id)
35+
where T : Android.Views.View
36+
{
37+
var view = FindViewById<T> (id);
38+
if (view == null) {
39+
throw new Java.Lang.IllegalArgumentException ($"Parameter 'id' of value 0x{id:X} does not reference a View of type '{typeof (T)}' inside this View");
40+
}
41+
return view;
42+
}
43+
#endif //NET7_0_OR_GREATER || (NET6_0_OR_GREATER && ANDROID_33) || ANDROID_34
44+
3245
public bool Post (Action action)
3346
{
3447
return Post (new Java.Lang.Thread.RunnableImplementor (action, true));

0 commit comments

Comments
 (0)